aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/vaadin/Application.java1476
-rw-r--r--src/com/vaadin/RootRequiresMoreInformationException.java25
-rw-r--r--src/com/vaadin/annotations/EagerInit.java30
-rw-r--r--src/com/vaadin/annotations/Theme.java24
-rw-r--r--src/com/vaadin/annotations/Widgetset.java25
-rw-r--r--src/com/vaadin/data/Buffered.java147
-rw-r--r--src/com/vaadin/data/Collapsible.java (renamed from src/com/vaadin/ui/treetable/Collapsible.java)7
-rw-r--r--src/com/vaadin/data/Container.java4
-rw-r--r--src/com/vaadin/data/Item.java2
-rw-r--r--src/com/vaadin/data/Property.java148
-rw-r--r--src/com/vaadin/data/Validator.java123
-rw-r--r--src/com/vaadin/data/fieldgroup/BeanFieldGroup.java157
-rw-r--r--src/com/vaadin/data/fieldgroup/Caption.java15
-rw-r--r--src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java156
-rw-r--r--src/com/vaadin/data/fieldgroup/FieldGroup.java978
-rw-r--r--src/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java31
-rw-r--r--src/com/vaadin/data/fieldgroup/PropertyId.java15
-rw-r--r--src/com/vaadin/data/util/AbstractBeanContainer.java53
-rw-r--r--src/com/vaadin/data/util/AbstractProperty.java23
-rw-r--r--src/com/vaadin/data/util/BeanItem.java47
-rw-r--r--src/com/vaadin/data/util/ContainerHierarchicalWrapper.java2
-rw-r--r--src/com/vaadin/data/util/ContainerOrderedWrapper.java2
-rw-r--r--src/com/vaadin/data/util/DefaultItemSorter.java4
-rw-r--r--src/com/vaadin/data/util/FilesystemContainer.java4
-rw-r--r--src/com/vaadin/data/util/HierarchicalContainerOrderedWrapper.java (renamed from src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java)12
-rw-r--r--src/com/vaadin/data/util/IndexedContainer.java54
-rw-r--r--src/com/vaadin/data/util/MethodProperty.java76
-rw-r--r--src/com/vaadin/data/util/MethodPropertyDescriptor.java4
-rw-r--r--src/com/vaadin/data/util/NestedMethodProperty.java34
-rw-r--r--src/com/vaadin/data/util/NestedPropertyDescriptor.java7
-rw-r--r--src/com/vaadin/data/util/ObjectProperty.java58
-rw-r--r--src/com/vaadin/data/util/PropertyFormatter.java54
-rw-r--r--src/com/vaadin/data/util/PropertysetItem.java10
-rw-r--r--src/com/vaadin/data/util/QueryContainer.java7
-rw-r--r--src/com/vaadin/data/util/TextFileProperty.java6
-rw-r--r--src/com/vaadin/data/util/TransactionalPropertyWrapper.java107
-rw-r--r--src/com/vaadin/data/util/VaadinPropertyDescriptor.java4
-rw-r--r--src/com/vaadin/data/util/converter/Converter.java159
-rw-r--r--src/com/vaadin/data/util/converter/ConverterFactory.java23
-rw-r--r--src/com/vaadin/data/util/converter/DateToLongConverter.java68
-rw-r--r--src/com/vaadin/data/util/converter/DefaultConverterFactory.java100
-rw-r--r--src/com/vaadin/data/util/converter/ReverseConverter.java80
-rw-r--r--src/com/vaadin/data/util/converter/StringToBooleanConverter.java104
-rw-r--r--src/com/vaadin/data/util/converter/StringToDateConverter.java108
-rw-r--r--src/com/vaadin/data/util/converter/StringToDoubleConverter.java103
-rw-r--r--src/com/vaadin/data/util/converter/StringToIntegerConverter.java84
-rw-r--r--src/com/vaadin/data/util/converter/StringToNumberConverter.java107
-rw-r--r--src/com/vaadin/data/util/filter/Compare.java2
-rw-r--r--src/com/vaadin/data/util/filter/IsNull.java2
-rw-r--r--src/com/vaadin/data/util/filter/SimpleStringFilter.java12
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java32
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java4
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/RowItem.java5
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/SQLContainer.java6
-rw-r--r--src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java56
-rw-r--r--src/com/vaadin/data/validator/AbstractStringValidator.java42
-rw-r--r--src/com/vaadin/data/validator/AbstractValidator.java71
-rw-r--r--src/com/vaadin/data/validator/BeanValidator.java173
-rw-r--r--src/com/vaadin/data/validator/CompositeValidator.java131
-rw-r--r--src/com/vaadin/data/validator/DateRangeValidator.java51
-rw-r--r--src/com/vaadin/data/validator/DoubleRangeValidator.java37
-rw-r--r--src/com/vaadin/data/validator/DoubleValidator.java20
-rw-r--r--src/com/vaadin/data/validator/IntegerRangeValidator.java37
-rw-r--r--src/com/vaadin/data/validator/IntegerValidator.java19
-rw-r--r--src/com/vaadin/data/validator/NullValidator.java11
-rw-r--r--src/com/vaadin/data/validator/RangeValidator.java186
-rw-r--r--src/com/vaadin/data/validator/RegexpValidator.java9
-rw-r--r--src/com/vaadin/data/validator/StringLengthValidator.java57
-rw-r--r--src/com/vaadin/event/ActionManager.java15
-rw-r--r--src/com/vaadin/event/FieldEvents.java23
-rw-r--r--src/com/vaadin/event/ItemClickEvent.java14
-rw-r--r--src/com/vaadin/event/LayoutEvents.java14
-rw-r--r--src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java5
-rw-r--r--src/com/vaadin/external/json/JSONArray.java963
-rw-r--r--src/com/vaadin/external/json/JSONException.java31
-rw-r--r--src/com/vaadin/external/json/JSONObject.java1693
-rw-r--r--src/com/vaadin/external/json/JSONString.java21
-rw-r--r--src/com/vaadin/external/json/JSONStringer.java83
-rw-r--r--src/com/vaadin/external/json/JSONTokener.java451
-rw-r--r--src/com/vaadin/external/json/JSONWriter.java355
-rw-r--r--src/com/vaadin/external/json/README68
-rw-r--r--src/com/vaadin/terminal/AbstractErrorMessage.java169
-rw-r--r--src/com/vaadin/terminal/CombinedRequest.java167
-rw-r--r--src/com/vaadin/terminal/CompositeErrorMessage.java107
-rw-r--r--src/com/vaadin/terminal/DeploymentConfiguration.java86
-rw-r--r--src/com/vaadin/terminal/DownloadStream.java129
-rw-r--r--src/com/vaadin/terminal/ErrorMessage.java114
-rw-r--r--src/com/vaadin/terminal/KeyMapper.java16
-rw-r--r--src/com/vaadin/terminal/LegacyPaint.java85
-rw-r--r--src/com/vaadin/terminal/PaintTarget.java76
-rw-r--r--src/com/vaadin/terminal/Paintable.java147
-rw-r--r--src/com/vaadin/terminal/ParameterHandler.java59
-rw-r--r--src/com/vaadin/terminal/RequestHandler.java36
-rw-r--r--src/com/vaadin/terminal/Scrollable.java60
-rw-r--r--src/com/vaadin/terminal/Sizeable.java204
-rw-r--r--src/com/vaadin/terminal/SystemError.java108
-rw-r--r--src/com/vaadin/terminal/URIHandler.java48
-rw-r--r--src/com/vaadin/terminal/UserError.java145
-rw-r--r--src/com/vaadin/terminal/Vaadin6Component.java44
-rw-r--r--src/com/vaadin/terminal/VariableOwner.java3
-rw-r--r--src/com/vaadin/terminal/WrappedRequest.java277
-rw-r--r--src/com/vaadin/terminal/WrappedResponse.java147
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml88
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSetBrowserSpecificOverrides.gwt.xml53
-rw-r--r--src/com/vaadin/terminal/gwt/client/AbstractFieldState.java137
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java513
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java1904
-rw-r--r--src/com/vaadin/terminal/gwt/client/BrowserInfo.java125
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentConnector.java130
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java74
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentDetail.java92
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentLocator.java147
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentState.java406
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComputedStyle.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/Connector.java49
-rw-r--r--src/com/vaadin/terminal/gwt/client/ConnectorHierarchyChangeEvent.java97
-rw-r--r--src/com/vaadin/terminal/gwt/client/ConnectorMap.java250
-rw-r--r--src/com/vaadin/terminal/gwt/client/Console.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/Container.java71
-rw-r--r--src/com/vaadin/terminal/gwt/client/DateTimeService.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/DirectionalManagedLayout.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/EventHelper.java70
-rw-r--r--src/com/vaadin/terminal/gwt/client/EventId.java1
-rw-r--r--src/com/vaadin/terminal/gwt/client/FastStringSet.java60
-rw-r--r--src/com/vaadin/terminal/gwt/client/HistoryImplIEVaadin.java192
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManager.java1191
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/MeasuredSize.java228
-rw-r--r--src/com/vaadin/terminal/gwt/client/MouseEventDetails.java82
-rw-r--r--src/com/vaadin/terminal/gwt/client/MouseEventDetailsBuilder.java74
-rw-r--r--src/com/vaadin/terminal/gwt/client/NullConsole.java7
-rw-r--r--src/com/vaadin/terminal/gwt/client/Paintable.java1
-rw-r--r--src/com/vaadin/terminal/gwt/client/ServerConnector.java104
-rw-r--r--src/com/vaadin/terminal/gwt/client/SimpleTree.java5
-rw-r--r--src/com/vaadin/terminal/gwt/client/TooltipInfo.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/UIDL.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/Util.java508
-rw-r--r--src/com/vaadin/terminal/gwt/client/VBrowserDetails.java51
-rw-r--r--src/com/vaadin/terminal/gwt/client/VCaption.java241
-rw-r--r--src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java27
-rw-r--r--src/com/vaadin/terminal/gwt/client/VConsole.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/VDebugConsole.java186
-rw-r--r--src/com/vaadin/terminal/gwt/client/VErrorMessage.java27
-rw-r--r--src/com/vaadin/terminal/gwt/client/VTooltip.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java42
-rw-r--r--src/com/vaadin/terminal/gwt/client/ValueMap.java5
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetMap.java50
-rw-r--r--src/com/vaadin/terminal/gwt/client/WidgetSet.java141
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/AbstractServerConnectorEvent.java33
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/FieldRpc.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/InitializableServerRpc.java26
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java60
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java171
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java207
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java62
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcManager.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcProxy.java37
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java34
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java15
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/SharedState.java41
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java34
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/URLReference.java31
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java161
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java353
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java110
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java186
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractFieldConnector.java54
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutConnector.java13
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutState.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java142
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java18
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/Connect.java (renamed from src/com/vaadin/ui/ClientWidget.java)32
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/FocusElementPanel.java56
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java16
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/Icon.java1
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java42
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java22
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/ManagedLayout.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java130
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/PostLayoutListener.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/ShortcutActionHandler.java29
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/SimpleManagedLayout.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/TabIndexState.java29
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/Table.java15
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/UnknownComponentConnector.java39
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java442
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java196
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java342
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java168
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java453
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VForm.java331
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java533
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java1132
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VLabel.java135
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VLink.java182
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java115
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java238
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java969
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VOverlay.java36
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VPanel.java609
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java446
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java153
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java35
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java85
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VView.java779
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/Vaadin6Connector.java17
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java219
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutState.java29
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/absolutelayout/VAbsoluteLayout.java134
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java83
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VAccordion.java)280
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/audio/AudioConnector.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VAudio.java)33
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/audio/VAudio.java27
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java134
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/ButtonServerRpc.java28
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java65
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/VButton.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VButton.java)131
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxConnector.java148
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxState.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/checkbox/VCheckBox.java61
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/combobox/ComboBoxConnector.java246
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java)554
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java169
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutState.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/csslayout/VCssLayout.java72
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java46
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customcomponent/VCustomComponent.java18
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customfield/CustomFieldConnector.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java118
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutState.java41
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java)303
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/AbstractDateFieldConnector.java109
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/InlineDateFieldConnector.java104
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/PopupDateFieldConnector.java124
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/TextualDateConnector.java56
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/VCalendarPanel.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java)57
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/VDateField.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VDateField.java)97
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java)76
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/VPopupCalendar.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java)143
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/datefield/VTextualDate.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java)146
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VAbstractDropHandler.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java26
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VDropHandler.java6
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VHasDropHandler.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java15
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java6
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java75
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java)145
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java)14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java205
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/embedded/VEmbedded.java239
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java209
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/form/FormState.java29
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/form/VForm.java80
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java105
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java379
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java239
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutState.java37
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java702
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java74
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/label/VLabel.java73
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java397
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java794
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java99
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java9
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java521
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java287
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/link/LinkConnector.java99
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/link/VLink.java117
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/listselect/ListSelectConnector.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/listselect/TooltipListBox.java41
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/listselect/VListSelect.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VListSelect.java)47
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBar.java (renamed from src/com/vaadin/terminal/gwt/client/ui/MenuBar.java)11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBarConnector.java169
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/menubar/MenuItem.java (renamed from src/com/vaadin/terminal/gwt/client/ui/MenuItem.java)2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/menubar/VMenuBar.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java)318
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java125
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java132
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/nativeselect/NativeSelectConnector.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/nativeselect/VNativeSelect.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java)17
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VNotification.java)38
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupBaseConnector.java93
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupConnector.java73
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroup.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java)74
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroupBase.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java)103
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java331
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutState.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/HorizontalLayoutConnector.java24
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VHorizontalLayout.java14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VMeasuringOrderedLayout.java241
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VVerticalLayout.java14
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VerticalLayoutConnector.java24
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java243
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/PanelState.java36
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java189
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/passwordfield/PasswordFieldConnector.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/passwordfield/VPasswordField.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java)3
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/popupview/PopupViewConnector.java121
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/popupview/VPopupView.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VPopupView.java)200
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/progressindicator/ProgressIndicatorConnector.java66
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/progressindicator/VProgressIndicator.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java)49
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/richtextarea/RichTextAreaConnector.java78
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java74
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java423
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java11
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/RootState.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java321
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/slider/SliderConnector.java77
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VSlider.java)123
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java206
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java28
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java88
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/HorizontalSplitPanelConnector.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java)416
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelHorizontal.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelVertical.java12
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/splitpanel/VerticalSplitPanelConnector.java19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java333
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java)682
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetBaseConnector.java99
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java105
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java)408
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetBase.java75
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java)25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/textarea/TextAreaConnector.java42
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/textarea/VTextArea.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTextArea.java)19
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/textfield/TextFieldConnector.java123
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTextField.java)262
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tree/TreeConnector.java260
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/tree/VTree.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTree.java)440
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/treetable/TreeTableConnector.java102
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java)124
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java69
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/twincolselect/VTwinColSelect.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java)125
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/upload/UploadConnector.java67
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java (renamed from src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategy.java)8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java (renamed from src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategyIE.java)8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/upload/VUpload.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VUpload.java)71
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VVideo.java)29
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/video/VideoConnector.java46
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java (renamed from src/com/vaadin/terminal/gwt/client/ui/VWindow.java)696
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java305
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java10
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/window/WindowState.java73
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java1170
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java1106
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java1854
-rw-r--r--src/com/vaadin/terminal/gwt/server/ApplicationPortlet.java250
-rw-r--r--src/com/vaadin/terminal/gwt/server/ApplicationPortlet2.java20
-rw-r--r--src/com/vaadin/terminal/gwt/server/ApplicationResourceHandler.java54
-rw-r--r--src/com/vaadin/terminal/gwt/server/ApplicationRunnerServlet.java218
-rw-r--r--src/com/vaadin/terminal/gwt/server/ApplicationServlet.java23
-rw-r--r--src/com/vaadin/terminal/gwt/server/BootstrapHandler.java602
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientConnector.java38
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java69
-rw-r--r--src/com/vaadin/terminal/gwt/server/CommunicationManager.java365
-rw-r--r--src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java56
-rw-r--r--src/com/vaadin/terminal/gwt/server/Constants.java1
-rw-r--r--src/com/vaadin/terminal/gwt/server/DragAndDropService.java30
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java587
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java302
-rw-r--r--src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java38
-rw-r--r--src/com/vaadin/terminal/gwt/server/PortletApplicationContext.java186
-rw-r--r--src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java52
-rw-r--r--src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java399
-rw-r--r--src/com/vaadin/terminal/gwt/server/RequestTimer.java70
-rw-r--r--src/com/vaadin/terminal/gwt/server/ResourceReference.java51
-rw-r--r--src/com/vaadin/terminal/gwt/server/RpcManager.java17
-rw-r--r--src/com/vaadin/terminal/gwt/server/RpcTarget.java28
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcManager.java138
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java107
-rw-r--r--src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java73
-rw-r--r--src/com/vaadin/terminal/gwt/server/UnsupportedBrowserHandler.java88
-rw-r--r--src/com/vaadin/terminal/gwt/server/WebBrowser.java80
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java109
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedHttpServletResponse.java73
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java189
-rw-r--r--src/com/vaadin/terminal/gwt/server/WrappedPortletResponse.java102
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java55
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java43
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java8
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java7
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java197
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java126
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java142
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java272
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java320
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java221
-rw-r--r--src/com/vaadin/tools/ReflectTools.java86
-rw-r--r--src/com/vaadin/ui/AbsoluteLayout.java278
-rw-r--r--src/com/vaadin/ui/AbstractComponent.java929
-rw-r--r--src/com/vaadin/ui/AbstractComponentContainer.java86
-rw-r--r--src/com/vaadin/ui/AbstractField.java853
-rw-r--r--src/com/vaadin/ui/AbstractLayout.java74
-rw-r--r--src/com/vaadin/ui/AbstractMedia.java58
-rw-r--r--src/com/vaadin/ui/AbstractOrderedLayout.java95
-rw-r--r--src/com/vaadin/ui/AbstractSelect.java174
-rw-r--r--src/com/vaadin/ui/AbstractSplitPanel.java262
-rw-r--r--src/com/vaadin/ui/AbstractTextField.java28
-rw-r--r--src/com/vaadin/ui/Accordion.java4
-rw-r--r--src/com/vaadin/ui/AlignmentUtils.java151
-rw-r--r--src/com/vaadin/ui/Audio.java2
-rw-r--r--src/com/vaadin/ui/BaseFieldFactory.java90
-rw-r--r--src/com/vaadin/ui/Button.java333
-rw-r--r--src/com/vaadin/ui/CheckBox.java167
-rw-r--r--src/com/vaadin/ui/ComboBox.java15
-rw-r--r--src/com/vaadin/ui/Component.java230
-rw-r--r--src/com/vaadin/ui/ComponentContainer.java22
-rw-r--r--src/com/vaadin/ui/CssLayout.java65
-rw-r--r--src/com/vaadin/ui/CustomComponent.java56
-rw-r--r--src/com/vaadin/ui/CustomField.java230
-rw-r--r--src/com/vaadin/ui/CustomLayout.java99
-rw-r--r--src/com/vaadin/ui/DateField.java363
-rw-r--r--src/com/vaadin/ui/DefaultFieldFactory.java17
-rw-r--r--src/com/vaadin/ui/DirtyConnectorTracker.java132
-rw-r--r--src/com/vaadin/ui/DragAndDropWrapper.java12
-rw-r--r--src/com/vaadin/ui/Embedded.java56
-rw-r--r--src/com/vaadin/ui/ExpandLayout.java101
-rw-r--r--src/com/vaadin/ui/Field.java27
-rw-r--r--src/com/vaadin/ui/FieldFactory.java46
-rw-r--r--src/com/vaadin/ui/Form.java325
-rw-r--r--src/com/vaadin/ui/FormFieldFactory.java2
-rw-r--r--src/com/vaadin/ui/FormLayout.java7
-rw-r--r--src/com/vaadin/ui/GridLayout.java165
-rw-r--r--src/com/vaadin/ui/HasComponents.java54
-rw-r--r--src/com/vaadin/ui/HorizontalLayout.java4
-rw-r--r--src/com/vaadin/ui/HorizontalSplitPanel.java4
-rw-r--r--src/com/vaadin/ui/InlineDateField.java2
-rw-r--r--src/com/vaadin/ui/Label.java339
-rw-r--r--src/com/vaadin/ui/Layout.java9
-rw-r--r--src/com/vaadin/ui/Link.java18
-rw-r--r--src/com/vaadin/ui/ListSelect.java2
-rw-r--r--src/com/vaadin/ui/LoginForm.java74
-rw-r--r--src/com/vaadin/ui/MenuBar.java93
-rw-r--r--src/com/vaadin/ui/NativeButton.java36
-rw-r--r--src/com/vaadin/ui/NativeSelect.java2
-rw-r--r--src/com/vaadin/ui/Notification.java321
-rw-r--r--src/com/vaadin/ui/OptionGroup.java5
-rw-r--r--src/com/vaadin/ui/OrderedLayout.java128
-rw-r--r--src/com/vaadin/ui/Panel.java267
-rw-r--r--src/com/vaadin/ui/PasswordField.java2
-rw-r--r--src/com/vaadin/ui/PopupView.java14
-rw-r--r--src/com/vaadin/ui/ProgressIndicator.java35
-rw-r--r--src/com/vaadin/ui/RichTextArea.java22
-rw-r--r--src/com/vaadin/ui/Root.java1590
-rw-r--r--src/com/vaadin/ui/Select.java18
-rw-r--r--src/com/vaadin/ui/Slider.java196
-rw-r--r--src/com/vaadin/ui/SplitPanel.java114
-rw-r--r--src/com/vaadin/ui/TabSheet.java139
-rw-r--r--src/com/vaadin/ui/Table.java549
-rw-r--r--src/com/vaadin/ui/TableFieldFactory.java2
-rw-r--r--src/com/vaadin/ui/TextArea.java2
-rw-r--r--src/com/vaadin/ui/TextField.java203
-rw-r--r--src/com/vaadin/ui/Tree.java29
-rw-r--r--src/com/vaadin/ui/TreeTable.java15
-rw-r--r--src/com/vaadin/ui/TwinColSelect.java3
-rw-r--r--src/com/vaadin/ui/Upload.java9
-rw-r--r--src/com/vaadin/ui/UriFragmentUtility.java153
-rw-r--r--src/com/vaadin/ui/VerticalLayout.java4
-rw-r--r--src/com/vaadin/ui/VerticalSplitPanel.java4
-rw-r--r--src/com/vaadin/ui/Video.java5
-rw-r--r--src/com/vaadin/ui/Window.java1681
-rw-r--r--src/com/vaadin/ui/themes/BaseTheme.java6
484 files changed, 40401 insertions, 28944 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java
index 9fb4cfe7fa..4da1d52c00 100644
--- a/src/com/vaadin/Application.java
+++ b/src/com/vaadin/Application.java
@@ -4,35 +4,60 @@
package com.vaadin;
+import java.io.IOException;
import java.io.Serializable;
+import java.lang.annotation.Annotation;
import java.net.SocketException;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.EventObject;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Properties;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.vaadin.annotations.EagerInit;
+import com.vaadin.annotations.Theme;
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterFactory;
+import com.vaadin.data.util.converter.DefaultConverterFactory;
import com.vaadin.service.ApplicationContext;
+import com.vaadin.terminal.AbstractErrorMessage;
import com.vaadin.terminal.ApplicationResource;
-import com.vaadin.terminal.DownloadStream;
-import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.ParameterHandler;
-import com.vaadin.terminal.SystemError;
+import com.vaadin.terminal.CombinedRequest;
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.RequestHandler;
import com.vaadin.terminal.Terminal;
-import com.vaadin.terminal.URIHandler;
import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedRequest.BrowserDetails;
+import com.vaadin.terminal.WrappedResponse;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
-import com.vaadin.terminal.gwt.server.PortletApplicationContext;
+import com.vaadin.terminal.gwt.server.ClientConnector;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractField;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Root;
+import com.vaadin.ui.Table;
import com.vaadin.ui.Window;
/**
@@ -89,37 +114,317 @@ import com.vaadin.ui.Window;
* @since 3.0
*/
@SuppressWarnings("serial")
-public abstract class Application implements URIHandler,
- Terminal.ErrorListener, Serializable {
-
- private final static Logger logger = Logger.getLogger(Application.class
- .getName());
+public class Application implements Terminal.ErrorListener, Serializable {
/**
- * Id use for the next window that is opened. Access to this must be
- * synchronized.
+ * The name of the parameter that is by default used in e.g. web.xml to
+ * define the name of the default {@link Root} class.
*/
- private int nextWindowId = 1;
+ public static final String ROOT_PARAMETER = "root";
/**
- * Application context the application is running in.
+ * A special application designed to help migrating applications from Vaadin
+ * 6 to Vaadin 7. The legacy application supports setting a main window,
+ * adding additional browser level windows and defining the theme for the
+ * entire application.
+ *
+ * @deprecated This class is only intended to ease migration and should not
+ * be used for new projects.
+ *
+ * @since 7.0
*/
- private ApplicationContext context;
+ @Deprecated
+ public static class LegacyApplication extends Application {
+ /**
+ * Ignore initial / and then get everything up to the next /
+ */
+ private static final Pattern WINDOW_NAME_PATTERN = Pattern
+ .compile("^/?([^/]+).*");
+
+ private Root.LegacyWindow mainWindow;
+ private String theme;
+
+ private Map<String, Root.LegacyWindow> legacyRootNames = new HashMap<String, Root.LegacyWindow>();
+
+ /**
+ * Sets the main window of this application. Setting window as a main
+ * window of this application also adds the window to this application.
+ *
+ * @param mainWindow
+ * the root to set as the default window
+ */
+ public void setMainWindow(Root.LegacyWindow mainWindow) {
+ if (this.mainWindow != null) {
+ throw new IllegalStateException(
+ "mainWindow has already been set");
+ }
+ if (mainWindow.getApplication() == null) {
+ mainWindow.setApplication(this);
+ } else if (mainWindow.getApplication() != this) {
+ throw new IllegalStateException(
+ "mainWindow is attached to another application");
+ }
+ this.mainWindow = mainWindow;
+ }
+
+ /**
+ * Gets the mainWindow of the application.
+ *
+ * <p>
+ * The main window is the window attached to the application URL (
+ * {@link #getURL()}) and thus which is show by default to the user.
+ * </p>
+ * <p>
+ * Note that each application must have at least one main window.
+ * </p>
+ *
+ * @return the root used as the default window
+ */
+ public Root.LegacyWindow getMainWindow() {
+ return mainWindow;
+ }
+
+ /**
+ * This implementation simulates the way of finding a window for a
+ * request by extracting a window name from the requested path and
+ * passes that name to {@link #getWindow(String)}.
+ *
+ * {@inheritDoc}
+ *
+ * @see #getWindow(String)
+ * @see Application#getRoot(WrappedRequest)
+ */
+ @Override
+ public Root.LegacyWindow getRoot(WrappedRequest request) {
+ String pathInfo = request.getRequestPathInfo();
+ String name = null;
+ if (pathInfo != null && pathInfo.length() > 0) {
+ Matcher matcher = WINDOW_NAME_PATTERN.matcher(pathInfo);
+ if (matcher.matches()) {
+ // Skip the initial slash
+ name = matcher.group(1);
+ }
+ }
+ Root.LegacyWindow window = getWindow(name);
+ if (window != null) {
+ return window;
+ }
+ return mainWindow;
+ }
+
+ /**
+ * Sets the application's theme.
+ * <p>
+ * Note that this theme can be overridden for a specific root with
+ * {@link Application#getThemeForRoot(Root)}. Setting theme to be
+ * <code>null</code> selects the default theme. For the available theme
+ * names, see the contents of the VAADIN/themes directory.
+ * </p>
+ *
+ * @param theme
+ * the new theme for this application.
+ */
+ public void setTheme(String theme) {
+ this.theme = theme;
+ }
+
+ /**
+ * Gets the application's theme. The application's theme is the default
+ * theme used by all the roots for which a theme is not explicitly
+ * defined. If the application theme is not explicitly set,
+ * <code>null</code> is returned.
+ *
+ * @return the name of the application's theme.
+ */
+ public String getTheme() {
+ return theme;
+ }
+
+ /**
+ * This implementation returns the theme that has been set using
+ * {@link #setTheme(String)}
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getThemeForRoot(Root root) {
+ return theme;
+ }
+
+ /**
+ * <p>
+ * Gets a root by name. Returns <code>null</code> if the application is
+ * not running or it does not contain a window corresponding to the
+ * name.
+ * </p>
+ *
+ * @param name
+ * the name of the requested window
+ * @return a root corresponding to the name, or <code>null</code> to use
+ * the default window
+ */
+ public Root.LegacyWindow getWindow(String name) {
+ return legacyRootNames.get(name);
+ }
+
+ /**
+ * Counter to get unique names for windows with no explicit name
+ */
+ private int namelessRootIndex = 0;
+
+ /**
+ * Adds a new browser level window to this application. Please note that
+ * Root doesn't have a name that is used in the URL - to add a named
+ * window you should instead use {@link #addWindow(Root, String)}
+ *
+ * @param root
+ * the root window to add to the application
+ * @return returns the name that has been assigned to the window
+ *
+ * @see #addWindow(Root, String)
+ */
+ public void addWindow(Root.LegacyWindow root) {
+ if (root.getName() == null) {
+ String name = Integer.toString(namelessRootIndex++);
+ root.setName(name);
+ }
+
+ legacyRootNames.put(root.getName(), root);
+ root.setApplication(this);
+ }
+
+ /**
+ * Removes the specified window from the application. This also removes
+ * all name mappings for the window (see
+ * {@link #addWindow(Root, String) and #getWindowName(Root)}.
+ *
+ * <p>
+ * Note that removing window from the application does not close the
+ * browser window - the window is only removed from the server-side.
+ * </p>
+ *
+ * @param root
+ * the root to remove
+ */
+ public void removeWindow(Root.LegacyWindow root) {
+ for (Entry<String, Root.LegacyWindow> entry : legacyRootNames
+ .entrySet()) {
+ if (entry.getValue() == root) {
+ legacyRootNames.remove(entry.getKey());
+ }
+ }
+ }
+
+ /**
+ * Gets the set of windows contained by the application.
+ *
+ * <p>
+ * Note that the returned set of windows can not be modified.
+ * </p>
+ *
+ * @return the unmodifiable collection of windows.
+ */
+ public Collection<Root.LegacyWindow> getWindows() {
+ return Collections.unmodifiableCollection(legacyRootNames.values());
+ }
+ }
/**
- * The current user or <code>null</code> if no user has logged in.
+ * An event sent to {@link #start(ApplicationStartEvent)} when a new
+ * Application is being started.
+ *
+ * @since 7.0
*/
- private Object user;
+ public static class ApplicationStartEvent implements Serializable {
+ private final URL applicationUrl;
+
+ private final Properties applicationProperties;
+
+ private final ApplicationContext context;
+
+ private final boolean productionMode;
+
+ /**
+ * @param applicationUrl
+ * the URL the application should respond to.
+ * @param applicationProperties
+ * the Application properties as specified by the deployment
+ * configuration.
+ * @param context
+ * the context application will be running in.
+ * @param productionMode
+ * flag indicating whether the application is running in
+ * production mode.
+ */
+ public ApplicationStartEvent(URL applicationUrl,
+ Properties applicationProperties, ApplicationContext context,
+ boolean productionMode) {
+ this.applicationUrl = applicationUrl;
+ this.applicationProperties = applicationProperties;
+ this.context = context;
+ this.productionMode = productionMode;
+ }
+
+ /**
+ * Gets the URL the application should respond to.
+ *
+ * @return the URL the application should respond to or
+ * <code>null</code> if the URL is not defined.
+ *
+ * @see Application#getURL()
+ */
+ public URL getApplicationUrl() {
+ return applicationUrl;
+ }
+
+ /**
+ * Gets the Application properties as specified by the deployment
+ * configuration.
+ *
+ * @return the properties configured for the applciation.
+ *
+ * @see Application#getProperty(String)
+ */
+ public Properties getApplicationProperties() {
+ return applicationProperties;
+ }
+
+ /**
+ * Gets the context application will be running in.
+ *
+ * @return the context application will be running in.
+ *
+ * @see Application#getContext()
+ */
+ public ApplicationContext getContext() {
+ return context;
+ }
+
+ /**
+ * Checks whether the application is running in production mode.
+ *
+ * @return <code>true</code> if in production mode, else
+ * <code>false</code>
+ *
+ * @see Application#isProductionMode()
+ */
+ public boolean isProductionMode() {
+ return productionMode;
+ }
+ }
+
+ private final static Logger logger = Logger.getLogger(Application.class
+ .getName());
/**
- * Mapping from window name to window instance.
+ * Application context the application is running in.
*/
- private final Hashtable<String, Window> windows = new Hashtable<String, Window>();
+ private ApplicationContext context;
/**
- * Main window of the application.
+ * The current user or <code>null</code> if no user has logged in.
*/
- private Window mainWindow = null;
+ private Object user;
/**
* The application's URL.
@@ -127,11 +432,6 @@ public abstract class Application implements URIHandler,
private URL applicationUrl;
/**
- * Name of the theme currently used by the application.
- */
- private String theme = null;
-
- /**
* Application status.
*/
private volatile boolean applicationIsRunning = false;
@@ -152,16 +452,6 @@ public abstract class Application implements URIHandler,
private LinkedList<UserChangeListener> userChangeListeners = null;
/**
- * Window attach listeners.
- */
- private LinkedList<WindowAttachListener> windowAttachListeners = null;
-
- /**
- * Window detach listeners.
- */
- private LinkedList<WindowDetachListener> windowDetachListeners = null;
-
- /**
* Application resource mapping: key <-> resource.
*/
private final Hashtable<ApplicationResource, String> resourceKeyMap = new Hashtable<ApplicationResource, String>();
@@ -189,237 +479,28 @@ public abstract class Application implements URIHandler,
private Terminal.ErrorListener errorHandler = this;
/**
- * <p>
- * Gets a window by name. Returns <code>null</code> if the application is
- * not running or it does not contain a window corresponding to the name.
- * </p>
- *
- * <p>
- * All windows can be referenced by their names in url
- * <code>http://host:port/foo/bar/</code> where
- * <code>http://host:port/foo/</code> is the application url as returned by
- * getURL() and <code>bar</code> is the name of the window.
- * </p>
- *
- * <p>
- * One should note that this method can, as a side effect create new windows
- * if needed by the application. This can be achieved by overriding the
- * default implementation.
- * </p>
- *
- * <p>
- * If for some reason user opens another window with same url that is
- * already open, the name is modified by adding a "_N" postfix to the name,
- * where N is a running number starting from 1. One can decide to create
- * another window-object for those windows (recommended) or to discard the
- * postfix. If the user has two browser windows pointing to the same
- * window-object on server, synchronization errors are likely to occur.
- * </p>
- *
- * <p>
- * If no browser-level windowing is used, all defaults are fine and this
- * method can be left as is. In case browser-level windows are needed, it is
- * recommended to create new window-objects on this method from their names
- * if the super.getWindow() does not find existing windows. See below for
- * implementation example: <code><pre>
- // If we already have the requested window, use it
- Window w = super.getWindow(name);
- if (w == null) {
- // If no window found, create it
- w = new Window(name);
- // set windows name to the one requested
- w.setName(name);
- // add it to this application
- addWindow(w);
- // ensure use of window specific url
- w.open(new ExternalResource(w.getURL().toString()));
- // add some content
- w.addComponent(new Label("Test window"));
- }
- return w;</pre></code>
- * </p>
- *
- * <p>
- * <strong>Note</strong> that all returned Window objects must be added to
- * this application instance.
- *
- * <p>
- * The method should return null if the window does not exists (and is not
- * created as a side-effect) or if the application is not running anymore.
- * </p>
- *
- * @param name
- * the name of the window.
- * @return the window associated with the given URI or <code>null</code>
+ * The converter factory that is used to provide default converters for the
+ * application.
*/
- public Window getWindow(String name) {
-
- // For closed app, do not give any windows
- if (!isRunning()) {
- return null;
- }
-
- // Gets the window by name
- final Window window = windows.get(name);
-
- return window;
- }
-
- /**
- * Adds a new window to the application.
- *
- * <p>
- * This implicitly invokes the
- * {@link com.vaadin.ui.Window#setApplication(Application)} method.
- * </p>
- *
- * <p>
- * Note that all application-level windows can be accessed by their names in
- * url <code>http://host:port/foo/bar/</code> where
- * <code>http://host:port/foo/</code> is the application url as returned by
- * getURL() and <code>bar</code> is the name of the window. Also note that
- * not all windows should be added to application - one can also add windows
- * inside other windows - these windows show as smaller windows inside those
- * windows.
- * </p>
- *
- * @param window
- * the new <code>Window</code> to add. If the name of the window
- * is <code>null</code>, an unique name is automatically given
- * for the window.
- * @throws IllegalArgumentException
- * if a window with the same name as the new window already
- * exists in the application.
- * @throws NullPointerException
- * if the given <code>Window</code> is <code>null</code>.
- */
- public void addWindow(Window window) throws IllegalArgumentException,
- NullPointerException {
-
- // Nulls can not be added to application
- if (window == null) {
- return;
- }
-
- // Check that one is not adding a sub-window to application
- if (window.getParent() != null) {
- throw new IllegalArgumentException(
- "Window was already added inside another window"
- + " - it can not be added to application also.");
- }
-
- // Gets the naming proposal from window
- String name = window.getName();
-
- // Checks that the application does not already contain
- // window having the same name
- if (name != null && windows.containsKey(name)) {
-
- // If the window is already added
- if (window == windows.get(name)) {
- return;
- }
-
- // Otherwise complain
- throw new IllegalArgumentException("Window with name '"
- + window.getName()
- + "' is already present in the application");
- }
+ private ConverterFactory converterFactory = new DefaultConverterFactory();
- // If the name of the window is null, the window is automatically named
- if (name == null) {
- boolean accepted = false;
- while (!accepted) {
+ private LinkedList<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
- // Try another name
- synchronized (this) {
- name = String.valueOf(nextWindowId);
- nextWindowId++;
- }
-
- if (!windows.containsKey(name)) {
- accepted = true;
- }
- }
- window.setName(name);
- }
-
- // Adds the window to application
- windows.put(name, window);
- window.setApplication(this);
+ private int nextRootId = 0;
+ private Map<Integer, Root> roots = new HashMap<Integer, Root>();
- fireWindowAttachEvent(window);
-
- // If no main window is set, declare the window to be main window
- if (getMainWindow() == null) {
- mainWindow = window;
- }
- }
+ private boolean productionMode = true;
- /**
- * Send information to all listeners about new Windows associated with this
- * application.
- *
- * @param window
- */
- private void fireWindowAttachEvent(Window window) {
- // Fires the window attach event
- if (windowAttachListeners != null) {
- final Object[] listeners = windowAttachListeners.toArray();
- final WindowAttachEvent event = new WindowAttachEvent(window);
- for (int i = 0; i < listeners.length; i++) {
- ((WindowAttachListener) listeners[i]).windowAttached(event);
- }
- }
- }
+ private final Map<String, Integer> retainOnRefreshRoots = new HashMap<String, Integer>();
/**
- * Removes the specified window from the application.
- *
- * <p>
- * Removing the main window of the Application also sets the main window to
- * null. One must another window to be the main window after this with
- * {@link #setMainWindow(Window)}.
- * </p>
- *
+ * Keeps track of which roots have been inited.
* <p>
- * Note that removing window from the application does not close the browser
- * window - the window is only removed from the server-side.
+ * TODO Investigate whether this might be derived from the different states
+ * in getRootForRrequest.
* </p>
- *
- * @param window
- * the window to be removed.
*/
- public void removeWindow(Window window) {
- if (window != null && windows.contains(window)) {
-
- // Removes the window from application
- windows.remove(window.getName());
-
- // If the window was main window, clear it
- if (getMainWindow() == window) {
- setMainWindow(null);
- }
-
- // Removes the application from window
- if (window.getApplication() == this) {
- window.setApplication(null);
- }
-
- fireWindowDetachEvent(window);
- }
- }
-
- private void fireWindowDetachEvent(Window window) {
- // Fires the window detach event
- if (windowDetachListeners != null) {
- final Object[] listeners = windowDetachListeners.toArray();
- final WindowDetachEvent event = new WindowDetachEvent(window);
- for (int i = 0; i < listeners.length; i++) {
- ((WindowDetachListener) listeners[i]).windowDetached(event);
- }
- }
- }
+ private Set<Integer> initedRoots = new HashSet<Integer>();
/**
* Gets the user of the application.
@@ -525,20 +606,16 @@ public abstract class Application implements URIHandler,
* {@link javax.servlet.ServletContext}.
* </p>
*
- * @param applicationUrl
- * the URL the application should respond to.
- * @param applicationProperties
- * the Application properties as specified by the servlet
- * configuration.
- * @param context
- * the context application will be running in.
+ * @param event
+ * the application start event containing details required for
+ * starting the application.
*
*/
- public void start(URL applicationUrl, Properties applicationProperties,
- ApplicationContext context) {
- this.applicationUrl = applicationUrl;
- properties = applicationProperties;
- this.context = context;
+ public void start(ApplicationStartEvent event) {
+ applicationUrl = event.getApplicationUrl();
+ productionMode = event.isProductionMode();
+ properties = event.getApplicationProperties();
+ context = event.getContext();
init();
applicationIsRunning = true;
}
@@ -560,106 +637,14 @@ public abstract class Application implements URIHandler,
}
/**
- * Gets the set of windows contained by the application.
- *
- * <p>
- * Note that the returned set of windows can not be modified.
- * </p>
- *
- * @return the Unmodifiable collection of windows.
- */
- public Collection<Window> getWindows() {
- return Collections.unmodifiableCollection(windows.values());
- }
-
- /**
* <p>
* Main initializer of the application. The <code>init</code> method is
* called by the framework when the application is started, and it should
- * perform whatever initialization operations the application needs, such as
- * creating windows and adding components to them.
- * </p>
- */
- public abstract void init();
-
- /**
- * Gets the application's theme. The application's theme is the default
- * theme used by all the windows in it that do not explicitly specify a
- * theme. If the application theme is not explicitly set, the
- * <code>null</code> is returned.
- *
- * @return the name of the application's theme.
- */
- public String getTheme() {
- return theme;
- }
-
- /**
- * Sets the application's theme.
- * <p>
- * Note that this theme can be overridden in the the application level
- * windows with {@link com.vaadin.ui.Window#setTheme(String)}. Setting theme
- * to be <code>null</code> selects the default theme. For the available
- * theme names, see the contents of the VAADIN/themes directory.
- * </p>
- *
- * @param theme
- * the new theme for this application.
- */
- public void setTheme(String theme) {
- // Collect list of windows not having the current or future theme
- final LinkedList<Window> toBeUpdated = new LinkedList<Window>();
- final String oldAppTheme = getTheme();
- for (final Iterator<Window> i = getWindows().iterator(); i.hasNext();) {
- final Window w = i.next();
- final String windowTheme = w.getTheme();
- if ((windowTheme == null)
- || (!windowTheme.equals(theme) && windowTheme
- .equals(oldAppTheme))) {
- toBeUpdated.add(w);
- }
- }
-
- // Updates the theme
- this.theme = theme;
-
- // Ask windows to update themselves
- for (final Iterator<Window> i = toBeUpdated.iterator(); i.hasNext();) {
- i.next().requestRepaint();
- }
- }
-
- /**
- * Gets the mainWindow of the application.
- *
- * <p>
- * The main window is the window attached to the application URL (
- * {@link #getURL()}) and thus which is show by default to the user.
- * </p>
- * <p>
- * Note that each application must have at least one main window.
- * </p>
- *
- * @return the main window.
- */
- public Window getMainWindow() {
- return mainWindow;
- }
-
- /**
- * <p>
- * Sets the mainWindow. If the main window is not explicitly set, the main
- * window defaults to first created window. Setting window as a main window
- * of this application also adds the window to this application.
+ * perform whatever initialization operations the application needs.
* </p>
- *
- * @param mainWindow
- * the mainWindow to set.
*/
- public void setMainWindow(Window mainWindow) {
-
- addWindow(mainWindow);
- this.mainWindow = mainWindow;
+ public void init() {
+ // Default implementation does nothing
}
/**
@@ -759,50 +744,6 @@ public abstract class Application implements URIHandler,
}
/**
- * Application URI handling hub.
- *
- * <p>
- * This method gets called by terminal. It has lots of duties like to pass
- * uri handler to proper uri handlers registered to windows etc.
- * </p>
- *
- * <p>
- * In most situations developers should NOT OVERRIDE this method. Instead
- * developers should implement and register uri handlers to windows.
- * </p>
- *
- * @deprecated this method is called be the terminal implementation only and
- * might be removed or moved in the future. Instead of
- * overriding this method, add your {@link URIHandler} to a top
- * level {@link Window} (eg.
- * getMainWindow().addUriHanler(handler) instead.
- */
- @Deprecated
- public DownloadStream handleURI(URL context, String relativeUri) {
-
- if (this.context.isApplicationResourceURL(context, relativeUri)) {
-
- // Handles the resource request
- final String key = this.context.getURLKey(context, relativeUri);
- final ApplicationResource resource = keyResourceMap.get(key);
- if (resource != null) {
- DownloadStream stream = resource.getStream();
- if (stream != null) {
- stream.setCacheTime(resource.getCacheTime());
- return stream;
- } else {
- return null;
- }
- } else {
- // Resource requests override uri handling
- return null;
- }
- } else {
- return null;
- }
- }
-
- /**
* Gets the default locale for this application.
*
* By default this is the preferred locale of the user using the
@@ -1061,68 +1002,6 @@ public abstract class Application implements URIHandler,
}
/**
- * Adds the window attach listener.
- *
- * Use this to get notifications each time a window is attached to the
- * application with {@link #addWindow(Window)}.
- *
- * @param listener
- * the window attach listener to add.
- */
- public void addListener(WindowAttachListener listener) {
- if (windowAttachListeners == null) {
- windowAttachListeners = new LinkedList<WindowAttachListener>();
- }
- windowAttachListeners.add(listener);
- }
-
- /**
- * Adds the window detach listener.
- *
- * Use this to get notifications each time a window is remove from the
- * application with {@link #removeWindow(Window)}.
- *
- * @param listener
- * the window detach listener to add.
- */
- public void addListener(WindowDetachListener listener) {
- if (windowDetachListeners == null) {
- windowDetachListeners = new LinkedList<WindowDetachListener>();
- }
- windowDetachListeners.add(listener);
- }
-
- /**
- * Removes the window attach listener.
- *
- * @param listener
- * the window attach listener to remove.
- */
- public void removeListener(WindowAttachListener listener) {
- if (windowAttachListeners != null) {
- windowAttachListeners.remove(listener);
- if (windowAttachListeners.isEmpty()) {
- windowAttachListeners = null;
- }
- }
- }
-
- /**
- * Removes the window detach listener.
- *
- * @param listener
- * the window detach listener to remove.
- */
- public void removeListener(WindowDetachListener listener) {
- if (windowDetachListeners != null) {
- windowDetachListeners.remove(listener);
- if (windowDetachListeners.isEmpty()) {
- windowDetachListeners = null;
- }
- }
- }
-
- /**
* Returns the URL user is redirected to on application close. If the URL is
* <code>null</code>, the application is closed normally as defined by the
* application running environment.
@@ -1200,22 +1079,14 @@ public abstract class Application implements URIHandler,
Object owner = null;
if (event instanceof VariableOwner.ErrorEvent) {
owner = ((VariableOwner.ErrorEvent) event).getVariableOwner();
- } else if (event instanceof URIHandler.ErrorEvent) {
- owner = ((URIHandler.ErrorEvent) event).getURIHandler();
- } else if (event instanceof ParameterHandler.ErrorEvent) {
- owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler();
} else if (event instanceof ChangeVariablesErrorEvent) {
owner = ((ChangeVariablesErrorEvent) event).getComponent();
}
// Shows the error in AbstractComponent
if (owner instanceof AbstractComponent) {
- if (t instanceof ErrorMessage) {
- ((AbstractComponent) owner).setComponentError((ErrorMessage) t);
- } else {
- ((AbstractComponent) owner)
- .setComponentError(new SystemError(t));
- }
+ ((AbstractComponent) owner).setComponentError(AbstractErrorMessage
+ .getErrorMessageForException(t));
}
// also print the error on console
@@ -1280,6 +1151,43 @@ public abstract class Application implements URIHandler,
}
/**
+ * Gets the {@link ConverterFactory} used to locate a suitable
+ * {@link Converter} for fields in the application.
+ *
+ * See {@link #setConverterFactory(ConverterFactory)} for more details
+ *
+ * @return The converter factory used in the application
+ */
+ public ConverterFactory getConverterFactory() {
+ return converterFactory;
+ }
+
+ /**
+ * Sets the {@link ConverterFactory} used to locate a suitable
+ * {@link Converter} for fields in the application.
+ * <p>
+ * The {@link ConverterFactory} is used to find a suitable converter when
+ * binding data to a UI component and the data type does not match the UI
+ * component type, e.g. binding a Double to a TextField (which is based on a
+ * String).
+ * </p>
+ * <p>
+ * The {@link Converter} for an individual field can be overridden using
+ * {@link AbstractField#setConverter(Converter)} and for individual property
+ * ids in a {@link Table} using
+ * {@link Table#setConverter(Object, Converter)}.
+ * </p>
+ * <p>
+ * The converter factory must never be set to null.
+ *
+ * @param converterFactory
+ * The converter factory used in the application
+ */
+ public void setConverterFactory(ConverterFactory converterFactory) {
+ this.converterFactory = converterFactory;
+ }
+
+ /**
* Contains the system messages used to notify the user about various
* critical situations that can occur.
* <p>
@@ -1906,4 +1814,606 @@ public abstract class Application implements URIHandler,
}
}
-} \ No newline at end of file
+
+ /**
+ * Gets a root for a request for which no root is already known. This method
+ * is called when the framework processes a request that does not originate
+ * from an existing root instance. This typically happens when a host page
+ * is requested.
+ *
+ * <p>
+ * Subclasses of Application may override this method to provide custom
+ * logic for choosing how to create a suitable root or for picking an
+ * already created root. If an existing root is picked, care should be taken
+ * to avoid keeping the same root open in multiple browser windows, as that
+ * will cause the states to go out of sync.
+ * </p>
+ *
+ * <p>
+ * If {@link BrowserDetails} are required to create a Root, the
+ * implementation can throw a {@link RootRequiresMoreInformationException}
+ * exception. In this case, the framework will instruct the browser to send
+ * the additional details, whereupon this method is invoked again with the
+ * browser details present in the wrapped request. Throwing the exception if
+ * the browser details are already available is not supported.
+ * </p>
+ *
+ * <p>
+ * The default implementation in {@link Application} creates a new instance
+ * of the Root class returned by {@link #getRootClassName(WrappedRequest)},
+ * which in turn uses the {@value #ROOT_PARAMETER} parameter from web.xml.
+ * If {@link DeploymentConfiguration#getClassLoader()} for the request
+ * returns a {@link ClassLoader}, it is used for loading the Root class.
+ * Otherwise the {@link ClassLoader} used to load this class is used.
+ * </p>
+ *
+ * @param request
+ * the wrapped request for which a root is needed
+ * @return a root instance to use for the request
+ * @throws RootRequiresMoreInformationException
+ * may be thrown by an implementation to indicate that
+ * {@link BrowserDetails} are required to create a root
+ *
+ * @see #getRootClassName(WrappedRequest)
+ * @see Root
+ * @see RootRequiresMoreInformationException
+ * @see WrappedRequest#getBrowserDetails()
+ *
+ * @since 7.0
+ */
+ protected Root getRoot(WrappedRequest request)
+ throws RootRequiresMoreInformationException {
+ String rootClassName = getRootClassName(request);
+ try {
+ ClassLoader classLoader = request.getDeploymentConfiguration()
+ .getClassLoader();
+ if (classLoader == null) {
+ classLoader = getClass().getClassLoader();
+ }
+ Class<? extends Root> rootClass = Class.forName(rootClassName,
+ true, classLoader).asSubclass(Root.class);
+ try {
+ Root root = rootClass.newInstance();
+ return root;
+ } catch (Exception e) {
+ throw new RuntimeException("Could not instantiate root class "
+ + rootClassName, e);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Could not load root class "
+ + rootClassName, e);
+ }
+ }
+
+ /**
+ * Provides the name of the <code>Root</code> class that should be used for
+ * a request. The class must have an accessible no-args constructor.
+ * <p>
+ * The default implementation uses the {@value #ROOT_PARAMETER} parameter
+ * from web.xml.
+ * </p>
+ * <p>
+ * This method is mainly used by the default implementation of
+ * {@link #getRoot(WrappedRequest)}. If you override that method with your
+ * own functionality, the results of this method might not be used.
+ * </p>
+ *
+ * @param request
+ * the request for which a new root is required
+ * @return the name of the root class to use
+ *
+ * @since 7.0
+ */
+ protected String getRootClassName(WrappedRequest request) {
+ Object rootClassNameObj = properties.get(ROOT_PARAMETER);
+ if (rootClassNameObj instanceof String) {
+ return (String) rootClassNameObj;
+ } else {
+ throw new RuntimeException("No " + ROOT_PARAMETER
+ + " defined in web.xml");
+ }
+ }
+
+ /**
+ * Finds the theme to use for a specific root. If no specific theme is
+ * required, <code>null</code> is returned.
+ *
+ * TODO Tell what the default implementation does once it does something.
+ *
+ * @param root
+ * the root to get a theme for
+ * @return the name of the theme, or <code>null</code> if the default theme
+ * should be used
+ *
+ * @since 7.0
+ */
+ public String getThemeForRoot(Root root) {
+ Theme rootTheme = getAnnotationFor(root.getClass(), Theme.class);
+ if (rootTheme != null) {
+ return rootTheme.value();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Finds the widgetset to use for a specific root. If no specific widgetset
+ * is required, <code>null</code> is returned.
+ *
+ * TODO Tell what the default implementation does once it does something.
+ *
+ * @param root
+ * the root to get a widgetset for
+ * @return the name of the widgetset, or <code>null</code> if the default
+ * widgetset should be used
+ *
+ * @since 7.0
+ */
+ public String getWidgetsetForRoot(Root root) {
+ Widgetset rootWidgetset = getAnnotationFor(root.getClass(),
+ Widgetset.class);
+ if (rootWidgetset != null) {
+ return rootWidgetset.value();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Helper to get an annotation for a class. If the annotation is not present
+ * on the target class, it's superclasses and implemented interfaces are
+ * also searched for the annotation.
+ *
+ * @param type
+ * the target class from which the annotation should be found
+ * @param annotationType
+ * the annotation type to look for
+ * @return an annotation of the given type, or <code>null</code> if the
+ * annotation is not present on the class
+ */
+ private static <T extends Annotation> T getAnnotationFor(Class<?> type,
+ Class<T> annotationType) {
+ // Find from the class hierarchy
+ Class<?> currentType = type;
+ while (currentType != Object.class) {
+ T annotation = currentType.getAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ } else {
+ currentType = currentType.getSuperclass();
+ }
+ }
+
+ // Find from an implemented interface
+ for (Class<?> iface : type.getInterfaces()) {
+ T annotation = iface.getAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Handles a request by passing it to each registered {@link RequestHandler}
+ * in turn until one produces a response. This method is used for requests
+ * that have not been handled by any specific functionality in the terminal
+ * implementation (e.g. {@link AbstractApplicationServlet}).
+ * <p>
+ * The request handlers are invoked in the revere order in which they were
+ * added to the application until a response has been produced. This means
+ * that the most recently added handler is used first and the first request
+ * handler that was added to the application is invoked towards the end
+ * unless any previous handler has already produced a response.
+ * </p>
+ *
+ * @param request
+ * the wrapped request to get information from
+ * @param response
+ * the response to which data can be written
+ * @return returns <code>true</code> if a {@link RequestHandler} has
+ * produced a response and <code>false</code> if no response has
+ * been written.
+ * @throws IOException
+ *
+ * @see #addRequestHandler(RequestHandler)
+ * @see RequestHandler
+ *
+ * @since 7.0
+ */
+ public boolean handleRequest(WrappedRequest request,
+ WrappedResponse response) throws IOException {
+ // Use a copy to avoid ConcurrentModificationException
+ for (RequestHandler handler : new ArrayList<RequestHandler>(
+ requestHandlers)) {
+ if (handler.handleRequest(this, request, response)) {
+ return true;
+ }
+ }
+ // If not handled
+ return false;
+ }
+
+ /**
+ * Adds a request handler to this application. Request handlers can be added
+ * to provide responses to requests that are not handled by the default
+ * functionality of the framework.
+ * <p>
+ * Handlers are called in reverse order of addition, so the most recently
+ * added handler will be called first.
+ * </p>
+ *
+ * @param handler
+ * the request handler to add
+ *
+ * @see #handleRequest(WrappedRequest, WrappedResponse)
+ * @see #removeRequestHandler(RequestHandler)
+ *
+ * @since 7.0
+ */
+ public void addRequestHandler(RequestHandler handler) {
+ requestHandlers.addFirst(handler);
+ }
+
+ /**
+ * Removes a request handler from the application.
+ *
+ * @param handler
+ * the request handler to remove
+ *
+ * @since 7.0
+ */
+ public void removeRequestHandler(RequestHandler handler) {
+ requestHandlers.remove(handler);
+ }
+
+ /**
+ * Gets the request handlers that are registered to the application. The
+ * iteration order of the returned collection is the same as the order in
+ * which the request handlers will be invoked when a request is handled.
+ *
+ * @return a collection of request handlers, with the iteration order
+ * according to the order they would be invoked
+ *
+ * @see #handleRequest(WrappedRequest, WrappedResponse)
+ * @see #addRequestHandler(RequestHandler)
+ * @see #removeRequestHandler(RequestHandler)
+ *
+ * @since 7.0
+ */
+ public Collection<RequestHandler> getRequestHandlers() {
+ return Collections.unmodifiableCollection(requestHandlers);
+ }
+
+ /**
+ * Find an application resource with a given key.
+ *
+ * @param key
+ * The key of the resource
+ * @return The application resource corresponding to the provided key, or
+ * <code>null</code> if no resource is registered for the key
+ *
+ * @since 7.0
+ */
+ public ApplicationResource getResource(String key) {
+ return keyResourceMap.get(key);
+ }
+
+ /**
+ * Thread local for keeping track of currently used application instance
+ *
+ * @since 7.0
+ */
+ private static final ThreadLocal<Application> currentApplication = new ThreadLocal<Application>();
+
+ private boolean rootPreserved = false;
+
+ /**
+ * Gets the currently used application. The current application is
+ * automatically defined when processing requests to the server. In other
+ * cases, (e.g. from background threads), the current application is not
+ * automatically defined.
+ *
+ * @return the current application instance if available, otherwise
+ * <code>null</code>
+ *
+ * @see #setCurrentApplication(Application)
+ *
+ * @since 7.0
+ */
+ public static Application getCurrentApplication() {
+ return currentApplication.get();
+ }
+
+ /**
+ * Sets the thread local for the current application. This method is used by
+ * the framework to set the current application whenever a new request is
+ * processed and it is cleared when the request has been processed.
+ * <p>
+ * The application developer can also use this method to define the current
+ * application outside the normal request handling, e.g. when initiating
+ * custom background threads.
+ * </p>
+ *
+ * @param application
+ *
+ * @see #getCurrentApplication()
+ * @see ThreadLocal
+ *
+ * @since 7.0
+ */
+ public static void setCurrentApplication(Application application) {
+ currentApplication.set(application);
+ }
+
+ /**
+ * Check whether this application is in production mode. If an application
+ * is in production mode, certain debugging facilities are not available.
+ *
+ * @return the status of the production mode flag
+ *
+ * @since 7.0
+ */
+ public boolean isProductionMode() {
+ return productionMode;
+ }
+
+ /**
+ * Finds the {@link Root} to which a particular request belongs. If the
+ * request originates from an existing Root, that root is returned. In other
+ * cases, the method attempts to create and initialize a new root and might
+ * throw a {@link RootRequiresMoreInformationException} if all required
+ * information is not available.
+ * <p>
+ * Please note that this method can also return a newly created
+ * <code>Root</code> which has not yet been initialized. You can use
+ * {@link #isRootInitPending(int)} with the root's id (
+ * {@link Root#getRootId()} to check whether the initialization is still
+ * pending.
+ * </p>
+ *
+ * @param request
+ * the request for which a root is desired
+ * @return a root belonging to the request
+ * @throws RootRequiresMoreInformationException
+ * if no existing root could be found and creating a new root
+ * requires additional information from the browser
+ *
+ * @see #getRoot(WrappedRequest)
+ * @see RootRequiresMoreInformationException
+ *
+ * @since 7.0
+ */
+ public Root getRootForRequest(WrappedRequest request)
+ throws RootRequiresMoreInformationException {
+ Root root = Root.getCurrentRoot();
+ if (root != null) {
+ return root;
+ }
+ Integer rootId = getRootId(request);
+
+ synchronized (this) {
+ BrowserDetails browserDetails = request.getBrowserDetails();
+ boolean hasBrowserDetails = browserDetails != null
+ && browserDetails.getUriFragment() != null;
+
+ root = roots.get(rootId);
+
+ if (root == null && isRootPreserved()) {
+ // Check for a known root
+ if (!retainOnRefreshRoots.isEmpty()) {
+
+ Integer retainedRootId;
+ if (!hasBrowserDetails) {
+ throw new RootRequiresMoreInformationException();
+ } else {
+ String windowName = browserDetails.getWindowName();
+ retainedRootId = retainOnRefreshRoots.get(windowName);
+ }
+
+ if (retainedRootId != null) {
+ rootId = retainedRootId;
+ root = roots.get(rootId);
+ }
+ }
+ }
+
+ if (root == null) {
+ // Throws exception if root can not yet be created
+ root = getRoot(request);
+
+ // Initialize some fields for a newly created root
+ if (root.getApplication() == null) {
+ root.setApplication(this);
+ }
+ if (root.getRootId() < 0) {
+
+ if (rootId == null) {
+ // Get the next id if none defined
+ rootId = Integer.valueOf(nextRootId++);
+ }
+ root.setRootId(rootId.intValue());
+ roots.put(rootId, root);
+ }
+ }
+
+ // Set thread local here so it is available in init
+ Root.setCurrentRoot(root);
+
+ if (!initedRoots.contains(rootId)) {
+ boolean initRequiresBrowserDetails = isRootPreserved()
+ || !root.getClass()
+ .isAnnotationPresent(EagerInit.class);
+ if (!initRequiresBrowserDetails || hasBrowserDetails) {
+ root.doInit(request);
+
+ // Remember that this root has been initialized
+ initedRoots.add(rootId);
+
+ // init() might turn on preserve so do this afterwards
+ if (isRootPreserved()) {
+ // Remember this root
+ String windowName = request.getBrowserDetails()
+ .getWindowName();
+ retainOnRefreshRoots.put(windowName, rootId);
+ }
+ }
+ }
+ } // end synchronized block
+
+ return root;
+ }
+
+ /**
+ * Internal helper to finds the root id for a request.
+ *
+ * @param request
+ * the request to get the root id for
+ * @return a root id, or <code>null</code> if no root id is defined
+ *
+ * @since 7.0
+ */
+ private static Integer getRootId(WrappedRequest request) {
+ if (request instanceof CombinedRequest) {
+ // Combined requests has the rootid parameter in the second request
+ CombinedRequest combinedRequest = (CombinedRequest) request;
+ request = combinedRequest.getSecondRequest();
+ }
+ String rootIdString = request
+ .getParameter(ApplicationConnection.ROOT_ID_PARAMETER);
+ Integer rootId = rootIdString == null ? null
+ : new Integer(rootIdString);
+ return rootId;
+ }
+
+ /**
+ * Sets whether the same Root state should be reused if the framework can
+ * detect that the application is opened in a browser window where it has
+ * previously been open. The framework attempts to discover this by checking
+ * the value of window.name in the browser.
+ * <p>
+ * NOTE that you should avoid turning this feature on/off on-the-fly when
+ * the UI is already shown, as it might not be retained as intended.
+ * </p>
+ *
+ * @param rootPreserved
+ * <code>true</code>if the same Root instance should be reused
+ * e.g. when the browser window is refreshed.
+ */
+ public void setRootPreserved(boolean rootPreserved) {
+ this.rootPreserved = rootPreserved;
+ if (!rootPreserved) {
+ retainOnRefreshRoots.clear();
+ }
+ }
+
+ /**
+ * Checks whether the same Root state should be reused if the framework can
+ * detect that the application is opened in a browser window where it has
+ * previously been open. The framework attempts to discover this by checking
+ * the value of window.name in the browser.
+ *
+ * @return <code>true</code>if the same Root instance should be reused e.g.
+ * when the browser window is refreshed.
+ */
+ public boolean isRootPreserved() {
+ return rootPreserved;
+ }
+
+ /**
+ * Checks whether there's a pending initialization for the root with the
+ * given id.
+ *
+ * @param rootId
+ * root id to check for
+ * @return <code>true</code> of the initialization is pending,
+ * <code>false</code> if the root id is not registered or if the
+ * root has already been initialized
+ *
+ * @see #getRootForRequest(WrappedRequest)
+ */
+ public boolean isRootInitPending(int rootId) {
+ return !initedRoots.contains(Integer.valueOf(rootId));
+ }
+
+ /**
+ * Gets all the roots of this application. This includes roots that have
+ * been requested but not yet initialized. Please note, that roots are not
+ * automatically removed e.g. if the browser window is closed and that there
+ * is no way to manually remove a root. Inactive roots will thus not be
+ * released for GC until the entire application is released when the session
+ * has timed out (unless there are dangling references). Improved support
+ * for releasing unused roots is planned for an upcoming alpha release of
+ * Vaadin 7.
+ *
+ * @return a collection of roots belonging to this application
+ *
+ * @since 7.0
+ */
+ public Collection<Root> getRoots() {
+ return Collections.unmodifiableCollection(roots.values());
+ }
+
+ private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
+
+ private int connectorIdSequence = 0;
+
+ /**
+ * Generate an id for the given Connector. Connectors must not call this
+ * method more than once, the first time they need an id.
+ *
+ * @param connector
+ * A connector that has not yet been assigned an id.
+ * @return A new id for the connector
+ */
+ public String createConnectorId(ClientConnector connector) {
+ String connectorId = String.valueOf(connectorIdSequence++);
+ Connector oldReference = connectorIdToConnector.put(connectorId,
+ connector);
+ if (oldReference != null) {
+ throw new RuntimeException(
+ "An error occured while generating connector ids. A connector with id "
+ + connectorId + " was already found!");
+ }
+ return connectorId;
+ }
+
+ /**
+ * Gets a connector by its id.
+ *
+ * @param connectorId
+ * The connector id to look for
+ * @return The connector with the given id or null if no connector has the
+ * given id
+ */
+ public ClientConnector getConnector(String connectorId) {
+ return connectorIdToConnector.get(connectorId);
+ }
+
+ /**
+ * Cleans the connector map from all connectors that are no longer attached
+ * to the application. This should only be called by the framework.
+ */
+ public void cleanConnectorMap() {
+ // remove detached components from paintableIdMap so they
+ // can be GC'ed
+ Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
+
+ while (iterator.hasNext()) {
+ String connectorId = iterator.next();
+ Connector connector = connectorIdToConnector.get(connectorId);
+ if (connector instanceof Component) {
+ Component component = (Component) connector;
+ if (component.getApplication() != this) {
+ // If component is no longer part of this application,
+ // remove it from the map. If it is re-attached to the
+ // application at some point it will be re-added to this
+ // collection when sent to the client.
+ iterator.remove();
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/com/vaadin/RootRequiresMoreInformationException.java b/src/com/vaadin/RootRequiresMoreInformationException.java
new file mode 100644
index 0000000000..ed0fa41437
--- /dev/null
+++ b/src/com/vaadin/RootRequiresMoreInformationException.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin;
+
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedRequest.BrowserDetails;
+
+/**
+ * Exception that is thrown to indicate that creating or initializing the root
+ * requires information detailed from the web browser ({@link BrowserDetails})
+ * to be present.
+ *
+ * This exception may not be thrown if that information is already present in
+ * the current WrappedRequest.
+ *
+ * @see Application#getRoot(WrappedRequest)
+ * @see WrappedRequest#getBrowserDetails()
+ *
+ * @since 7.0
+ */
+public class RootRequiresMoreInformationException extends Exception {
+ // Nothing of interest here
+}
diff --git a/src/com/vaadin/annotations/EagerInit.java b/src/com/vaadin/annotations/EagerInit.java
new file mode 100644
index 0000000000..c7c2702d2a
--- /dev/null
+++ b/src/com/vaadin/annotations/EagerInit.java
@@ -0,0 +1,30 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.ui.Root;
+
+/**
+ * Indicates that the init method in a Root class can be called before full
+ * browser details ({@link WrappedRequest#getBrowserDetails()}) are available.
+ * This will make the UI appear more quickly, as ensuring the availability of
+ * this information typically requires an additional round trip to the client.
+ *
+ * @see Root#init(com.vaadin.terminal.WrappedRequest)
+ * @see WrappedRequest#getBrowserDetails()
+ *
+ * @since 7.0
+ *
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EagerInit {
+ // No values
+}
diff --git a/src/com/vaadin/annotations/Theme.java b/src/com/vaadin/annotations/Theme.java
new file mode 100644
index 0000000000..7c62b07741
--- /dev/null
+++ b/src/com/vaadin/annotations/Theme.java
@@ -0,0 +1,24 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.ui.Root;
+
+/**
+ * Defines a specific theme for a {@link Root}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Theme {
+ /**
+ * @return simple name of the theme
+ */
+ public String value();
+}
diff --git a/src/com/vaadin/annotations/Widgetset.java b/src/com/vaadin/annotations/Widgetset.java
new file mode 100644
index 0000000000..99113f73f9
--- /dev/null
+++ b/src/com/vaadin/annotations/Widgetset.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.ui.Root;
+
+/**
+ * Defines a specific theme for a {@link Root}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Widgetset {
+ /**
+ * @return name of the widgetset
+ */
+ public String value();
+
+}
diff --git a/src/com/vaadin/data/Buffered.java b/src/com/vaadin/data/Buffered.java
index 28167f71df..1387cb965b 100644
--- a/src/com/vaadin/data/Buffered.java
+++ b/src/com/vaadin/data/Buffered.java
@@ -7,10 +7,6 @@ package com.vaadin.data;
import java.io.Serializable;
import com.vaadin.data.Validator.InvalidValueException;
-import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.SystemError;
/**
* <p>
@@ -77,7 +73,11 @@ public interface Buffered extends Serializable {
*
* @return <code>true</code> if the object is in write-through mode,
* <code>false</code> if it's not.
+ * @deprecated Use {@link #setBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public boolean isWriteThrough();
/**
@@ -95,7 +95,11 @@ public interface Buffered extends Serializable {
* If the implicit commit operation fails because of a
* validation error.
*
+ * @deprecated Use {@link #setBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public void setWriteThrough(boolean writeThrough) throws SourceException,
InvalidValueException;
@@ -112,7 +116,11 @@ public interface Buffered extends Serializable {
*
* @return <code>true</code> if the object is in read-through mode,
* <code>false</code> if it's not.
+ * @deprecated Use {@link #isBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public boolean isReadThrough();
/**
@@ -127,10 +135,52 @@ public interface Buffered extends Serializable {
* @throws SourceException
* If the operation fails because of an exception is thrown by
* the data source. The cause is included in the exception.
+ * @deprecated Use {@link #setBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public void setReadThrough(boolean readThrough) throws SourceException;
/**
+ * Sets the object's buffered mode to the specified status.
+ * <p>
+ * When the object is in buffered mode, an internal buffer will be used to
+ * store changes until {@link #commit()} is called. Calling
+ * {@link #discard()} will revert the internal buffer to the value of the
+ * data source.
+ * </p>
+ * <p>
+ * This is an easier way to use {@link #setReadThrough(boolean)} and
+ * {@link #setWriteThrough(boolean)} and not as error prone. Changing
+ * buffered mode will change both the read through and write through state
+ * of the object.
+ * </p>
+ * <p>
+ * Mixing calls to {@link #setBuffered(boolean)}/{@link #isBuffered()} and
+ * {@link #setReadThrough(boolean)}/{@link #isReadThrough()} or
+ * {@link #setWriteThrough(boolean)}/{@link #isWriteThrough()} is generally
+ * a bad idea.
+ * </p>
+ *
+ * @param buffered
+ * true if buffered mode should be turned on, false otherwise
+ * @since 7.0
+ */
+ public void setBuffered(boolean buffered);
+
+ /**
+ * Checks the buffered mode of this Object.
+ * <p>
+ * This method only returns true if both read and write buffering is used.
+ * </p>
+ *
+ * @return true if buffered mode is on, false otherwise
+ * @since 7.0
+ */
+ public boolean isBuffered();
+
+ /**
* Tests if the value stored in the object has been modified since it was
* last updated from the data source.
*
@@ -151,7 +201,7 @@ public interface Buffered extends Serializable {
*/
@SuppressWarnings("serial")
public class SourceException extends RuntimeException implements
- ErrorMessage, Serializable {
+ Serializable {
/** Source class implementing the buffered interface */
private final Buffered source;
@@ -198,11 +248,7 @@ public interface Buffered extends Serializable {
/**
* Gets the cause of the exception.
*
- * @return The cause for the exception.
- * @throws MoreThanOneCauseException
- * if there is more than one cause for the exception. This
- * is possible if the commit operation triggers more than
- * one error at the same time.
+ * @return The (first) cause for the exception, null if no cause.
*/
@Override
public final Throwable getCause() {
@@ -230,86 +276,5 @@ public interface Buffered extends Serializable {
return source;
}
- /**
- * Gets the error level of this buffered source exception. The level of
- * the exception is maximum error level of all the contained causes.
- * <p>
- * The causes that do not specify error level default to
- * <code>ERROR</code> level. Also source exception without any causes
- * are of level <code>ERROR</code>.
- * </p>
- *
- * @see com.vaadin.terminal.ErrorMessage#getErrorLevel()
- */
- public int getErrorLevel() {
-
- int level = Integer.MIN_VALUE;
-
- for (int i = 0; i < causes.length; i++) {
- final int causeLevel = (causes[i] instanceof ErrorMessage) ? ((ErrorMessage) causes[i])
- .getErrorLevel() : ErrorMessage.ERROR;
- if (causeLevel > level) {
- level = causeLevel;
- }
- }
-
- return level == Integer.MIN_VALUE ? ErrorMessage.ERROR : level;
- }
-
- /* Documented in super interface */
- public void paint(PaintTarget target) throws PaintException {
- target.startTag("error");
- final int level = getErrorLevel();
- if (level > 0 && level <= ErrorMessage.INFORMATION) {
- target.addAttribute("level", "info");
- } else if (level <= ErrorMessage.WARNING) {
- target.addAttribute("level", "warning");
- } else if (level <= ErrorMessage.ERROR) {
- target.addAttribute("level", "error");
- } else if (level <= ErrorMessage.CRITICAL) {
- target.addAttribute("level", "critical");
- } else {
- target.addAttribute("level", "system");
- }
-
- // Paint all the exceptions
- for (int i = 0; i < causes.length; i++) {
- if (causes[i] instanceof ErrorMessage) {
- ((ErrorMessage) causes[i]).paint(target);
- } else {
- new SystemError(causes[i]).paint(target);
- }
- }
-
- target.endTag("error");
-
- }
-
- /* Documented in super interface */
- public void addListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void removeListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void requestRepaint() {
- }
-
- /* Documented in super interface */
- public void requestRepaintRequests() {
- }
-
- public String getDebugId() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public void setDebugId(String id) {
- throw new UnsupportedOperationException(
- "Setting testing id for this Paintable is not implemented");
- }
-
}
}
diff --git a/src/com/vaadin/ui/treetable/Collapsible.java b/src/com/vaadin/data/Collapsible.java
index bec0ba9ae9..06c96b7ea7 100644
--- a/src/com/vaadin/ui/treetable/Collapsible.java
+++ b/src/com/vaadin/data/Collapsible.java
@@ -1,15 +1,14 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.ui.treetable;
+package com.vaadin.data;
-import com.vaadin.data.Container;
import com.vaadin.data.Container.Hierarchical;
import com.vaadin.data.Container.Ordered;
-import com.vaadin.data.Item;
/**
- * Container needed by large lazy loading hierarchies displayed in TreeTable.
+ * Container needed by large lazy loading hierarchies displayed e.g. in
+ * TreeTable.
* <p>
* Container of this type gets notified when a subtree is opened/closed in a
* component displaying its content. This allows container to lazy load subtrees
diff --git a/src/com/vaadin/data/Container.java b/src/com/vaadin/data/Container.java
index 7f64dc6efa..f722e07741 100644
--- a/src/com/vaadin/data/Container.java
+++ b/src/com/vaadin/data/Container.java
@@ -121,7 +121,7 @@ public interface Container extends Serializable {
* ID of the Property to retrieve
* @return Property with the given ID or <code>null</code>
*/
- public Property getContainerProperty(Object itemId, Object propertyId);
+ public Property<?> getContainerProperty(Object itemId, Object propertyId);
/**
* Gets the data type of all Properties identified by the given Property ID.
@@ -1101,4 +1101,4 @@ public interface Container extends Serializable {
*/
public void removeListener(Container.PropertySetChangeListener listener);
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/data/Item.java b/src/com/vaadin/data/Item.java
index 3a884a99ab..98b95aecff 100644
--- a/src/com/vaadin/data/Item.java
+++ b/src/com/vaadin/data/Item.java
@@ -30,7 +30,7 @@ public interface Item extends Serializable {
* identifier of the Property to get
* @return the Property with the given ID or <code>null</code>
*/
- public Property getItemProperty(Object id);
+ public Property<?> getItemProperty(Object id);
/**
* Gets the collection of IDs of all Properties stored in the Item.
diff --git a/src/com/vaadin/data/Property.java b/src/com/vaadin/data/Property.java
index 70d57c3aee..9fab642381 100644
--- a/src/com/vaadin/data/Property.java
+++ b/src/com/vaadin/data/Property.java
@@ -30,12 +30,15 @@ import java.io.Serializable;
* needs to be changed through the implementing class.
* </p>
*
+ * @param T
+ * type of values of the property
+ *
* @author Vaadin Ltd
* @version
* @VERSION@
* @since 3.0
*/
-public interface Property extends Serializable {
+public interface Property<T> extends Serializable {
/**
* Gets the value stored in the Property. The returned object is compatible
@@ -43,7 +46,7 @@ public interface Property extends Serializable {
*
* @return the value stored in the Property
*/
- public Object getValue();
+ public T getValue();
/**
* Sets the value of the Property.
@@ -52,37 +55,18 @@ public interface Property extends Serializable {
* missing, one should declare the Property to be in read-only mode and
* throw <code>Property.ReadOnlyException</code> in this function.
* </p>
- * Note : It is not required, but highly recommended to support setting the
- * value also as a <code>String</code> in addition to the native type of the
- * Property (as given by the <code>getType</code> method). If the
- * <code>String</code> conversion fails or is unsupported, the method should
- * throw <code>Property.ConversionException</code>. The string conversion
- * should at least understand the format returned by the
- * <code>toString</code> method of the Property.
+ *
+ * Note : Since Vaadin 7.0, setting the value of a non-String property as a
+ * String is no longer supported.
*
* @param newValue
* New value of the Property. This should be assignable to the
- * type returned by getType, but also String type should be
- * supported
+ * type returned by getType
*
* @throws Property.ReadOnlyException
* if the object is in read-only mode
- * @throws Property.ConversionException
- * if newValue can't be converted into the Property's native
- * type directly or through String
*/
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException;
-
- /**
- * Returns the value of the Property in human readable textual format. The
- * return value should be assignable to the <code>setValue</code> method if
- * the Property is not in read-only mode.
- *
- * @return <code>String</code> representation of the value stored in the
- * Property
- */
- public String toString();
+ public void setValue(Object newValue) throws Property.ReadOnlyException;
/**
* Returns the type of the Property. The methods <code>getValue</code> and
@@ -93,7 +77,7 @@ public interface Property extends Serializable {
*
* @return type of the Property
*/
- public Class<?> getType();
+ public Class<? extends T> getType();
/**
* Tests if the Property is in read-only mode. In read-only mode calls to
@@ -118,90 +102,94 @@ public interface Property extends Serializable {
public void setReadOnly(boolean newStatus);
/**
- * <code>Exception</code> object that signals that a requested Property
- * modification failed because it's in read-only mode.
+ * A Property that is capable of handle a transaction that can end in commit
+ * or rollback.
*
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.0
+ * Note that this does not refer to e.g. database transactions but rather
+ * two-phase commit that allows resetting old field values on a form etc. if
+ * the commit of one of the properties fails after others have already been
+ * committed. If
+ *
+ * @param <T>
+ * The type of the property
+ * @author Vaadin Ltd
+ * @version @version@
+ * @since 7.0
*/
- @SuppressWarnings("serial")
- public class ReadOnlyException extends RuntimeException {
+ public interface Transactional<T> extends Property<T> {
/**
- * Constructs a new <code>ReadOnlyException</code> without a detail
- * message.
+ * Starts a transaction.
+ *
+ * <p>
+ * If the value is set during a transaction the value must not replace
+ * the original value until {@link #commit()} is called. Still,
+ * {@link #getValue()} must return the current value set in the
+ * transaction. Calling {@link #rollback()} while in a transaction must
+ * rollback the value to what it was before the transaction started.
+ * </p>
+ * <p>
+ * {@link ValueChangeEvent}s must not be emitted for internal value
+ * changes during a transaction. If the value changes as a result of
+ * {@link #commit()}, a {@link ValueChangeEvent} should be emitted.
+ * </p>
*/
- public ReadOnlyException() {
- }
+ public void startTransaction();
/**
- * Constructs a new <code>ReadOnlyException</code> with the specified
- * detail message.
- *
- * @param msg
- * the detail message
+ * Commits and ends the transaction that is in progress.
+ * <p>
+ * If the value is changed as a result of this operation, a
+ * {@link ValueChangeEvent} is emitted if such are supported.
+ * <p>
+ * This method has no effect if there is no transaction is in progress.
+ * <p>
+ * This method must never throw an exception.
*/
- public ReadOnlyException(String msg) {
- super(msg);
- }
+ public void commit();
+
+ /**
+ * Aborts and rolls back the transaction that is in progress.
+ * <p>
+ * The value is reset to the value before the transaction started. No
+ * {@link ValueChangeEvent} is emitted as a result of this.
+ * <p>
+ * This method has no effect if there is no transaction is in progress.
+ * <p>
+ * This method must never throw an exception.
+ */
+ public void rollback();
}
/**
- * An exception that signals that the value passed to the
- * <code>setValue</code> method couldn't be converted to the native type of
- * the Property.
+ * <code>Exception</code> object that signals that a requested Property
+ * modification failed because it's in read-only mode.
*
- * @author Vaadin Ltd
+ * @author Vaadin Ltd.
* @version
* @VERSION@
* @since 3.0
*/
@SuppressWarnings("serial")
- public class ConversionException extends RuntimeException {
+ public class ReadOnlyException extends RuntimeException {
/**
- * Constructs a new <code>ConversionException</code> without a detail
+ * Constructs a new <code>ReadOnlyException</code> without a detail
* message.
*/
- public ConversionException() {
+ public ReadOnlyException() {
}
/**
- * Constructs a new <code>ConversionException</code> with the specified
+ * Constructs a new <code>ReadOnlyException</code> with the specified
* detail message.
*
* @param msg
* the detail message
*/
- public ConversionException(String msg) {
+ public ReadOnlyException(String msg) {
super(msg);
}
-
- /**
- * Constructs a new <code>ConversionException</code> from another
- * exception.
- *
- * @param cause
- * The cause of the the conversion failure
- */
- public ConversionException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Constructs a new <code>ConversionException</code> with the specified
- * detail message and cause.
- *
- * @param message
- * the detail message
- * @param cause
- * The cause of the the conversion failure
- */
- public ConversionException(String message, Throwable cause) {
- super(message, cause);
- }
}
/**
diff --git a/src/com/vaadin/data/Validator.java b/src/com/vaadin/data/Validator.java
index fc4cdf5b42..768a02babe 100644
--- a/src/com/vaadin/data/Validator.java
+++ b/src/com/vaadin/data/Validator.java
@@ -6,9 +6,6 @@ package com.vaadin.data;
import java.io.Serializable;
-import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
/**
@@ -20,15 +17,20 @@ import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
* value.
* </p>
* <p>
- * {@link #isValid(Object)} and {@link #validate(Object)} can be used to check
- * if a value is valid. {@link #isValid(Object)} and {@link #validate(Object)}
- * must use the same validation logic so that iff {@link #isValid(Object)}
- * returns false, {@link #validate(Object)} throws an
- * {@link InvalidValueException}.
+ * {@link #validate(Object)} can be used to check if a value is valid. An
+ * {@link InvalidValueException} with an appropriate validation error message is
+ * thrown if the value is not valid.
* </p>
* <p>
* Validators must not have any side effects.
* </p>
+ * <p>
+ * Since Vaadin 7, the method isValid(Object) does not exist in the interface -
+ * {@link #validate(Object)} should be used instead, and the exception caught
+ * where applicable. Concrete classes implementing {@link Validator} can still
+ * internally implement and use isValid(Object) for convenience or to ease
+ * migration from earlier Vaadin versions.
+ * </p>
*
* @author Vaadin Ltd.
* @version
@@ -50,18 +52,6 @@ public interface Validator extends Serializable {
public void validate(Object value) throws Validator.InvalidValueException;
/**
- * Tests if the given value is valid. This method must be symmetric with
- * {@link #validate(Object)} so that {@link #validate(Object)} throws an
- * error iff this method returns false.
- *
- * @param value
- * the value to check
- * @return <code>true</code> if the value is valid, <code>false</code>
- * otherwise.
- */
- public boolean isValid(Object value);
-
- /**
* Exception that is thrown by a {@link Validator} when a value is invalid.
*
* <p>
@@ -76,8 +66,7 @@ public interface Validator extends Serializable {
* @since 3.0
*/
@SuppressWarnings("serial")
- public class InvalidValueException extends RuntimeException implements
- ErrorMessage {
+ public class InvalidValueException extends RuntimeException {
/**
* Array of one or more validation errors that are causing this
@@ -141,104 +130,16 @@ public interface Validator extends Serializable {
return true;
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.ErrorMessage#getErrorLevel()
- */
- public final int getErrorLevel() {
- return ErrorMessage.ERROR;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.Paintable#paint(com.vaadin.terminal.PaintTarget)
- */
- public void paint(PaintTarget target) throws PaintException {
- target.startTag("error");
- target.addAttribute("level", "error");
-
- // Error message
- final String message = getHtmlMessage();
- if (message != null) {
- target.addText(message);
- }
-
- // Paint all the causes
- for (int i = 0; i < causes.length; i++) {
- causes[i].paint(target);
- }
-
- target.endTag("error");
- }
-
/**
* Returns the message of the error in HTML.
*
* Note that this API may change in future versions.
*/
- protected String getHtmlMessage() {
+ public String getHtmlMessage() {
return AbstractApplicationServlet
.safeEscapeForHtml(getLocalizedMessage());
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.ErrorMessage#addListener(com.vaadin.terminal.
- * Paintable.RepaintRequestListener)
- */
- public void addListener(RepaintRequestListener listener) {
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.ErrorMessage#removeListener(com.vaadin.terminal
- * .Paintable.RepaintRequestListener)
- */
- public void removeListener(RepaintRequestListener listener) {
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.ErrorMessage#requestRepaint()
- */
- public void requestRepaint() {
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Paintable#requestRepaintRequests()
- */
- public void requestRepaintRequests() {
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Paintable#getDebugId()
- */
- public String getDebugId() {
- return null;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Paintable#setDebugId(java.lang.String)
- */
- public void setDebugId(String id) {
- throw new UnsupportedOperationException(
- "InvalidValueException cannot have a debug id");
- }
-
/**
* Returns the {@code InvalidValueExceptions} that caused this
* exception.
diff --git a/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
new file mode 100644
index 0000000000..b8efa5b1e4
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
@@ -0,0 +1,157 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.lang.reflect.Method;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.validator.BeanValidator;
+import com.vaadin.ui.Field;
+
+public class BeanFieldGroup<T> extends FieldGroup {
+
+ private Class<T> beanType;
+
+ private static Boolean beanValidationImplementationAvailable = null;
+
+ public BeanFieldGroup(Class<T> beanType) {
+ this.beanType = beanType;
+ }
+
+ @Override
+ protected Class<?> getPropertyType(Object propertyId) {
+ if (getItemDataSource() != null) {
+ return super.getPropertyType(propertyId);
+ } else {
+ // Data source not set so we need to figure out the type manually
+ /*
+ * toString should never really be needed as propertyId should be of
+ * form "fieldName" or "fieldName.subField[.subField2]" but the
+ * method declaration comes from parent.
+ */
+ java.lang.reflect.Field f;
+ try {
+ f = getField(beanType, propertyId.toString());
+ return f.getType();
+ } catch (SecurityException e) {
+ throw new BindException("Cannot determine type of propertyId '"
+ + propertyId + "'.", e);
+ } catch (NoSuchFieldException e) {
+ throw new BindException("Cannot determine type of propertyId '"
+ + propertyId + "'. The propertyId was not found in "
+ + beanType.getName(), e);
+ }
+ }
+ }
+
+ private static java.lang.reflect.Field getField(Class<?> cls,
+ String propertyId) throws SecurityException, NoSuchFieldException {
+ if (propertyId.contains(".")) {
+ String[] parts = propertyId.split("\\.", 2);
+ // Get the type of the field in the "cls" class
+ java.lang.reflect.Field field1 = getField(cls, parts[0]);
+ // Find the rest from the sub type
+ return getField(field1.getType(), parts[1]);
+ } else {
+ try {
+ // Try to find the field directly in the given class
+ java.lang.reflect.Field field1 = cls
+ .getDeclaredField(propertyId);
+ return field1;
+ } catch (NoSuchFieldError e) {
+ // Try super classes until we reach Object
+ Class<?> superClass = cls.getSuperclass();
+ if (superClass != Object.class) {
+ return getField(superClass, propertyId);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method for setting the data source directly using a bean. This
+ * method wraps the bean in a {@link BeanItem} and calls
+ * {@link #setItemDataSource(Item)}.
+ *
+ * @param bean
+ * The bean to use as data source.
+ */
+ public void setItemDataSource(T bean) {
+ setItemDataSource(new BeanItem(bean));
+ }
+
+ @Override
+ public void setItemDataSource(Item item) {
+ if (!(item instanceof BeanItem)) {
+ throw new RuntimeException(getClass().getSimpleName()
+ + " only supports BeanItems as item data source");
+ }
+ super.setItemDataSource(item);
+ }
+
+ @Override
+ public BeanItem<T> getItemDataSource() {
+ return (BeanItem<T>) super.getItemDataSource();
+ }
+
+ @Override
+ public void bind(Field field, Object propertyId) {
+ if (getItemDataSource() != null) {
+ // The data source is set so the property must be found in the item.
+ // If it is not we try to add it.
+ try {
+ getItemProperty(propertyId);
+ } catch (BindException e) {
+ // Not found, try to add a nested property;
+ // BeanItem property ids are always strings so this is safe
+ getItemDataSource().addNestedProperty((String) propertyId);
+ }
+ }
+
+ super.bind(field, propertyId);
+ }
+
+ @Override
+ protected void configureField(Field<?> field) {
+ super.configureField(field);
+ // Add Bean validators if there are annotations
+ if (isBeanValidationImplementationAvailable()) {
+ BeanValidator validator = new BeanValidator(beanType,
+ getPropertyId(field).toString());
+ field.addValidator(validator);
+ if (field.getLocale() != null) {
+ validator.setLocale(field.getLocale());
+ }
+ }
+ }
+
+ /**
+ * Checks whether a bean validation implementation (e.g. Hibernate Validator
+ * or Apache Bean Validation) is available.
+ *
+ * TODO move this method to some more generic location
+ *
+ * @return true if a JSR-303 bean validation implementation is available
+ */
+ protected static boolean isBeanValidationImplementationAvailable() {
+ if (beanValidationImplementationAvailable != null) {
+ return beanValidationImplementationAvailable;
+ }
+ try {
+ Class<?> validationClass = Class
+ .forName("javax.validation.Validation");
+ Method buildFactoryMethod = validationClass
+ .getMethod("buildDefaultValidatorFactory");
+ Object factory = buildFactoryMethod.invoke(null);
+ beanValidationImplementationAvailable = (factory != null);
+ } catch (Exception e) {
+ // no bean validation implementation available
+ beanValidationImplementationAvailable = false;
+ }
+ return beanValidationImplementationAvailable;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/data/fieldgroup/Caption.java b/src/com/vaadin/data/fieldgroup/Caption.java
new file mode 100644
index 0000000000..b990b720cd
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/Caption.java
@@ -0,0 +1,15 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Caption {
+ String value();
+}
diff --git a/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java
new file mode 100644
index 0000000000..569f643998
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java
@@ -0,0 +1,156 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.util.EnumSet;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.fieldgroup.FieldGroup.BindException;
+import com.vaadin.ui.AbstractSelect;
+import com.vaadin.ui.AbstractTextField;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Field;
+import com.vaadin.ui.ListSelect;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.OptionGroup;
+import com.vaadin.ui.RichTextArea;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.TextField;
+
+public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory {
+
+ public static final Object CAPTION_PROPERTY_ID = "Caption";
+
+ public <T extends Field> T createField(Class<?> type, Class<T> fieldType) {
+ if (Enum.class.isAssignableFrom(type)) {
+ return createEnumField(type, fieldType);
+ } else if (Boolean.class.isAssignableFrom(type)
+ || boolean.class.isAssignableFrom(type)) {
+ return createBooleanField(fieldType);
+ }
+ if (AbstractTextField.class.isAssignableFrom(fieldType)) {
+ return fieldType.cast(createAbstractTextField(fieldType
+ .asSubclass(AbstractTextField.class)));
+ } else if (fieldType == RichTextArea.class) {
+ return fieldType.cast(createRichTextArea());
+ }
+ return createDefaultField(type, fieldType);
+ }
+
+ protected RichTextArea createRichTextArea() {
+ RichTextArea rta = new RichTextArea();
+ rta.setImmediate(true);
+
+ return rta;
+ }
+
+ private <T extends Field> T createEnumField(Class<?> type,
+ Class<T> fieldType) {
+ if (AbstractSelect.class.isAssignableFrom(fieldType)) {
+ AbstractSelect s = createCompatibleSelect((Class<? extends AbstractSelect>) fieldType);
+ populateWithEnumData(s, (Class<? extends Enum>) type);
+ return (T) s;
+ }
+
+ return null;
+ }
+
+ protected AbstractSelect createCompatibleSelect(
+ Class<? extends AbstractSelect> fieldType) {
+ AbstractSelect select;
+ if (fieldType.isAssignableFrom(ListSelect.class)) {
+ select = new ListSelect();
+ select.setMultiSelect(false);
+ } else if (fieldType.isAssignableFrom(NativeSelect.class)) {
+ select = new NativeSelect();
+ } else if (fieldType.isAssignableFrom(OptionGroup.class)) {
+ select = new OptionGroup();
+ select.setMultiSelect(false);
+ } else if (fieldType.isAssignableFrom(Table.class)) {
+ Table t = new Table();
+ t.setSelectable(true);
+ select = t;
+ } else {
+ select = new ComboBox(null);
+ }
+ select.setImmediate(true);
+ select.setNullSelectionAllowed(false);
+
+ return select;
+ }
+
+ protected <T extends Field> T createBooleanField(Class<T> fieldType) {
+ if (fieldType.isAssignableFrom(CheckBox.class)) {
+ CheckBox cb = new CheckBox(null);
+ cb.setImmediate(true);
+ return (T) cb;
+ } else if (AbstractTextField.class.isAssignableFrom(fieldType)) {
+ return (T) createAbstractTextField((Class<? extends AbstractTextField>) fieldType);
+ }
+
+ return null;
+ }
+
+ protected <T extends AbstractTextField> T createAbstractTextField(
+ Class<T> fieldType) {
+ if (fieldType == AbstractTextField.class) {
+ fieldType = (Class<T>) TextField.class;
+ }
+ try {
+ T field = fieldType.newInstance();
+ field.setImmediate(true);
+ return field;
+ } catch (Exception e) {
+ throw new BindException("Could not create a field of type "
+ + fieldType, e);
+ }
+ }
+
+ /**
+ * Fallback when no specific field has been created. Typically returns a
+ * TextField.
+ *
+ * @param <T>
+ * The type of field to create
+ * @param type
+ * The type of data that should be edited
+ * @param fieldType
+ * The type of field to create
+ * @return A field capable of editing the data or null if no field could be
+ * created
+ */
+ protected <T extends Field> T createDefaultField(Class<?> type,
+ Class<T> fieldType) {
+ if (fieldType.isAssignableFrom(TextField.class)) {
+ return fieldType.cast(createAbstractTextField(TextField.class));
+ }
+ return null;
+ }
+
+ /**
+ * Populates the given select with all the enums in the given {@link Enum}
+ * class. Uses {@link Enum}.toString() for caption.
+ *
+ * @param select
+ * The select to populate
+ * @param enumClass
+ * The Enum class to use
+ */
+ protected void populateWithEnumData(AbstractSelect select,
+ Class<? extends Enum> enumClass) {
+ select.removeAllItems();
+ for (Object p : select.getContainerPropertyIds()) {
+ select.removeContainerProperty(p);
+ }
+ select.addContainerProperty(CAPTION_PROPERTY_ID, String.class, "");
+ select.setItemCaptionPropertyId(CAPTION_PROPERTY_ID);
+ @SuppressWarnings("unchecked")
+ EnumSet<?> enumSet = EnumSet.allOf(enumClass);
+ for (Object r : enumSet) {
+ Item newItem = select.addItem(r);
+ newItem.getItemProperty(CAPTION_PROPERTY_ID).setValue(r.toString());
+ }
+ }
+}
diff --git a/src/com/vaadin/data/fieldgroup/FieldGroup.java b/src/com/vaadin/data/fieldgroup/FieldGroup.java
new file mode 100644
index 0000000000..3df19f5bc9
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/FieldGroup.java
@@ -0,0 +1,978 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.logging.Logger;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.util.TransactionalPropertyWrapper;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.DefaultFieldFactory;
+import com.vaadin.ui.Field;
+import com.vaadin.ui.Form;
+
+/**
+ * FieldGroup provides an easy way of binding fields to data and handling
+ * commits of these fields.
+ * <p>
+ * The functionality of FieldGroup is similar to {@link Form} but
+ * {@link FieldGroup} does not handle layouts in any way. The typical use case
+ * is to create a layout outside the FieldGroup and then use FieldGroup to bind
+ * the fields to a data source.
+ * </p>
+ * <p>
+ * {@link FieldGroup} is not a UI component so it cannot be added to a layout.
+ * Using the buildAndBind methods {@link FieldGroup} can create fields for you
+ * using a FieldGroupFieldFactory but you still have to add them to the correct
+ * position in your layout.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version @version@
+ * @since 7.0
+ */
+public class FieldGroup implements Serializable {
+
+ private static final Logger logger = Logger.getLogger(FieldGroup.class
+ .getName());
+
+ private Item itemDataSource;
+ private boolean buffered = true;
+
+ private boolean enabled = true;
+ private boolean readOnly = false;
+
+ private HashMap<Object, Field<?>> propertyIdToField = new HashMap<Object, Field<?>>();
+ private LinkedHashMap<Field<?>, Object> fieldToPropertyId = new LinkedHashMap<Field<?>, Object>();
+ private List<CommitHandler> commitHandlers = new ArrayList<CommitHandler>();
+
+ /**
+ * The field factory used by builder methods.
+ */
+ private FieldGroupFieldFactory fieldFactory = new DefaultFieldGroupFieldFactory();
+
+ /**
+ * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a
+ * data source for the field binder.
+ *
+ */
+ public FieldGroup() {
+
+ }
+
+ /**
+ * Constructs a field binder that uses the given data source.
+ *
+ * @param itemDataSource
+ * The data source to bind the fields to
+ */
+ public FieldGroup(Item itemDataSource) {
+ setItemDataSource(itemDataSource);
+ }
+
+ /**
+ * Updates the item that is used by this FieldBinder. Rebinds all fields to
+ * the properties in the new item.
+ *
+ * @param itemDataSource
+ * The new item to use
+ */
+ public void setItemDataSource(Item itemDataSource) {
+ this.itemDataSource = itemDataSource;
+
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ bind(f, fieldToPropertyId.get(f));
+ }
+ }
+
+ /**
+ * Gets the item used by this FieldBinder. Note that you must call
+ * {@link #commit()} for the item to be updated unless buffered mode has
+ * been switched off.
+ *
+ * @see #setBuffered(boolean)
+ * @see #commit()
+ *
+ * @return The item used by this FieldBinder
+ */
+ public Item getItemDataSource() {
+ return itemDataSource;
+ }
+
+ /**
+ * Checks the buffered mode for the bound fields.
+ * <p>
+ *
+ * @see #setBuffered(boolean) for more details on buffered mode
+ *
+ * @see Field#isBuffered()
+ * @return true if buffered mode is on, false otherwise
+ *
+ */
+ public boolean isBuffered() {
+ return buffered;
+ }
+
+ /**
+ * Sets the buffered mode for the bound fields.
+ * <p>
+ * When buffered mode is on the item will not be updated until
+ * {@link #commit()} is called. If buffered mode is off the item will be
+ * updated once the fields are updated.
+ * </p>
+ * <p>
+ * The default is to use buffered mode.
+ * </p>
+ *
+ * @see Field#setBuffered(boolean)
+ * @param buffered
+ * true to turn on buffered mode, false otherwise
+ */
+ public void setBuffered(boolean buffered) {
+ if (buffered == this.buffered) {
+ return;
+ }
+
+ this.buffered = buffered;
+ for (Field<?> field : getFields()) {
+ field.setBuffered(buffered);
+ }
+ }
+
+ /**
+ * Returns the enabled status for the fields.
+ * <p>
+ * Note that this will not accurately represent the enabled status of all
+ * fields if you change the enabled status of the fields through some other
+ * method than {@link #setEnabled(boolean)}.
+ *
+ * @return true if the fields are enabled, false otherwise
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Updates the enabled state of all bound fields.
+ *
+ * @param fieldsEnabled
+ * true to enable all bound fields, false to disable them
+ */
+ public void setEnabled(boolean fieldsEnabled) {
+ enabled = fieldsEnabled;
+ for (Field<?> field : getFields()) {
+ field.setEnabled(fieldsEnabled);
+ }
+ }
+
+ /**
+ * Returns the read only status for the fields.
+ * <p>
+ * Note that this will not accurately represent the read only status of all
+ * fields if you change the read only status of the fields through some
+ * other method than {@link #setReadOnly(boolean)}.
+ *
+ * @return true if the fields are set to read only, false otherwise
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ /**
+ * Updates the read only state of all bound fields.
+ *
+ * @param fieldsReadOnly
+ * true to set all bound fields to read only, false to set them
+ * to read write
+ */
+ public void setReadOnly(boolean fieldsReadOnly) {
+ readOnly = fieldsReadOnly;
+ }
+
+ /**
+ * Returns a collection of all fields that have been bound.
+ * <p>
+ * The fields are not returned in any specific order.
+ * </p>
+ *
+ * @return A collection with all bound Fields
+ */
+ public Collection<Field<?>> getFields() {
+ return fieldToPropertyId.keySet();
+ }
+
+ /**
+ * Binds the field with the given propertyId from the current item. If an
+ * item has not been set then the binding is postponed until the item is set
+ * using {@link #setItemDataSource(Item)}.
+ * <p>
+ * This method also adds validators when applicable.
+ * </p>
+ *
+ * @param field
+ * The field to bind
+ * @param propertyId
+ * The propertyId to bind to the field
+ * @throws BindException
+ * If the property id is already bound to another field by this
+ * field binder
+ */
+ public void bind(Field<?> field, Object propertyId) throws BindException {
+ if (propertyIdToField.containsKey(propertyId)
+ && propertyIdToField.get(propertyId) != field) {
+ throw new BindException("Property id " + propertyId
+ + " is already bound to another field");
+ }
+ fieldToPropertyId.put(field, propertyId);
+ propertyIdToField.put(propertyId, field);
+ if (itemDataSource == null) {
+ // Will be bound when data source is set
+ return;
+ }
+
+ field.setPropertyDataSource(wrapInTransactionalProperty(getItemProperty(propertyId)));
+ configureField(field);
+ }
+
+ private <T> Property.Transactional<T> wrapInTransactionalProperty(
+ Property<T> itemProperty) {
+ return new TransactionalPropertyWrapper<T>(itemProperty);
+ }
+
+ /**
+ * Gets the property with the given property id from the item.
+ *
+ * @param propertyId
+ * The id if the property to find
+ * @return The property with the given id from the item
+ * @throws BindException
+ * If the property was not found in the item or no item has been
+ * set
+ */
+ protected Property<?> getItemProperty(Object propertyId)
+ throws BindException {
+ Item item = getItemDataSource();
+ if (item == null) {
+ throw new BindException("Could not lookup property with id "
+ + propertyId + " as no item has been set");
+ }
+ Property<?> p = item.getItemProperty(propertyId);
+ if (p == null) {
+ throw new BindException("A property with id " + propertyId
+ + " was not found in the item");
+ }
+ return p;
+ }
+
+ /**
+ * Detaches the field from its property id and removes it from this
+ * FieldBinder.
+ * <p>
+ * Note that the field is not detached from its property data source if it
+ * is no longer connected to the same property id it was bound to using this
+ * FieldBinder.
+ *
+ * @param field
+ * The field to detach
+ * @throws BindException
+ * If the field is not bound by this field binder or not bound
+ * to the correct property id
+ */
+ public void unbind(Field<?> field) throws BindException {
+ Object propertyId = fieldToPropertyId.get(field);
+ if (propertyId == null) {
+ throw new BindException(
+ "The given field is not part of this FieldBinder");
+ }
+
+ Property fieldDataSource = field.getPropertyDataSource();
+ if (fieldDataSource instanceof TransactionalPropertyWrapper) {
+ fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource)
+ .getWrappedProperty();
+ }
+ if (fieldDataSource == getItemProperty(propertyId)) {
+ field.setPropertyDataSource(null);
+ }
+ fieldToPropertyId.remove(field);
+ propertyIdToField.remove(propertyId);
+ }
+
+ /**
+ * Configures a field with the settings set for this FieldBinder.
+ * <p>
+ * By default this updates the buffered, read only and enabled state of the
+ * field. Also adds validators when applicable.
+ *
+ * @param field
+ * The field to update
+ */
+ protected void configureField(Field<?> field) {
+ field.setBuffered(isBuffered());
+
+ field.setEnabled(isEnabled());
+ field.setReadOnly(isReadOnly());
+ }
+
+ /**
+ * Gets the type of the property with the given property id.
+ *
+ * @param propertyId
+ * The propertyId. Must be find
+ * @return The type of the property
+ */
+ protected Class<?> getPropertyType(Object propertyId) throws BindException {
+ if (getItemDataSource() == null) {
+ throw new BindException(
+ "Property type for '"
+ + propertyId
+ + "' could not be determined. No item data source has been set.");
+ }
+ Property<?> p = getItemDataSource().getItemProperty(propertyId);
+ if (p == null) {
+ throw new BindException(
+ "Property type for '"
+ + propertyId
+ + "' could not be determined. No property with that id was found.");
+ }
+
+ return p.getType();
+ }
+
+ /**
+ * Returns a collection of all property ids that have been bound to fields.
+ * <p>
+ * Note that this will return property ids even before the item has been
+ * set. In that case it returns the property ids that will be bound once the
+ * item is set.
+ * </p>
+ * <p>
+ * No guarantee is given for the order of the property ids
+ * </p>
+ *
+ * @return A collection of bound property ids
+ */
+ public Collection<Object> getBoundPropertyIds() {
+ return Collections.unmodifiableCollection(propertyIdToField.keySet());
+ }
+
+ /**
+ * Returns a collection of all property ids that exist in the item set using
+ * {@link #setItemDataSource(Item)} but have not been bound to fields.
+ * <p>
+ * Will always return an empty collection before an item has been set using
+ * {@link #setItemDataSource(Item)}.
+ * </p>
+ * <p>
+ * No guarantee is given for the order of the property ids
+ * </p>
+ *
+ * @return A collection of property ids that have not been bound to fields
+ */
+ public Collection<Object> getUnboundPropertyIds() {
+ if (getItemDataSource() == null) {
+ return new ArrayList<Object>();
+ }
+ List<Object> unboundPropertyIds = new ArrayList<Object>();
+ unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds());
+ unboundPropertyIds.removeAll(propertyIdToField.keySet());
+ return unboundPropertyIds;
+ }
+
+ /**
+ * Commits all changes done to the bound fields.
+ * <p>
+ * Calls all {@link CommitHandler}s before and after committing the field
+ * changes to the item data source. The whole commit is aborted and state is
+ * restored to what it was before commit was called if any
+ * {@link CommitHandler} throws a CommitException or there is a problem
+ * committing the fields
+ *
+ * @throws CommitException
+ * If the commit was aborted
+ */
+ public void commit() throws CommitException {
+ if (!isBuffered()) {
+ // Not using buffered mode, nothing to do
+ return;
+ }
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ ((Property.Transactional<?>) f.getPropertyDataSource())
+ .startTransaction();
+ }
+ try {
+ firePreCommitEvent();
+ // Commit the field values to the properties
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ f.commit();
+ }
+ firePostCommitEvent();
+
+ // Commit the properties
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ ((Property.Transactional<?>) f.getPropertyDataSource())
+ .commit();
+ }
+
+ } catch (Exception e) {
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ try {
+ ((Property.Transactional<?>) f.getPropertyDataSource())
+ .rollback();
+ } catch (Exception rollbackException) {
+ // FIXME: What to do ?
+ }
+ }
+
+ throw new CommitException("Commit failed", e);
+ }
+
+ }
+
+ /**
+ * Sends a preCommit event to all registered commit handlers
+ *
+ * @throws CommitException
+ * If the commit should be aborted
+ */
+ private void firePreCommitEvent() throws CommitException {
+ CommitHandler[] handlers = commitHandlers
+ .toArray(new CommitHandler[commitHandlers.size()]);
+
+ for (CommitHandler handler : handlers) {
+ handler.preCommit(new CommitEvent(this));
+ }
+ }
+
+ /**
+ * Sends a postCommit event to all registered commit handlers
+ *
+ * @throws CommitException
+ * If the commit should be aborted
+ */
+ private void firePostCommitEvent() throws CommitException {
+ CommitHandler[] handlers = commitHandlers
+ .toArray(new CommitHandler[commitHandlers.size()]);
+
+ for (CommitHandler handler : handlers) {
+ handler.postCommit(new CommitEvent(this));
+ }
+ }
+
+ /**
+ * Discards all changes done to the bound fields.
+ * <p>
+ * Only has effect if buffered mode is used.
+ *
+ */
+ public void discard() {
+ for (Field<?> f : fieldToPropertyId.keySet()) {
+ try {
+ f.discard();
+ } catch (Exception e) {
+ // TODO: handle exception
+ // What can we do if discard fails other than try to discard all
+ // other fields?
+ }
+ }
+ }
+
+ /**
+ * Returns the field that is bound to the given property id
+ *
+ * @param propertyId
+ * The property id to use to lookup the field
+ * @return The field that is bound to the property id or null if no field is
+ * bound to that property id
+ */
+ public Field<?> getField(Object propertyId) {
+ return propertyIdToField.get(propertyId);
+ }
+
+ /**
+ * Returns the property id that is bound to the given field
+ *
+ * @param field
+ * The field to use to lookup the property id
+ * @return The property id that is bound to the field or null if the field
+ * is not bound to any property id by this FieldBinder
+ */
+ public Object getPropertyId(Field<?> field) {
+ return fieldToPropertyId.get(field);
+ }
+
+ /**
+ * Adds a commit handler.
+ * <p>
+ * The commit handler is called before the field values are committed to the
+ * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item
+ * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a
+ * {@link CommitHandler} throws a CommitException the whole commit is
+ * aborted and the fields retain their old values.
+ *
+ * @param commitHandler
+ * The commit handler to add
+ */
+ public void addCommitHandler(CommitHandler commitHandler) {
+ commitHandlers.add(commitHandler);
+ }
+
+ /**
+ * Removes the given commit handler.
+ *
+ * @see #addCommitHandler(CommitHandler)
+ *
+ * @param commitHandler
+ * The commit handler to remove
+ */
+ public void removeCommitHandler(CommitHandler commitHandler) {
+ commitHandlers.remove(commitHandler);
+ }
+
+ /**
+ * Returns a list of all commit handlers for this {@link FieldGroup}.
+ * <p>
+ * Use {@link #addCommitHandler(CommitHandler)} and
+ * {@link #removeCommitHandler(CommitHandler)} to register or unregister a
+ * commit handler.
+ *
+ * @return A collection of commit handlers
+ */
+ protected Collection<CommitHandler> getCommitHandlers() {
+ return Collections.unmodifiableCollection(commitHandlers);
+ }
+
+ /**
+ * CommitHandlers are used by {@link FieldGroup#commit()} as part of the
+ * commit transactions. CommitHandlers can perform custom operations as part
+ * of the commit and cause the commit to be aborted by throwing a
+ * {@link CommitException}.
+ */
+ public interface CommitHandler extends Serializable {
+ /**
+ * Called before changes are committed to the field and the item is
+ * updated.
+ * <p>
+ * Throw a {@link CommitException} to abort the commit.
+ *
+ * @param commitEvent
+ * An event containing information regarding the commit
+ * @throws CommitException
+ * if the commit should be aborted
+ */
+ public void preCommit(CommitEvent commitEvent) throws CommitException;
+
+ /**
+ * Called after changes are committed to the fields and the item is
+ * updated..
+ * <p>
+ * Throw a {@link CommitException} to abort the commit.
+ *
+ * @param commitEvent
+ * An event containing information regarding the commit
+ * @throws CommitException
+ * if the commit should be aborted
+ */
+ public void postCommit(CommitEvent commitEvent) throws CommitException;
+ }
+
+ /**
+ * FIXME javadoc
+ *
+ */
+ public static class CommitEvent implements Serializable {
+ private FieldGroup fieldBinder;
+
+ private CommitEvent(FieldGroup fieldBinder) {
+ this.fieldBinder = fieldBinder;
+ }
+
+ /**
+ * Returns the field binder that this commit relates to
+ *
+ * @return The FieldBinder that is being committed.
+ */
+ public FieldGroup getFieldBinder() {
+ return fieldBinder;
+ }
+
+ }
+
+ /**
+ * Checks the validity of the bound fields.
+ * <p>
+ * Call the {@link Field#validate()} for the fields to get the individual
+ * error messages.
+ *
+ * @return true if all bound fields are valid, false otherwise.
+ */
+ public boolean isValid() {
+ try {
+ for (Field<?> field : getFields()) {
+ field.validate();
+ }
+ return true;
+ } catch (InvalidValueException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if any bound field has been modified.
+ *
+ * @return true if at least on field has been modified, false otherwise
+ */
+ public boolean isModified() {
+ for (Field<?> field : getFields()) {
+ if (field.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the field factory for the {@link FieldGroup}. The field factory is
+ * only used when {@link FieldGroup} creates a new field.
+ *
+ * @return The field factory in use
+ *
+ */
+ public FieldGroupFieldFactory getFieldFactory() {
+ return fieldFactory;
+ }
+
+ /**
+ * Sets the field factory for the {@link FieldGroup}. The field factory is
+ * only used when {@link FieldGroup} creates a new field.
+ *
+ * @param fieldFactory
+ * The field factory to use
+ */
+ public void setFieldFactory(FieldGroupFieldFactory fieldFactory) {
+ this.fieldFactory = fieldFactory;
+ }
+
+ /**
+ * Binds member fields found in the given object.
+ * <p>
+ * This method processes all (Java) member fields whose type extends
+ * {@link Field} and that can be mapped to a property id. Property id
+ * mapping is done based on the field name or on a @{@link PropertyId}
+ * annotation on the field. All non-null fields for which a property id can
+ * be determined are bound to the property id.
+ * </p>
+ * <p>
+ * For example:
+ *
+ * <pre>
+ * public class MyForm extends VerticalLayout {
+ * private TextField firstName = new TextField("First name");
+ * @PropertyId("last")
+ * private TextField lastName = new TextField("Last name");
+ * private TextField age = new TextField("Age"); ... }
+ *
+ * MyForm myForm = new MyForm();
+ * ...
+ * fieldGroup.bindMemberFields(myForm);
+ * </pre>
+ *
+ * </p>
+ * This binds the firstName TextField to a "firstName" property in the item,
+ * lastName TextField to a "last" property and the age TextField to a "age"
+ * property.
+ *
+ * @param objectWithMemberFields
+ * The object that contains (Java) member fields to bind
+ * @throws BindException
+ * If there is a problem binding a field
+ */
+ public void bindMemberFields(Object objectWithMemberFields)
+ throws BindException {
+ buildAndBindMemberFields(objectWithMemberFields, false);
+ }
+
+ /**
+ * Binds member fields found in the given object and builds member fields
+ * that have not been initialized.
+ * <p>
+ * This method processes all (Java) member fields whose type extends
+ * {@link Field} and that can be mapped to a property id. Property id
+ * mapping is done based on the field name or on a @{@link PropertyId}
+ * annotation on the field. Fields that are not initialized (null) are built
+ * using the field factory. All non-null fields for which a property id can
+ * be determined are bound to the property id.
+ * </p>
+ * <p>
+ * For example:
+ *
+ * <pre>
+ * public class MyForm extends VerticalLayout {
+ * private TextField firstName = new TextField("First name");
+ * @PropertyId("last")
+ * private TextField lastName = new TextField("Last name");
+ * private TextField age;
+ *
+ * MyForm myForm = new MyForm();
+ * ...
+ * fieldGroup.buildAndBindMemberFields(myForm);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * This binds the firstName TextField to a "firstName" property in the item,
+ * lastName TextField to a "last" property and builds an age TextField using
+ * the field factory and then binds it to the "age" property.
+ * </p>
+ *
+ * @param objectWithMemberFields
+ * The object that contains (Java) member fields to build and
+ * bind
+ * @throws BindException
+ * If there is a problem binding or building a field
+ */
+ public void buildAndBindMemberFields(Object objectWithMemberFields)
+ throws BindException {
+ buildAndBindMemberFields(objectWithMemberFields, true);
+ }
+
+ /**
+ * Binds member fields found in the given object and optionally builds
+ * member fields that have not been initialized.
+ * <p>
+ * This method processes all (Java) member fields whose type extends
+ * {@link Field} and that can be mapped to a property id. Property id
+ * mapping is done based on the field name or on a @{@link PropertyId}
+ * annotation on the field. Fields that are not initialized (null) are built
+ * using the field factory is buildFields is true. All non-null fields for
+ * which a property id can be determined are bound to the property id.
+ * </p>
+ *
+ * @param objectWithMemberFields
+ * The object that contains (Java) member fields to build and
+ * bind
+ * @throws BindException
+ * If there is a problem binding or building a field
+ */
+ protected void buildAndBindMemberFields(Object objectWithMemberFields,
+ boolean buildFields) throws BindException {
+ Class<?> objectClass = objectWithMemberFields.getClass();
+
+ for (java.lang.reflect.Field memberField : objectClass
+ .getDeclaredFields()) {
+
+ if (!Field.class.isAssignableFrom(memberField.getType())) {
+ // Process next field
+ continue;
+ }
+
+ PropertyId propertyIdAnnotation = memberField
+ .getAnnotation(PropertyId.class);
+
+ Class<? extends Field> fieldType = (Class<? extends Field>) memberField
+ .getType();
+
+ Object propertyId = null;
+ if (propertyIdAnnotation != null) {
+ // @PropertyId(propertyId) always overrides property id
+ propertyId = propertyIdAnnotation.value();
+ } else {
+ propertyId = memberField.getName();
+ }
+
+ // Ensure that the property id exists
+ Class<?> propertyType;
+
+ try {
+ propertyType = getPropertyType(propertyId);
+ } catch (BindException e) {
+ // Property id was not found, skip this field
+ continue;
+ }
+
+ Field<?> field;
+ try {
+ // Get the field from the object
+ field = (Field<?>) ReflectTools.getJavaFieldValue(
+ objectWithMemberFields, memberField);
+ } catch (Exception e) {
+ // If we cannot determine the value, just skip the field and try
+ // the next one
+ continue;
+ }
+
+ if (field == null && buildFields) {
+ Caption captionAnnotation = memberField
+ .getAnnotation(Caption.class);
+ String caption;
+ if (captionAnnotation != null) {
+ caption = captionAnnotation.value();
+ } else {
+ caption = DefaultFieldFactory
+ .createCaptionByPropertyId(propertyId);
+ }
+
+ // Create the component (Field)
+ field = build(caption, propertyType, fieldType);
+
+ // Store it in the field
+ try {
+ ReflectTools.setJavaFieldValue(objectWithMemberFields,
+ memberField, field);
+ } catch (IllegalArgumentException e) {
+ throw new BindException("Could not assign value to field '"
+ + memberField.getName() + "'", e);
+ } catch (IllegalAccessException e) {
+ throw new BindException("Could not assign value to field '"
+ + memberField.getName() + "'", e);
+ } catch (InvocationTargetException e) {
+ throw new BindException("Could not assign value to field '"
+ + memberField.getName() + "'", e);
+ }
+ }
+
+ if (field != null) {
+ // Bind it to the property id
+ bind(field, propertyId);
+ }
+ }
+ }
+
+ public static class CommitException extends Exception {
+
+ public CommitException() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommitException(String message, Throwable cause) {
+ super(message, cause);
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommitException(String message) {
+ super(message);
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommitException(Throwable cause) {
+ super(cause);
+ // TODO Auto-generated constructor stub
+ }
+
+ }
+
+ public static class BindException extends RuntimeException {
+
+ public BindException(String message) {
+ super(message);
+ }
+
+ public BindException(String message, Throwable t) {
+ super(message, t);
+ }
+
+ }
+
+ /**
+ * Builds a field and binds it to the given property id using the field
+ * binder.
+ *
+ * @param propertyId
+ * The property id to bind to. Must be present in the field
+ * finder.
+ * @throws BindException
+ * If there is a problem while building or binding
+ * @return The created and bound field
+ */
+ public Field<?> buildAndBind(Object propertyId) throws BindException {
+ String caption = DefaultFieldFactory
+ .createCaptionByPropertyId(propertyId);
+ return buildAndBind(caption, propertyId);
+ }
+
+ /**
+ * Builds a field using the given caption and binds it to the given property
+ * id using the field binder.
+ *
+ * @param caption
+ * The caption for the field
+ * @param propertyId
+ * The property id to bind to. Must be present in the field
+ * finder.
+ * @throws BindException
+ * If there is a problem while building or binding
+ * @return The created and bound field. Can be any type of {@link Field}.
+ */
+ public Field<?> buildAndBind(String caption, Object propertyId)
+ throws BindException {
+ Class<?> type = getPropertyType(propertyId);
+ return buildAndBind(caption, propertyId, Field.class);
+
+ }
+
+ /**
+ * Builds a field using the given caption and binds it to the given property
+ * id using the field binder. Ensures the new field is of the given type.
+ *
+ * @param caption
+ * The caption for the field
+ * @param propertyId
+ * The property id to bind to. Must be present in the field
+ * finder.
+ * @throws BindException
+ * If the field could not be created
+ * @return The created and bound field. Can be any type of {@link Field}.
+ */
+
+ public <T extends Field> T buildAndBind(String caption, Object propertyId,
+ Class<T> fieldType) throws BindException {
+ Class<?> type = getPropertyType(propertyId);
+
+ T field = build(caption, type, fieldType);
+ bind(field, propertyId);
+
+ return field;
+ }
+
+ /**
+ * Creates a field based on the given data type.
+ * <p>
+ * The data type is the type that we want to edit using the field. The field
+ * type is the type of field we want to create, can be {@link Field} if any
+ * Field is good.
+ * </p>
+ *
+ * @param caption
+ * The caption for the new field
+ * @param dataType
+ * The data model type that we want to edit using the field
+ * @param fieldType
+ * The type of field that we want to create
+ * @return A Field capable of editing the given type
+ * @throws BindException
+ * If the field could not be created
+ */
+ protected <T extends Field> T build(String caption, Class<?> dataType,
+ Class<T> fieldType) throws BindException {
+ T field = getFieldFactory().createField(dataType, fieldType);
+ if (field == null) {
+ throw new BindException("Unable to build a field of type "
+ + fieldType.getName() + " for editing "
+ + dataType.getName());
+ }
+
+ field.setCaption(caption);
+ return field;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java b/src/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java
new file mode 100644
index 0000000000..80c012cbdc
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java
@@ -0,0 +1,31 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.io.Serializable;
+
+import com.vaadin.ui.Field;
+
+/**
+ * Factory interface for creating new Field-instances based on the data type
+ * that should be edited.
+ *
+ * @author Vaadin Ltd.
+ * @version @version@
+ * @since 7.0
+ */
+public interface FieldGroupFieldFactory extends Serializable {
+ /**
+ * Creates a field based on the data type that we want to edit
+ *
+ * @param dataType
+ * The type that we want to edit using the field
+ * @param fieldType
+ * The type of field we want to create. If set to {@link Field}
+ * then any type of field is accepted
+ * @return A field that can be assigned to the given fieldType and that is
+ * capable of editing the given type of data
+ */
+ <T extends Field> T createField(Class<?> dataType, Class<T> fieldType);
+}
diff --git a/src/com/vaadin/data/fieldgroup/PropertyId.java b/src/com/vaadin/data/fieldgroup/PropertyId.java
new file mode 100644
index 0000000000..268047401d
--- /dev/null
+++ b/src/com/vaadin/data/fieldgroup/PropertyId.java
@@ -0,0 +1,15 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PropertyId {
+ String value();
+}
diff --git a/src/com/vaadin/data/util/AbstractBeanContainer.java b/src/com/vaadin/data/util/AbstractBeanContainer.java
index 6260e05518..bed3ca0450 100644
--- a/src/com/vaadin/data/util/AbstractBeanContainer.java
+++ b/src/com/vaadin/data/util/AbstractBeanContainer.java
@@ -104,8 +104,9 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
+ " not found");
}
try {
- Property property = pd.createProperty(bean);
- return (IDTYPE) property.getValue();
+ Property<IDTYPE> property = (Property<IDTYPE>) pd
+ .createProperty(bean);
+ return property.getValue();
} catch (MethodException e) {
throw new IllegalArgumentException(e);
}
@@ -256,7 +257,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
* @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
* java.lang.Object)
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
Item item = getItem(itemId);
if (item == null) {
return null;
@@ -371,7 +372,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
* The id of the property
*/
private void addValueChangeListener(Item item, Object propertyId) {
- Property property = item.getItemProperty(propertyId);
+ Property<?> property = item.getItemProperty(propertyId);
if (property instanceof ValueChangeNotifier) {
// avoid multiple notifications for the same property if
// multiple filters are in use
@@ -390,7 +391,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
* The id of the property
*/
private void removeValueChangeListener(Item item, Object propertyId) {
- Property property = item.getItemProperty(propertyId);
+ Property<?> property = item.getItemProperty(propertyId);
if (property instanceof ValueChangeNotifier) {
((ValueChangeNotifier) property).removeListener(this);
}
@@ -754,9 +755,9 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
}
model.put(propertyId, propertyDescriptor);
- for (BeanItem item : itemIdToItem.values()) {
- item.addItemProperty(propertyId, propertyDescriptor
- .createProperty((BEANTYPE) item.getBean()));
+ for (BeanItem<BEANTYPE> item : itemIdToItem.values()) {
+ item.addItemProperty(propertyId,
+ propertyDescriptor.createProperty(item.getBean()));
}
// Sends a change event
@@ -775,7 +776,6 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
* @see NestedMethodProperty
*
* @param propertyId
- * @param propertyType
* @return true if the property was added
*/
public boolean addNestedContainerProperty(String propertyId) {
@@ -783,6 +783,41 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
propertyId, type));
}
+ /**
+ * Adds a nested container properties for all sub-properties of a named
+ * property to the container. The named property itself is removed from the
+ * model as its subproperties are added.
+ *
+ * All intermediate getters must exist and must return non-null values when
+ * the property value is accessed.
+ *
+ * @see NestedMethodProperty
+ * @see #addNestedContainerProperty(String)
+ *
+ * @param propertyId
+ */
+ @SuppressWarnings("unchecked")
+ public void addNestedContainerBean(String propertyId) {
+ Class<?> propertyType = getType(propertyId);
+ LinkedHashMap<String, VaadinPropertyDescriptor<Object>> pds = BeanItem
+ .getPropertyDescriptors((Class<Object>) propertyType);
+ for (String subPropertyId : pds.keySet()) {
+ String qualifiedPropertyId = propertyId + "." + subPropertyId;
+ NestedPropertyDescriptor<BEANTYPE> pd = new NestedPropertyDescriptor<BEANTYPE>(
+ qualifiedPropertyId, (Class<BEANTYPE>) type);
+ model.put(qualifiedPropertyId, pd);
+ model.remove(propertyId);
+ for (BeanItem<BEANTYPE> item : itemIdToItem.values()) {
+ item.addItemProperty(propertyId,
+ pd.createProperty(item.getBean()));
+ item.removeItemProperty(propertyId);
+ }
+ }
+
+ // Sends a change event
+ fireContainerPropertySetChange();
+ }
+
@Override
public boolean removeContainerProperty(Object propertyId)
throws UnsupportedOperationException {
diff --git a/src/com/vaadin/data/util/AbstractProperty.java b/src/com/vaadin/data/util/AbstractProperty.java
index f9c6faacf1..3b6db3807e 100644
--- a/src/com/vaadin/data/util/AbstractProperty.java
+++ b/src/com/vaadin/data/util/AbstractProperty.java
@@ -17,7 +17,7 @@ import com.vaadin.data.Property;
*
* @since 6.6
*/
-public abstract class AbstractProperty implements Property,
+public abstract class AbstractProperty<T> implements Property<T>,
Property.ValueChangeNotifier, Property.ReadOnlyStatusChangeNotifier {
/**
@@ -56,18 +56,17 @@ public abstract class AbstractProperty implements Property,
/**
* Returns the value of the <code>Property</code> in human readable textual
- * format. The return value should be assignable to the
- * <code>setValue</code> method if the Property is not in read-only mode.
+ * format.
*
* @return String representation of the value stored in the Property
+ * @deprecated use {@link #getValue()} instead and possibly toString on that
*/
+ @Deprecated
@Override
public String toString() {
- final Object value = getValue();
- if (value == null) {
- return null;
- }
- return value.toString();
+ throw new UnsupportedOperationException(
+ "Use Property.getValue() instead of " + getClass()
+ + ".toString()");
}
/* Events */
@@ -76,8 +75,8 @@ public abstract class AbstractProperty implements Property,
* An <code>Event</code> object specifying the Property whose read-only
* status has been changed.
*/
- protected class ReadOnlyStatusChangeEvent extends java.util.EventObject
- implements Property.ReadOnlyStatusChangeEvent {
+ protected static class ReadOnlyStatusChangeEvent extends
+ java.util.EventObject implements Property.ReadOnlyStatusChangeEvent {
/**
* Constructs a new read-only status change event for this object.
@@ -144,8 +143,8 @@ public abstract class AbstractProperty implements Property,
* An <code>Event</code> object specifying the Property whose value has been
* changed.
*/
- private class ValueChangeEvent extends java.util.EventObject implements
- Property.ValueChangeEvent {
+ private static class ValueChangeEvent extends java.util.EventObject
+ implements Property.ValueChangeEvent {
/**
* Constructs a new value change event for this object.
diff --git a/src/com/vaadin/data/util/BeanItem.java b/src/com/vaadin/data/util/BeanItem.java
index ed59baa9f8..94439471f5 100644
--- a/src/com/vaadin/data/util/BeanItem.java
+++ b/src/com/vaadin/data/util/BeanItem.java
@@ -12,9 +12,11 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* A wrapper class for adding the Item interface to any Java Bean.
@@ -162,7 +164,7 @@ public class BeanItem<BT> extends PropertysetItem {
final Method getMethod = pd.getReadMethod();
if ((getMethod != null)
&& getMethod.getDeclaringClass() != Object.class) {
- VaadinPropertyDescriptor<BT> vaadinPropertyDescriptor = new MethodPropertyDescriptor(
+ VaadinPropertyDescriptor<BT> vaadinPropertyDescriptor = new MethodPropertyDescriptor<BT>(
pd.getName(), pd.getPropertyType(),
pd.getReadMethod(), pd.getWriteMethod());
pdMap.put(pd.getName(), vaadinPropertyDescriptor);
@@ -213,6 +215,49 @@ public class BeanItem<BT> extends PropertysetItem {
}
/**
+ * Expands nested bean properties by replacing a top-level property with
+ * some or all of its sub-properties. The expansion is not recursive.
+ *
+ * @param propertyId
+ * property id for the property whose sub-properties are to be
+ * expanded,
+ * @param subPropertyIds
+ * sub-properties to expand, all sub-properties are expanded if
+ * not specified
+ */
+ public void expandProperty(String propertyId, String... subPropertyIds) {
+ Set<String> subPropertySet = new HashSet<String>(
+ Arrays.asList(subPropertyIds));
+
+ if (0 == subPropertyIds.length) {
+ // Enumerate all sub-properties
+ Class<?> propertyType = getItemProperty(propertyId).getType();
+ Map<String, ?> pds = getPropertyDescriptors(propertyType);
+ subPropertySet.addAll(pds.keySet());
+ }
+
+ for (String subproperty : subPropertySet) {
+ String qualifiedPropertyId = propertyId + "." + subproperty;
+ addNestedProperty(qualifiedPropertyId);
+ }
+
+ removeItemProperty(propertyId);
+ }
+
+ /**
+ * Adds a nested property to the item.
+ *
+ * @param nestedPropertyId
+ * property id to add. This property must not exist in the item
+ * already and must of of form "field1.field2" where field2 is a
+ * field in the object referenced to by field1
+ */
+ public void addNestedProperty(String nestedPropertyId) {
+ addItemProperty(nestedPropertyId, new NestedMethodProperty<Object>(
+ getBean(), nestedPropertyId));
+ }
+
+ /**
* Gets the underlying JavaBean object.
*
* @return the bean object.
diff --git a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
index e5972f697e..91950f5d4f 100644
--- a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
+++ b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java
@@ -641,7 +641,7 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical,
* Container Don't add a JavaDoc comment here, we use the default
* documentation from implemented interface.
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
return container.getContainerProperty(itemId, propertyId);
}
diff --git a/src/com/vaadin/data/util/ContainerOrderedWrapper.java b/src/com/vaadin/data/util/ContainerOrderedWrapper.java
index 1600699362..f333edecf4 100644
--- a/src/com/vaadin/data/util/ContainerOrderedWrapper.java
+++ b/src/com/vaadin/data/util/ContainerOrderedWrapper.java
@@ -437,7 +437,7 @@ public class ContainerOrderedWrapper implements Container.Ordered,
* Container Don't add a JavaDoc comment here, we use the default
* documentation from implemented interface.
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
return container.getContainerProperty(itemId, propertyId);
}
diff --git a/src/com/vaadin/data/util/DefaultItemSorter.java b/src/com/vaadin/data/util/DefaultItemSorter.java
index 9b834f4a2e..47db5d7507 100644
--- a/src/com/vaadin/data/util/DefaultItemSorter.java
+++ b/src/com/vaadin/data/util/DefaultItemSorter.java
@@ -122,8 +122,8 @@ public class DefaultItemSorter implements ItemSorter {
Item item1, Item item2) {
// Get the properties to compare
- final Property property1 = item1.getItemProperty(propertyId);
- final Property property2 = item2.getItemProperty(propertyId);
+ final Property<?> property1 = item1.getItemProperty(propertyId);
+ final Property<?> property2 = item2.getItemProperty(propertyId);
// Get the values to compare
final Object value1 = (property1 == null) ? null : property1.getValue();
diff --git a/src/com/vaadin/data/util/FilesystemContainer.java b/src/com/vaadin/data/util/FilesystemContainer.java
index 8e9873334b..7100286712 100644
--- a/src/com/vaadin/data/util/FilesystemContainer.java
+++ b/src/com/vaadin/data/util/FilesystemContainer.java
@@ -459,7 +459,7 @@ public class FilesystemContainer implements Container.Hierarchical {
* the property's ID.
* @return the requested property's value, or <code>null</code>
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
if (!(itemId instanceof File)) {
return null;
@@ -609,7 +609,7 @@ public class FilesystemContainer implements Container.Hierarchical {
* Gets the specified property of this file. Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
- public Property getItemProperty(Object id) {
+ public Property<?> getItemProperty(Object id) {
return getContainerProperty(file, id);
}
diff --git a/src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java b/src/com/vaadin/data/util/HierarchicalContainerOrderedWrapper.java
index f826c59bf7..b7eac3e378 100644
--- a/src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java
+++ b/src/com/vaadin/data/util/HierarchicalContainerOrderedWrapper.java
@@ -1,18 +1,20 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.ui.treetable;
+package com.vaadin.data.util;
import java.util.Collection;
import com.vaadin.data.Container.Hierarchical;
-import com.vaadin.data.util.ContainerOrderedWrapper;
-@SuppressWarnings({ "serial", "unchecked" })
/**
- * Helper for TreeTable. Does the same thing as ContainerOrderedWrapper
- * to fit into table but retains Hierarchical feature.
+ * A wrapper class for adding external ordering to containers not implementing
+ * the {@link com.vaadin.data.Container.Ordered} interface while retaining
+ * {@link Hierarchical} features.
+ *
+ * @see ContainerOrderedWrapper
*/
+@SuppressWarnings({ "serial" })
public class HierarchicalContainerOrderedWrapper extends
ContainerOrderedWrapper implements Hierarchical {
diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java
index 9728c79864..1e0a2fae1a 100644
--- a/src/com/vaadin/data/util/IndexedContainer.java
+++ b/src/com/vaadin/data/util/IndexedContainer.java
@@ -5,7 +5,6 @@
package com.vaadin.data.util;
import java.io.Serializable;
-import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -76,7 +75,7 @@ public class IndexedContainer extends
/**
* Set of properties that are read-only.
*/
- private HashSet<Property> readOnlyProperties = new HashSet<Property>();
+ private HashSet<Property<?>> readOnlyProperties = new HashSet<Property<?>>();
/**
* List of all Property value change event listeners listening all the
@@ -150,7 +149,7 @@ public class IndexedContainer extends
* @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
* java.lang.Object)
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
if (!containsId(itemId)) {
return null;
}
@@ -425,7 +424,7 @@ public class IndexedContainer extends
* @VERSION@
* @since 3.0
*/
- public class ItemSetChangeEvent extends BaseItemSetChangeEvent {
+ public static class ItemSetChangeEvent extends BaseItemSetChangeEvent {
private final int addedItemIndex;
@@ -455,7 +454,7 @@ public class IndexedContainer extends
* @VERSION@
* @since 3.0
*/
- private class PropertyValueChangeEvent extends EventObject implements
+ private static class PropertyValueChangeEvent extends EventObject implements
Property.ValueChangeEvent, Serializable {
private PropertyValueChangeEvent(Property source) {
@@ -680,7 +679,7 @@ public class IndexedContainer extends
*
* @see com.vaadin.data.Item#getItemProperty(java.lang.Object)
*/
- public Property getItemProperty(Object id) {
+ public Property<?> getItemProperty(Object id) {
return new IndexedContainerProperty(itemId, id);
}
@@ -691,8 +690,8 @@ public class IndexedContainer extends
/**
* Gets the <code>String</code> representation of the contents of the
* Item. The format of the string is a space separated catenation of the
- * <code>String</code> representations of the Properties contained by
- * the Item.
+ * <code>String</code> representations of the values of the Properties
+ * contained by the Item.
*
* @return <code>String</code> representation of the Item contents
*/
@@ -702,7 +701,7 @@ public class IndexedContainer extends
for (final Iterator<?> i = propertyIds.iterator(); i.hasNext();) {
final Object propertyId = i.next();
- retValue += getItemProperty(propertyId).toString();
+ retValue += getItemProperty(propertyId).getValue();
if (i.hasNext()) {
retValue += " ";
}
@@ -786,7 +785,7 @@ public class IndexedContainer extends
* @VERSION@
* @since 3.0
*/
- private class IndexedContainerProperty implements Property,
+ private class IndexedContainerProperty implements Property<Object>,
Property.ValueChangeNotifier {
/**
@@ -865,8 +864,7 @@ public class IndexedContainer extends
*
* @see com.vaadin.data.Property#setValue(java.lang.Object)
*/
- public void setValue(Object newValue)
- throws Property.ReadOnlyException, Property.ConversionException {
+ public void setValue(Object newValue) throws Property.ReadOnlyException {
// Gets the Property set
final Map<Object, Object> propertySet = items.get(itemId);
@@ -877,22 +875,8 @@ public class IndexedContainer extends
} else if (getType().isAssignableFrom(newValue.getClass())) {
propertySet.put(propertyId, newValue);
} else {
- try {
-
- // Gets the string constructor
- final Constructor<?> constr = getType().getConstructor(
- new Class[] { String.class });
-
- // Creates new object from the string
- propertySet.put(propertyId, constr
- .newInstance(new Object[] { newValue.toString() }));
-
- } catch (final java.lang.Exception e) {
- throw new Property.ConversionException(
- "Conversion for value '" + newValue + "' of class "
- + newValue.getClass().getName() + " to "
- + getType().getName() + " failed", e);
- }
+ throw new IllegalArgumentException("Value is of invalid type, "
+ + getType().getName() + " expected");
}
// update the container filtering if this property is being filtered
@@ -910,14 +894,14 @@ public class IndexedContainer extends
*
* @return <code>String</code> representation of the value stored in the
* Property
+ * @deprecated use {@link #getValue()} instead and possibly toString on
+ * that
*/
+ @Deprecated
@Override
public String toString() {
- final Object value = getValue();
- if (value == null) {
- return null;
- }
- return value.toString();
+ throw new UnsupportedOperationException(
+ "Use Property.getValue() instead of IndexedContainerProperty.toString()");
}
/**
@@ -1038,7 +1022,7 @@ public class IndexedContainer extends
getPropertySetChangeListeners()) : null);
nc.propertyValueChangeListeners = propertyValueChangeListeners != null ? (LinkedList<Property.ValueChangeListener>) propertyValueChangeListeners
.clone() : null;
- nc.readOnlyProperties = readOnlyProperties != null ? (HashSet<Property>) readOnlyProperties
+ nc.readOnlyProperties = readOnlyProperties != null ? (HashSet<Property<?>>) readOnlyProperties
.clone() : null;
nc.singlePropertyValueChangeListeners = singlePropertyValueChangeListeners != null ? (Hashtable<Object, Map<Object, List<Property.ValueChangeListener>>>) singlePropertyValueChangeListeners
.clone() : null;
@@ -1097,4 +1081,4 @@ public class IndexedContainer extends
removeFilter(filter);
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/data/util/MethodProperty.java b/src/com/vaadin/data/util/MethodProperty.java
index ff258d3e0f..4fc5531320 100644
--- a/src/com/vaadin/data/util/MethodProperty.java
+++ b/src/com/vaadin/data/util/MethodProperty.java
@@ -5,7 +5,6 @@
package com.vaadin.data.util;
import java.io.IOException;
-import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
@@ -47,7 +46,7 @@ import com.vaadin.util.SerializerHelper;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class MethodProperty<T> extends AbstractProperty {
+public class MethodProperty<T> extends AbstractProperty<T> {
private static final Logger logger = Logger.getLogger(MethodProperty.class
.getName());
@@ -170,7 +169,7 @@ public class MethodProperty<T> extends AbstractProperty {
@SuppressWarnings("unchecked")
public MethodProperty(Object instance, String beanPropertyName) {
- final Class beanClass = instance.getClass();
+ final Class<?> beanClass = instance.getClass();
// Assure that the first letter is upper cased (it is a common
// mistake to write firstName, not FirstName).
@@ -349,7 +348,7 @@ public class MethodProperty<T> extends AbstractProperty {
}
// Tests the parameter types
- final Class[] c = m[i].getParameterTypes();
+ final Class<?>[] c = m[i].getParameterTypes();
if (c.length != getArgs.length) {
// not the right amount of parameters, try next method
@@ -398,7 +397,7 @@ public class MethodProperty<T> extends AbstractProperty {
}
// Checks parameter compatibility
- final Class[] c = m[i].getParameterTypes();
+ final Class<?>[] c = m[i].getParameterTypes();
if (c.length != setArgs.length) {
// not the right amount of parameters, try next method
@@ -474,7 +473,9 @@ public class MethodProperty<T> extends AbstractProperty {
* {@link #setValue(Object newValue)} is called.
*/
@SuppressWarnings("unchecked")
- public MethodProperty(Class type, Object instance, Method getMethod,
+ // cannot use "Class<? extends T>" because of automatic primitive type
+ // conversions
+ public MethodProperty(Class<?> type, Object instance, Method getMethod,
Method setMethod, Object[] getArgs, Object[] setArgs,
int setArgumentIndex) {
@@ -495,13 +496,13 @@ public class MethodProperty<T> extends AbstractProperty {
}
// Gets the return type from get method
- type = convertPrimitiveType(type);
+ Class<? extends T> convertedType = (Class<? extends T>) convertPrimitiveType(type);
this.getMethod = getMethod;
this.setMethod = setMethod;
setArguments(getArgs, setArgs, setArgumentIndex);
this.instance = instance;
- this.type = type;
+ this.type = convertedType;
}
/**
@@ -569,8 +570,7 @@ public class MethodProperty<T> extends AbstractProperty {
*
* @return type of the Property
*/
- @SuppressWarnings("unchecked")
- public final Class getType() {
+ public final Class<? extends T> getType() {
return type;
}
@@ -593,9 +593,9 @@ public class MethodProperty<T> extends AbstractProperty {
*
* @return the value of the Property
*/
- public Object getValue() {
+ public T getValue() {
try {
- return getMethod.invoke(instance, getArgs);
+ return (T) getMethod.invoke(instance, getArgs);
} catch (final Throwable e) {
throw new MethodException(this, e);
}
@@ -629,61 +629,33 @@ public class MethodProperty<T> extends AbstractProperty {
}
/**
- * Sets the value of the property. This method supports setting from
- * <code>String</code>s if either <code>String</code> is directly assignable
- * to property type, or the type class contains a string constructor.
+ * Sets the value of the property.
+ *
+ * Note that since Vaadin 7, no conversions are performed and the value must
+ * be of the correct type.
*
* @param newValue
* the New value of the property.
* @throws <code>Property.ReadOnlyException</code> if the object is in
* read-only mode.
- * @throws <code>Property.ConversionException</code> if
- * <code>newValue</code> can't be converted into the Property's
- * native type directly or through <code>String</code>.
* @see #invokeSetMethod(Object)
*/
@SuppressWarnings("unchecked")
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException {
+ public void setValue(Object newValue) throws Property.ReadOnlyException {
// Checks the mode
if (isReadOnly()) {
throw new Property.ReadOnlyException();
}
- Object value = convertValue(newValue, type);
-
- invokeSetMethod(value);
- fireValueChange();
- }
-
- /**
- * Convert a value to the given type, using a constructor of the type that
- * takes a single String parameter (toString() for the value) if necessary.
- *
- * @param value
- * to convert
- * @param type
- * type into which the value should be converted
- * @return converted value
- */
- static Object convertValue(Object value, Class<?> type) {
- if (null == value || type.isAssignableFrom(value.getClass())) {
- return value;
+ // Checks the type of the value
+ if (newValue != null && !type.isAssignableFrom(newValue.getClass())) {
+ throw new IllegalArgumentException(
+ "Invalid value type for ObjectProperty.");
}
- // convert using a string constructor
- try {
- // Gets the string constructor
- final Constructor constr = type
- .getConstructor(new Class[] { String.class });
-
- // Create a new object from the string
- return constr.newInstance(new Object[] { value.toString() });
-
- } catch (final java.lang.Exception e) {
- throw new Property.ConversionException(e);
- }
+ invokeSetMethod((T) newValue);
+ fireValueChange();
}
/**
@@ -692,7 +664,7 @@ public class MethodProperty<T> extends AbstractProperty {
*
* @param value
*/
- protected void invokeSetMethod(Object value) {
+ protected void invokeSetMethod(T value) {
try {
// Construct a temporary argument array only if needed
diff --git a/src/com/vaadin/data/util/MethodPropertyDescriptor.java b/src/com/vaadin/data/util/MethodPropertyDescriptor.java
index f0c879766b..10faa7a0f3 100644
--- a/src/com/vaadin/data/util/MethodPropertyDescriptor.java
+++ b/src/com/vaadin/data/util/MethodPropertyDescriptor.java
@@ -123,9 +123,9 @@ public class MethodPropertyDescriptor<BT> implements
return propertyType;
}
- public Property createProperty(Object bean) {
+ public Property<?> createProperty(Object bean) {
return new MethodProperty<Object>(propertyType, bean, readMethod,
writeMethod);
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/data/util/NestedMethodProperty.java b/src/com/vaadin/data/util/NestedMethodProperty.java
index 8f5a17af16..d7b0f44912 100644
--- a/src/com/vaadin/data/util/NestedMethodProperty.java
+++ b/src/com/vaadin/data/util/NestedMethodProperty.java
@@ -26,7 +26,7 @@ import com.vaadin.data.util.MethodProperty.MethodException;
*
* @since 6.6
*/
-public class NestedMethodProperty extends AbstractProperty {
+public class NestedMethodProperty<T> extends AbstractProperty<T> {
// needed for de-serialization
private String propertyName;
@@ -43,7 +43,7 @@ public class NestedMethodProperty extends AbstractProperty {
*/
private Object instance;
- private Class<?> type;
+ private Class<? extends T> type;
/* Special serialization to handle method references */
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
@@ -158,13 +158,14 @@ public class NestedMethodProperty extends AbstractProperty {
} catch (final NoSuchMethodException skipped) {
}
- this.type = MethodProperty.convertPrimitiveType(type);
+ this.type = (Class<? extends T>) MethodProperty
+ .convertPrimitiveType(type);
this.propertyName = propertyName;
this.getMethods = getMethods;
this.setMethod = setMethod;
}
- public Class<?> getType() {
+ public Class<? extends T> getType() {
return type;
}
@@ -179,42 +180,41 @@ public class NestedMethodProperty extends AbstractProperty {
*
* @return the value of the Property
*/
- public Object getValue() {
+ public T getValue() {
try {
Object object = instance;
for (Method m : getMethods) {
object = m.invoke(object);
}
- return object;
+ return (T) object;
} catch (final Throwable e) {
throw new MethodException(this, e);
}
}
/**
- * Sets the value of the property. This method supports setting from
- * <code>String</code>s if either <code>String</code> is directly assignable
- * to property type, or the type class contains a string constructor.
+ * Sets the value of the property. The new value must be assignable to the
+ * type of this property.
*
* @param newValue
* the New value of the property.
* @throws <code>Property.ReadOnlyException</code> if the object is in
* read-only mode.
- * @throws <code>Property.ConversionException</code> if
- * <code>newValue</code> can't be converted into the Property's
- * native type directly or through <code>String</code>.
* @see #invokeSetMethod(Object)
*/
- public void setValue(Object newValue) throws ReadOnlyException,
- ConversionException {
+ public void setValue(Object newValue) throws ReadOnlyException {
// Checks the mode
if (isReadOnly()) {
throw new Property.ReadOnlyException();
}
- Object value = MethodProperty.convertValue(newValue, type);
+ // Checks the type of the value
+ if (newValue != null && !type.isAssignableFrom(newValue.getClass())) {
+ throw new IllegalArgumentException(
+ "Invalid value type for NestedMethodProperty.");
+ }
- invokeSetMethod(value);
+ invokeSetMethod((T) newValue);
fireValueChange();
}
@@ -224,7 +224,7 @@ public class NestedMethodProperty extends AbstractProperty {
*
* @param value
*/
- protected void invokeSetMethod(Object value) {
+ protected void invokeSetMethod(T value) {
try {
Object object = instance;
for (int i = 0; i < getMethods.size() - 1; i++) {
diff --git a/src/com/vaadin/data/util/NestedPropertyDescriptor.java b/src/com/vaadin/data/util/NestedPropertyDescriptor.java
index abdb9e0cd3..6404f6361d 100644
--- a/src/com/vaadin/data/util/NestedPropertyDescriptor.java
+++ b/src/com/vaadin/data/util/NestedPropertyDescriptor.java
@@ -37,7 +37,8 @@ public class NestedPropertyDescriptor<BT> implements
public NestedPropertyDescriptor(String name, Class<BT> beanType)
throws IllegalArgumentException {
this.name = name;
- NestedMethodProperty property = new NestedMethodProperty(beanType, name);
+ NestedMethodProperty<?> property = new NestedMethodProperty<Object>(
+ beanType, name);
this.propertyType = property.getType();
}
@@ -49,8 +50,8 @@ public class NestedPropertyDescriptor<BT> implements
return propertyType;
}
- public Property createProperty(BT bean) {
- return new NestedMethodProperty(bean, name);
+ public Property<?> createProperty(BT bean) {
+ return new NestedMethodProperty<Object>(bean, name);
}
}
diff --git a/src/com/vaadin/data/util/ObjectProperty.java b/src/com/vaadin/data/util/ObjectProperty.java
index 4319ea7716..9c60b9146e 100644
--- a/src/com/vaadin/data/util/ObjectProperty.java
+++ b/src/com/vaadin/data/util/ObjectProperty.java
@@ -4,8 +4,6 @@
package com.vaadin.data.util;
-import java.lang.reflect.Constructor;
-
import com.vaadin.data.Property;
/**
@@ -19,7 +17,7 @@ import com.vaadin.data.Property;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class ObjectProperty<T> extends AbstractProperty {
+public class ObjectProperty<T> extends AbstractProperty<T> {
/**
* The value contained by the Property.
@@ -48,9 +46,8 @@ public class ObjectProperty<T> extends AbstractProperty {
/**
* Creates a new instance of ObjectProperty with the given value and type.
*
- * Any value of type Object is accepted because, if the type class contains
- * a string constructor, the toString of the value is used to create the new
- * value. See {@link #setValue(Object)}.
+ * Since Vaadin 7, only values of the correct type are accepted, and no
+ * automatic conversions are performed.
*
* @param value
* the Initial value of the Property.
@@ -58,7 +55,7 @@ public class ObjectProperty<T> extends AbstractProperty {
* the type of the value. The value must be assignable to given
* type.
*/
- public ObjectProperty(Object value, Class<T> type) {
+ public ObjectProperty(T value, Class<T> type) {
// Set the values
this.type = type;
@@ -69,7 +66,7 @@ public class ObjectProperty<T> extends AbstractProperty {
* Creates a new instance of ObjectProperty with the given value, type and
* read-only mode status.
*
- * Any value of type Object is accepted, see
+ * Since Vaadin 7, only the correct type of values is accepted, see
* {@link #ObjectProperty(Object, Class)}.
*
* @param value
@@ -80,7 +77,7 @@ public class ObjectProperty<T> extends AbstractProperty {
* @param readOnly
* Sets the read-only mode.
*/
- public ObjectProperty(Object value, Class<T> type, boolean readOnly) {
+ public ObjectProperty(T value, Class<T> type, boolean readOnly) {
this(value, type);
setReadOnly(readOnly);
}
@@ -108,49 +105,34 @@ public class ObjectProperty<T> extends AbstractProperty {
}
/**
- * Sets the value of the property. This method supports setting from
- * <code>String</code> if either <code>String</code> is directly assignable
- * to property type, or the type class contains a string constructor.
+ * Sets the value of the property.
+ *
+ * Note that since Vaadin 7, no conversions are performed and the value must
+ * be of the correct type.
*
* @param newValue
* the New value of the property.
* @throws <code>Property.ReadOnlyException</code> if the object is in
* read-only mode
- * @throws <code>Property.ConversionException</code> if the newValue can't
- * be converted into the Property's native type directly or through
- * <code>String</code>
*/
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException {
+ @SuppressWarnings("unchecked")
+ public void setValue(Object newValue) throws Property.ReadOnlyException {
// Checks the mode
if (isReadOnly()) {
throw new Property.ReadOnlyException();
}
- // Tries to assign the compatible value directly
- if (newValue == null || type.isAssignableFrom(newValue.getClass())) {
- @SuppressWarnings("unchecked")
- // the cast is safe after an isAssignableFrom check
- T value = (T) newValue;
- this.value = value;
- } else {
- try {
-
- // Gets the string constructor
- final Constructor<T> constr = getType().getConstructor(
- new Class[] { String.class });
-
- // Creates new object from the string
- value = constr
- .newInstance(new Object[] { newValue.toString() });
-
- } catch (final java.lang.Exception e) {
- throw new Property.ConversionException(e);
- }
+ // Checks the type of the value
+ if (newValue != null && !type.isAssignableFrom(newValue.getClass())) {
+ throw new IllegalArgumentException("Invalid value type "
+ + newValue.getClass().getName()
+ + " for ObjectProperty of type " + type.getName() + ".");
}
+ // the cast is safe after an isAssignableFrom check
+ this.value = (T) newValue;
+
fireValueChange();
}
-
}
diff --git a/src/com/vaadin/data/util/PropertyFormatter.java b/src/com/vaadin/data/util/PropertyFormatter.java
index 1491f9a25e..a63973535b 100644
--- a/src/com/vaadin/data/util/PropertyFormatter.java
+++ b/src/com/vaadin/data/util/PropertyFormatter.java
@@ -4,6 +4,7 @@
package com.vaadin.data.util;
import com.vaadin.data.Property;
+import com.vaadin.data.util.converter.Converter;
/**
* Formatting proxy for a {@link Property}.
@@ -29,16 +30,22 @@ import com.vaadin.data.Property;
* standard "1.0" notation with more zeroes.
* </p>
*
+ * @param T
+ * type of the underlying property (a PropertyFormatter is always a
+ * Property&lt;String&gt;)
+ *
+ * @deprecated Since 7.0 replaced by {@link Converter}
* @author Vaadin Ltd.
* @since 5.3.0
*/
@SuppressWarnings("serial")
-public abstract class PropertyFormatter extends AbstractProperty implements
- Property.Viewer, Property.ValueChangeListener,
+@Deprecated
+public abstract class PropertyFormatter<T> extends AbstractProperty<String>
+ implements Property.Viewer, Property.ValueChangeListener,
Property.ReadOnlyStatusChangeListener {
/** Datasource that stores the actual value. */
- Property dataSource;
+ Property<T> dataSource;
/**
* Construct a new {@code PropertyFormatter} that is not connected to any
@@ -57,7 +64,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
* @param propertyDataSource
* to connect this property to.
*/
- public PropertyFormatter(Property propertyDataSource) {
+ public PropertyFormatter(Property<T> propertyDataSource) {
setPropertyDataSource(propertyDataSource);
}
@@ -68,7 +75,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
* @return the current data source as a Property, or <code>null</code> if
* none defined.
*/
- public Property getPropertyDataSource() {
+ public Property<T> getPropertyDataSource() {
return dataSource;
}
@@ -99,7 +106,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
.removeListener(this);
}
readOnly = isReadOnly();
- prevValue = toString();
+ prevValue = getValue();
}
dataSource = newDataSource;
@@ -117,7 +124,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
if (isReadOnly() != readOnly) {
fireReadOnlyStatusChange();
}
- String newVal = toString();
+ String newVal = getValue();
if ((prevValue == null && newVal != null)
|| (prevValue != null && !prevValue.equals(newVal))) {
fireValueChange();
@@ -125,7 +132,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
}
/* Documented in the interface */
- public Class getType() {
+ public Class<String> getType() {
return String.class;
}
@@ -135,22 +142,8 @@ public abstract class PropertyFormatter extends AbstractProperty implements
* @return If the datasource returns null, this is null. Otherwise this is
* String given by format().
*/
- public Object getValue() {
- return toString();
- }
-
- /**
- * Get the formatted value.
- *
- * @return If the datasource returns null, this is null. Otherwise this is
- * String given by format().
- */
- @Override
- public String toString() {
- if (dataSource == null) {
- return null;
- }
- Object value = dataSource.getValue();
+ public String getValue() {
+ T value = dataSource == null ? null : dataSource.getValue();
if (value == null) {
return null;
}
@@ -173,7 +166,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
* datasource.
* @return
*/
- abstract public String format(Object value);
+ abstract public String format(T value);
/**
* Parse string and convert it to format compatible with datasource.
@@ -187,7 +180,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
* Any type of exception can be thrown to indicate that the
* conversion was not succesful.
*/
- abstract public Object parse(String formattedValue) throws Exception;
+ abstract public T parse(String formattedValue) throws Exception;
/**
* Sets the Property's read-only mode to the specified status.
@@ -202,8 +195,7 @@ public abstract class PropertyFormatter extends AbstractProperty implements
}
}
- public void setValue(Object newValue) throws ReadOnlyException,
- ConversionException {
+ public void setValue(Object newValue) throws ReadOnlyException {
if (dataSource == null) {
return;
}
@@ -215,13 +207,11 @@ public abstract class PropertyFormatter extends AbstractProperty implements
} else {
try {
dataSource.setValue(parse(newValue.toString()));
- if (!newValue.equals(toString())) {
+ if (!newValue.equals(getValue())) {
fireValueChange();
}
- } catch (ConversionException e) {
- throw e;
} catch (Exception e) {
- throw new ConversionException(e);
+ throw new IllegalArgumentException("Could not parse value", e);
}
}
}
diff --git a/src/com/vaadin/data/util/PropertysetItem.java b/src/com/vaadin/data/util/PropertysetItem.java
index 04a7c66257..3270fa31f9 100644
--- a/src/com/vaadin/data/util/PropertysetItem.java
+++ b/src/com/vaadin/data/util/PropertysetItem.java
@@ -34,7 +34,7 @@ public class PropertysetItem implements Item, Item.PropertySetChangeNotifier,
/**
* Mapping from property id to property.
*/
- private HashMap<Object, Property> map = new HashMap<Object, Property>();
+ private HashMap<Object, Property<?>> map = new HashMap<Object, Property<?>>();
/**
* List of all property ids to maintain the order.
@@ -57,7 +57,7 @@ public class PropertysetItem implements Item, Item.PropertySetChangeNotifier,
* the identifier of the Property to get.
* @return the Property with the given ID or <code>null</code>
*/
- public Property getItemProperty(Object id) {
+ public Property<?> getItemProperty(Object id) {
return map.get(id);
}
@@ -143,7 +143,7 @@ public class PropertysetItem implements Item, Item.PropertySetChangeNotifier,
for (final Iterator<?> i = getItemPropertyIds().iterator(); i.hasNext();) {
final Object propertyId = i.next();
- retValue += getItemProperty(propertyId).toString();
+ retValue += getItemProperty(propertyId).getValue();
if (i.hasNext()) {
retValue += " ";
}
@@ -163,7 +163,7 @@ public class PropertysetItem implements Item, Item.PropertySetChangeNotifier,
* @VERSION@
* @since 3.0
*/
- private class PropertySetChangeEvent extends EventObject implements
+ private static class PropertySetChangeEvent extends EventObject implements
Item.PropertySetChangeEvent {
private PropertySetChangeEvent(Item source) {
@@ -262,7 +262,7 @@ public class PropertysetItem implements Item, Item.PropertySetChangeNotifier,
npsi.list = list != null ? (LinkedList<Object>) list.clone() : null;
npsi.propertySetChangeListeners = propertySetChangeListeners != null ? (LinkedList<PropertySetChangeListener>) propertySetChangeListeners
.clone() : null;
- npsi.map = (HashMap<Object, Property>) map.clone();
+ npsi.map = (HashMap<Object, Property<?>>) map.clone();
return npsi;
}
diff --git a/src/com/vaadin/data/util/QueryContainer.java b/src/com/vaadin/data/util/QueryContainer.java
index 2281343c30..7fef63e7f1 100644
--- a/src/com/vaadin/data/util/QueryContainer.java
+++ b/src/com/vaadin/data/util/QueryContainer.java
@@ -136,7 +136,8 @@ public class QueryContainer implements Container, Container.Ordered,
for (int i = 1; i <= count; i++) {
final String columnName = metadata.getColumnName(i);
list.add(columnName);
- final Property p = getContainerProperty(new Integer(1), columnName);
+ final Property<?> p = getContainerProperty(new Integer(1),
+ columnName);
propertyTypes.put(columnName,
p == null ? Object.class : p.getType());
}
@@ -228,7 +229,7 @@ public class QueryContainer implements Container, Container.Ordered,
* otherwise.
*/
- public synchronized Property getContainerProperty(Object itemId,
+ public synchronized Property<?> getContainerProperty(Object itemId,
Object propertyId) {
if (!(itemId instanceof Integer && propertyId instanceof String)) {
return null;
@@ -531,7 +532,7 @@ public class QueryContainer implements Container, Container.Ordered,
* identifier of the Property to get
* @return the Property with the given ID or <code>null</code>
*/
- public Property getItemProperty(Object propertyId) {
+ public Property<?> getItemProperty(Object propertyId) {
return getContainerProperty(id, propertyId);
}
diff --git a/src/com/vaadin/data/util/TextFileProperty.java b/src/com/vaadin/data/util/TextFileProperty.java
index cfa8d4fabf..5ebba98062 100644
--- a/src/com/vaadin/data/util/TextFileProperty.java
+++ b/src/com/vaadin/data/util/TextFileProperty.java
@@ -26,7 +26,7 @@ import java.nio.charset.Charset;
*
*/
@SuppressWarnings("serial")
-public class TextFileProperty extends AbstractProperty {
+public class TextFileProperty extends AbstractProperty<String> {
private File file;
private Charset charset = null;
@@ -64,7 +64,7 @@ public class TextFileProperty extends AbstractProperty {
*
* @see com.vaadin.data.Property#getType()
*/
- public Class<?> getType() {
+ public Class<String> getType() {
return String.class;
}
@@ -73,7 +73,7 @@ public class TextFileProperty extends AbstractProperty {
*
* @see com.vaadin.data.Property#getValue()
*/
- public Object getValue() {
+ public String getValue() {
if (file == null) {
return null;
}
diff --git a/src/com/vaadin/data/util/TransactionalPropertyWrapper.java b/src/com/vaadin/data/util/TransactionalPropertyWrapper.java
new file mode 100644
index 0000000000..06ec0935c3
--- /dev/null
+++ b/src/com/vaadin/data/util/TransactionalPropertyWrapper.java
@@ -0,0 +1,107 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.util;
+
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeNotifier;
+
+/**
+ * Wrapper class that helps implement two-phase commit for a non-transactional
+ * property.
+ *
+ * When accessing the property through the wrapper, getting and setting the
+ * property value take place immediately. However, the wrapper keeps track of
+ * the old value of the property so that it can be set for the property in case
+ * of a roll-back. This can result in the underlying property value changing
+ * multiple times (first based on modifications made by the application, then
+ * back upon roll-back).
+ *
+ * Value change events on the {@link TransactionalPropertyWrapper} are only
+ * fired at the end of a successful transaction, whereas listeners attached to
+ * the underlying property may receive multiple value change events.
+ *
+ * @see com.vaadin.data.Property.Transactional
+ *
+ * @author Vaadin Ltd
+ * @version @version@
+ * @since 7.0
+ *
+ * @param <T>
+ */
+public class TransactionalPropertyWrapper<T> extends AbstractProperty<T>
+ implements ValueChangeNotifier, Property.Transactional<T> {
+
+ private Property<T> wrappedProperty;
+ private boolean inTransaction = false;
+ private boolean valueChangePending;
+ private T valueBeforeTransaction;
+
+ public TransactionalPropertyWrapper(Property<T> wrappedProperty) {
+ this.wrappedProperty = wrappedProperty;
+ if (wrappedProperty instanceof ValueChangeNotifier) {
+ ((ValueChangeNotifier) wrappedProperty)
+ .addListener(new ValueChangeListener() {
+
+ public void valueChange(ValueChangeEvent event) {
+ fireValueChange();
+ }
+ });
+ }
+ }
+
+ public Class getType() {
+ return wrappedProperty.getType();
+ }
+
+ public T getValue() {
+ return wrappedProperty.getValue();
+ }
+
+ public void setValue(Object newValue) throws ReadOnlyException {
+ // Causes a value change to be sent to this listener which in turn fires
+ // a new value change event for this property
+ wrappedProperty.setValue(newValue);
+ }
+
+ public void startTransaction() {
+ inTransaction = true;
+ valueBeforeTransaction = getValue();
+ }
+
+ public void commit() {
+ endTransaction();
+ }
+
+ public void rollback() {
+ try {
+ wrappedProperty.setValue(valueBeforeTransaction);
+ } finally {
+ valueChangePending = false;
+ endTransaction();
+ }
+ }
+
+ protected void endTransaction() {
+ inTransaction = false;
+ valueBeforeTransaction = null;
+ if (valueChangePending) {
+ fireValueChange();
+ }
+ }
+
+ @Override
+ protected void fireValueChange() {
+ if (inTransaction) {
+ valueChangePending = true;
+ } else {
+ super.fireValueChange();
+ }
+ }
+
+ public Property<T> getWrappedProperty() {
+ return wrappedProperty;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/VaadinPropertyDescriptor.java b/src/com/vaadin/data/util/VaadinPropertyDescriptor.java
index 2a28671881..ee1e525540 100644
--- a/src/com/vaadin/data/util/VaadinPropertyDescriptor.java
+++ b/src/com/vaadin/data/util/VaadinPropertyDescriptor.java
@@ -39,5 +39,5 @@ public interface VaadinPropertyDescriptor<BT> extends Serializable {
* @param bean
* @return
*/
- public Property createProperty(BT bean);
-} \ No newline at end of file
+ public Property<?> createProperty(BT bean);
+}
diff --git a/src/com/vaadin/data/util/converter/Converter.java b/src/com/vaadin/data/util/converter/Converter.java
new file mode 100644
index 0000000000..b8c15e8cdc
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/Converter.java
@@ -0,0 +1,159 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+/**
+ * Interface that implements conversion between a model and a presentation type.
+ * <p>
+ * Typically {@link #convertToPresentation(Object, Locale)} and
+ * {@link #convertToModel(Object, Locale)} should be symmetric so that chaining
+ * these together returns the original result for all input but this is not a
+ * requirement.
+ * </p>
+ * <p>
+ * Converters must not have any side effects (never update UI from inside a
+ * converter).
+ * </p>
+ * <p>
+ * All Converters must be stateless and thread safe.
+ * </p>
+ * <p>
+ * If conversion of a value fails, a {@link ConversionException} is thrown.
+ * </p>
+ *
+ * @param <MODEL>
+ * The model type. Must be compatible with what
+ * {@link #getModelType()} returns.
+ * @param <PRESENTATION>
+ * The presentation type. Must be compatible with what
+ * {@link #getPresentationType()} returns.
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public interface Converter<PRESENTATION, MODEL> extends Serializable {
+
+ /**
+ * Converts the given value from target type to source type.
+ * <p>
+ * A converter can optionally use locale to do the conversion.
+ * </p>
+ * A converter should in most cases be symmetric so chaining
+ * {@link #convertToPresentation(Object, Locale)} and
+ * {@link #convertToModel(Object, Locale)} should return the original value.
+ *
+ * @param value
+ * The value to convert, compatible with the target type. Can be
+ * null
+ * @param locale
+ * The locale to use for conversion. Can be null.
+ * @return The converted value compatible with the source type
+ * @throws ConversionException
+ * If the value could not be converted
+ */
+ public MODEL convertToModel(PRESENTATION value, Locale locale)
+ throws ConversionException;
+
+ /**
+ * Converts the given value from source type to target type.
+ * <p>
+ * A converter can optionally use locale to do the conversion.
+ * </p>
+ * A converter should in most cases be symmetric so chaining
+ * {@link #convertToPresentation(Object, Locale)} and
+ * {@link #convertToModel(Object, Locale)} should return the original value.
+ *
+ * @param value
+ * The value to convert, compatible with the target type. Can be
+ * null
+ * @param locale
+ * The locale to use for conversion. Can be null.
+ * @return The converted value compatible with the source type
+ * @throws ConversionException
+ * If the value could not be converted
+ */
+ public PRESENTATION convertToPresentation(MODEL value, Locale locale)
+ throws ConversionException;
+
+ /**
+ * The source type of the converter.
+ *
+ * Values of this type can be passed to
+ * {@link #convertToPresentation(Object, Locale)}.
+ *
+ * @return The source type
+ */
+ public Class<MODEL> getModelType();
+
+ /**
+ * The target type of the converter.
+ *
+ * Values of this type can be passed to
+ * {@link #convertToModel(Object, Locale)}.
+ *
+ * @return The target type
+ */
+ public Class<PRESENTATION> getPresentationType();
+
+ /**
+ * An exception that signals that the value passed to
+ * {@link Converter#convertToPresentation(Object, Locale)} or
+ * {@link Converter#convertToModel(Object, Locale)} could not be converted.
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+ public static class ConversionException extends RuntimeException {
+
+ /**
+ * Constructs a new <code>ConversionException</code> without a detail
+ * message.
+ */
+ public ConversionException() {
+ }
+
+ /**
+ * Constructs a new <code>ConversionException</code> with the specified
+ * detail message.
+ *
+ * @param msg
+ * the detail message
+ */
+ public ConversionException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs a new {@code ConversionException} with the specified
+ * cause.
+ *
+ * @param cause
+ * The cause of the the exception
+ */
+ public ConversionException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs a new <code>ConversionException</code> with the specified
+ * detail message and cause.
+ *
+ * @param message
+ * the detail message
+ * @param cause
+ * The cause of the the exception
+ */
+ public ConversionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/ConverterFactory.java b/src/com/vaadin/data/util/converter/ConverterFactory.java
new file mode 100644
index 0000000000..ed4ab41ac0
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/ConverterFactory.java
@@ -0,0 +1,23 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.io.Serializable;
+
+/**
+ * Factory interface for providing Converters based on a presentation type and a
+ * model type.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ *
+ */
+public interface ConverterFactory extends Serializable {
+ public <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL> createConverter(
+ Class<PRESENTATION> presentationType, Class<MODEL> modelType);
+
+}
diff --git a/src/com/vaadin/data/util/converter/DateToLongConverter.java b/src/com/vaadin/data/util/converter/DateToLongConverter.java
new file mode 100644
index 0000000000..537800f617
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/DateToLongConverter.java
@@ -0,0 +1,68 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link Long} to {@link Date} and back.
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class DateToLongConverter implements Converter<Date, Long> {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
+ public Long convertToModel(Date value, Locale locale) {
+ if (value == null) {
+ return null;
+ }
+
+ return value.getTime();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public Date convertToPresentation(Long value, Locale locale) {
+ if (value == null) {
+ return null;
+ }
+
+ return new Date(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
+ public Class<Long> getModelType() {
+ return Long.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ public Class<Date> getPresentationType() {
+ return Date.class;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
new file mode 100644
index 0000000000..3ad7b6a85b
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
@@ -0,0 +1,100 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.util.Date;
+import java.util.logging.Logger;
+
+import com.vaadin.Application;
+
+/**
+ * Default implementation of {@link ConverterFactory}. Provides converters for
+ * standard types like {@link String}, {@link Double} and {@link Date}. </p>
+ * <p>
+ * Custom converters can be provided by extending this class and using
+ * {@link Application#setConverterFactory(ConverterFactory)}.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class DefaultConverterFactory implements ConverterFactory {
+
+ private final static Logger log = Logger
+ .getLogger(DefaultConverterFactory.class.getName());
+
+ public <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL> createConverter(
+ Class<PRESENTATION> presentationType, Class<MODEL> modelType) {
+ Converter<PRESENTATION, MODEL> converter = findConverter(
+ presentationType, modelType);
+ if (converter != null) {
+ log.finest(getClass().getName() + " created a "
+ + converter.getClass());
+ return converter;
+ }
+
+ // Try to find a reverse converter
+ Converter<MODEL, PRESENTATION> reverseConverter = findConverter(
+ modelType, presentationType);
+ if (reverseConverter != null) {
+ log.finest(getClass().getName() + " created a reverse "
+ + reverseConverter.getClass());
+ return new ReverseConverter<PRESENTATION, MODEL>(reverseConverter);
+ }
+
+ log.finest(getClass().getName() + " could not find a converter for "
+ + presentationType.getName() + " to " + modelType.getName()
+ + " conversion");
+ return null;
+
+ }
+
+ protected <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL> findConverter(
+ Class<PRESENTATION> presentationType, Class<MODEL> modelType) {
+ if (presentationType == String.class) {
+ // TextField converters and more
+ Converter<PRESENTATION, MODEL> converter = (Converter<PRESENTATION, MODEL>) createStringConverter(modelType);
+ if (converter != null) {
+ return converter;
+ }
+ } else if (presentationType == Date.class) {
+ // DateField converters and more
+ Converter<PRESENTATION, MODEL> converter = (Converter<PRESENTATION, MODEL>) createDateConverter(modelType);
+ if (converter != null) {
+ return converter;
+ }
+ }
+
+ return null;
+
+ }
+
+ protected Converter<Date, ?> createDateConverter(Class<?> sourceType) {
+ if (Long.class.isAssignableFrom(sourceType)) {
+ return new DateToLongConverter();
+ } else {
+ return null;
+ }
+ }
+
+ protected Converter<String, ?> createStringConverter(Class<?> sourceType) {
+ if (Double.class.isAssignableFrom(sourceType)) {
+ return new StringToDoubleConverter();
+ } else if (Integer.class.isAssignableFrom(sourceType)) {
+ return new StringToIntegerConverter();
+ } else if (Boolean.class.isAssignableFrom(sourceType)) {
+ return new StringToBooleanConverter();
+ } else if (Number.class.isAssignableFrom(sourceType)) {
+ return new StringToNumberConverter();
+ } else if (Date.class.isAssignableFrom(sourceType)) {
+ return new StringToDateConverter();
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/ReverseConverter.java b/src/com/vaadin/data/util/converter/ReverseConverter.java
new file mode 100644
index 0000000000..1c561f29e8
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/ReverseConverter.java
@@ -0,0 +1,80 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.util.Locale;
+
+/**
+ * A converter that wraps another {@link Converter} and reverses source and
+ * target types.
+ *
+ * @param <MODEL>
+ * The source type
+ * @param <PRESENTATION>
+ * The target type
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class ReverseConverter<PRESENTATION, MODEL> implements
+ Converter<PRESENTATION, MODEL> {
+
+ private Converter<MODEL, PRESENTATION> realConverter;
+
+ /**
+ * Creates a converter from source to target based on a converter that
+ * converts from target to source.
+ *
+ * @param converter
+ * The converter to use in a reverse fashion
+ */
+ public ReverseConverter(Converter<MODEL, PRESENTATION> converter) {
+ this.realConverter = converter;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#convertToModel(java
+ * .lang.Object, java.util.Locale)
+ */
+ public MODEL convertToModel(PRESENTATION value, Locale locale)
+ throws com.vaadin.data.util.converter.Converter.ConversionException {
+ return realConverter.convertToPresentation(value, locale);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public PRESENTATION convertToPresentation(MODEL value, Locale locale)
+ throws com.vaadin.data.util.converter.Converter.ConversionException {
+ return realConverter.convertToModel(value, locale);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getSourceType()
+ */
+ public Class<MODEL> getModelType() {
+ return realConverter.getPresentationType();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getTargetType()
+ */
+ public Class<PRESENTATION> getPresentationType() {
+ return realConverter.getModelType();
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/StringToBooleanConverter.java b/src/com/vaadin/data/util/converter/StringToBooleanConverter.java
new file mode 100644
index 0000000000..96a3a3d071
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/StringToBooleanConverter.java
@@ -0,0 +1,104 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link String} to {@link Boolean} and back.
+ * The String representation is given by Boolean.toString().
+ * <p>
+ * Leading and trailing white spaces are ignored when converting from a String.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class StringToBooleanConverter implements Converter<String, Boolean> {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
+ public Boolean convertToModel(String value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ if (getTrueString().equals(value)) {
+ return true;
+ } else if (getFalseString().equals(value)) {
+ return false;
+ } else {
+ throw new ConversionException("Cannot convert " + value + " to "
+ + getModelType().getName());
+ }
+ }
+
+ /**
+ * Gets the string representation for true. Default is "true".
+ *
+ * @return the string representation for true
+ */
+ protected String getTrueString() {
+ return Boolean.TRUE.toString();
+ }
+
+ /**
+ * Gets the string representation for false. Default is "false".
+ *
+ * @return the string representation for false
+ */
+ protected String getFalseString() {
+ return Boolean.FALSE.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public String convertToPresentation(Boolean value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+ if (value) {
+ return getTrueString();
+ } else {
+ return getFalseString();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
+ public Class<Boolean> getModelType() {
+ return Boolean.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/StringToDateConverter.java b/src/com/vaadin/data/util/converter/StringToDateConverter.java
new file mode 100644
index 0000000000..6f3c2e47f6
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/StringToDateConverter.java
@@ -0,0 +1,108 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link Date} to {@link String} and back. Uses
+ * the given locale and {@link DateFormat} for formatting and parsing.
+ * <p>
+ * Leading and trailing white spaces are ignored when converting from a String.
+ * </p>
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class StringToDateConverter implements Converter<String, Date> {
+
+ /**
+ * Returns the format used by {@link #convertToPresentation(Date, Locale)}
+ * and {@link #convertToModel(String, Locale)}.
+ *
+ * @param locale
+ * The locale to use
+ * @return A DateFormat instance
+ */
+ protected DateFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ DateFormat f = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+ DateFormat.MEDIUM, locale);
+ f.setLenient(false);
+ return f;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
+ public Date convertToModel(String value, Locale locale)
+ throws com.vaadin.data.util.converter.Converter.ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ ParsePosition parsePosition = new ParsePosition(0);
+ Date parsedValue = getFormat(locale).parse(value, parsePosition);
+ if (parsePosition.getIndex() != value.length()) {
+ throw new ConversionException("Could not convert '" + value
+ + "' to " + getModelType().getName());
+ }
+
+ return parsedValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public String convertToPresentation(Date value, Locale locale)
+ throws com.vaadin.data.util.converter.Converter.ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ return getFormat(locale).format(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
+ public Class<Date> getModelType() {
+ return Date.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/StringToDoubleConverter.java b/src/com/vaadin/data/util/converter/StringToDoubleConverter.java
new file mode 100644
index 0000000000..60a38f4127
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/StringToDoubleConverter.java
@@ -0,0 +1,103 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link String} to {@link Double} and back.
+ * Uses the given locale and a {@link NumberFormat} instance for formatting and
+ * parsing.
+ * <p>
+ * Leading and trailing white spaces are ignored when converting from a String.
+ * </p>
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class StringToDoubleConverter implements Converter<String, Double> {
+
+ /**
+ * Returns the format used by {@link #convertToPresentation(Double, Locale)}
+ * and {@link #convertToModel(String, Locale)}.
+ *
+ * @param locale
+ * The locale to use
+ * @return A NumberFormat instance
+ */
+ protected NumberFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ return NumberFormat.getNumberInstance(locale);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
+ public Double convertToModel(String value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ ParsePosition parsePosition = new ParsePosition(0);
+ Number parsedValue = getFormat(locale).parse(value, parsePosition);
+ if (parsePosition.getIndex() != value.length()) {
+ throw new ConversionException("Could not convert '" + value
+ + "' to " + getModelType().getName());
+ }
+ return parsedValue.doubleValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public String convertToPresentation(Double value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ return getFormat(locale).format(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
+ public Class<Double> getModelType() {
+ return Double.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+}
diff --git a/src/com/vaadin/data/util/converter/StringToIntegerConverter.java b/src/com/vaadin/data/util/converter/StringToIntegerConverter.java
new file mode 100644
index 0000000000..e55feec3b6
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/StringToIntegerConverter.java
@@ -0,0 +1,84 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link String} to {@link Integer} and back.
+ * Uses the given locale and a {@link NumberFormat} instance for formatting and
+ * parsing.
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class StringToIntegerConverter implements Converter<String, Integer> {
+
+ /**
+ * Returns the format used by
+ * {@link #convertToPresentation(Integer, Locale)} and
+ * {@link #convertToModel(String, Locale)}.
+ *
+ * @param locale
+ * The locale to use
+ * @return A NumberFormat instance
+ */
+ protected NumberFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ return NumberFormat.getIntegerInstance(locale);
+ }
+
+ public Integer convertToModel(String value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ // Parse and detect errors. If the full string was not used, it is
+ // an error.
+ ParsePosition parsePosition = new ParsePosition(0);
+ Number parsedValue = getFormat(locale).parse(value, parsePosition);
+ if (parsePosition.getIndex() != value.length()) {
+ throw new ConversionException("Could not convert '" + value
+ + "' to " + getModelType().getName());
+ }
+
+ if (parsedValue == null) {
+ // Convert "" to null
+ return null;
+ }
+ return parsedValue.intValue();
+ }
+
+ public String convertToPresentation(Integer value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ return getFormat(locale).format(value);
+ }
+
+ public Class<Integer> getModelType() {
+ return Integer.class;
+ }
+
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/converter/StringToNumberConverter.java b/src/com/vaadin/data/util/converter/StringToNumberConverter.java
new file mode 100644
index 0000000000..d1816007e7
--- /dev/null
+++ b/src/com/vaadin/data/util/converter/StringToNumberConverter.java
@@ -0,0 +1,107 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.util.converter;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link Number} to {@link String} and back.
+ * Uses the given locale and {@link NumberFormat} for formatting and parsing.
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class StringToNumberConverter implements Converter<String, Number> {
+
+ /**
+ * Returns the format used by {@link #convertToPresentation(Number, Locale)}
+ * and {@link #convertToModel(String, Locale)}.
+ *
+ * @param locale
+ * The locale to use
+ * @return A NumberFormat instance
+ */
+ protected NumberFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ return NumberFormat.getNumberInstance(locale);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
+ public Number convertToModel(String value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ // Parse and detect errors. If the full string was not used, it is
+ // an error.
+ ParsePosition parsePosition = new ParsePosition(0);
+ Number parsedValue = getFormat(locale).parse(value, parsePosition);
+ if (parsePosition.getIndex() != value.length()) {
+ throw new ConversionException("Could not convert '" + value
+ + "' to " + getModelType().getName());
+ }
+
+ if (parsedValue == null) {
+ // Convert "" to null
+ return null;
+ }
+ return parsedValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ public String convertToPresentation(Number value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ return getFormat(locale).format(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
+ public Class<Number> getModelType() {
+ return Number.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+
+}
diff --git a/src/com/vaadin/data/util/filter/Compare.java b/src/com/vaadin/data/util/filter/Compare.java
index fe7d908c93..111d95f055 100644
--- a/src/com/vaadin/data/util/filter/Compare.java
+++ b/src/com/vaadin/data/util/filter/Compare.java
@@ -228,7 +228,7 @@ public abstract class Compare implements Filter {
}
public boolean passesFilter(Object itemId, Item item) {
- final Property p = item.getItemProperty(getPropertyId());
+ final Property<?> p = item.getItemProperty(getPropertyId());
if (null == p) {
return false;
}
diff --git a/src/com/vaadin/data/util/filter/IsNull.java b/src/com/vaadin/data/util/filter/IsNull.java
index 0ae00d2e09..aad71a7c80 100644
--- a/src/com/vaadin/data/util/filter/IsNull.java
+++ b/src/com/vaadin/data/util/filter/IsNull.java
@@ -35,7 +35,7 @@ public final class IsNull implements Filter {
public boolean passesFilter(Object itemId, Item item)
throws UnsupportedOperationException {
- final Property p = item.getItemProperty(getPropertyId());
+ final Property<?> p = item.getItemProperty(getPropertyId());
if (null == p) {
return false;
}
diff --git a/src/com/vaadin/data/util/filter/SimpleStringFilter.java b/src/com/vaadin/data/util/filter/SimpleStringFilter.java
index 243571582e..6203251045 100644
--- a/src/com/vaadin/data/util/filter/SimpleStringFilter.java
+++ b/src/com/vaadin/data/util/filter/SimpleStringFilter.java
@@ -40,12 +40,16 @@ public final class SimpleStringFilter implements Filter {
}
public boolean passesFilter(Object itemId, Item item) {
- final Property p = item.getItemProperty(propertyId);
- if (p == null || p.toString() == null) {
+ final Property<?> p = item.getItemProperty(propertyId);
+ if (p == null) {
return false;
}
- final String value = ignoreCase ? p.toString().toLowerCase() : p
- .toString();
+ Object propertyValue = p.getValue();
+ if (propertyValue == null) {
+ return false;
+ }
+ final String value = ignoreCase ? propertyValue.toString()
+ .toLowerCase() : propertyValue.toString();
if (onlyMatchPrefix) {
if (!value.startsWith(filterString)) {
return false;
diff --git a/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java b/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
index 85ff60d5d9..d84a164bfa 100644
--- a/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
+++ b/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
@@ -3,7 +3,6 @@
*/
package com.vaadin.data.util.sqlcontainer;
-import java.lang.reflect.Constructor;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@@ -69,8 +68,7 @@ final public class ColumnProperty implements Property {
return value;
}
- public void setValue(Object newValue) throws ReadOnlyException,
- ConversionException {
+ public void setValue(Object newValue) throws ReadOnlyException {
if (newValue == null && !nullable) {
throw new NotNullableException(
"Null values are not allowed for this property.");
@@ -109,19 +107,9 @@ final public class ColumnProperty implements Property {
}
}
- /*
- * If the type is not correct, try to generate it through a possibly
- * existing String constructor.
- */
if (!getType().isAssignableFrom(newValue.getClass())) {
- try {
- final Constructor<?> constr = getType().getConstructor(
- new Class[] { String.class });
- newValue = constr.newInstance(new Object[] { newValue
- .toString() });
- } catch (Exception e) {
- throw new ConversionException(e);
- }
+ throw new IllegalArgumentException(
+ "Illegal value type for ColumnProperty");
}
/*
@@ -168,13 +156,17 @@ final public class ColumnProperty implements Property {
return propertyId;
}
+ /**
+ * Returns the value of the Property in human readable textual format.
+ *
+ * @see java.lang.Object#toString()
+ * @deprecated get the string representation from the value
+ */
+ @Deprecated
@Override
public String toString() {
- Object val = getValue();
- if (val == null) {
- return null;
- }
- return val.toString();
+ throw new UnsupportedOperationException(
+ "Use ColumnProperty.getValue() instead of ColumnProperty.toString()");
}
public void setOwner(RowItem owner) {
diff --git a/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java b/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java
index 248b159aa9..adfd439ac8 100644
--- a/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java
+++ b/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java
@@ -3,6 +3,8 @@
*/
package com.vaadin.data.util.sqlcontainer;
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;
+
/**
* An OptimisticLockException is thrown when trying to update or delete a row
* that has been changed since last read from the database.
@@ -12,7 +14,7 @@ package com.vaadin.data.util.sqlcontainer;
* configuration. In order to turn on optimistic locking, you need to specify
* the version column in your TableQuery instance.
*
- * @see com.vaadin.addon.sqlcontainer.query.TableQuery#setVersionColumn(String)
+ * @see TableQuery#setVersionColumn(String)
*
* @author Jonatan Kronqvist / Vaadin Ltd
*/
diff --git a/src/com/vaadin/data/util/sqlcontainer/RowItem.java b/src/com/vaadin/data/util/sqlcontainer/RowItem.java
index 3bd73bc48f..adededb65c 100644
--- a/src/com/vaadin/data/util/sqlcontainer/RowItem.java
+++ b/src/com/vaadin/data/util/sqlcontainer/RowItem.java
@@ -48,7 +48,7 @@ public final class RowItem implements Item {
this.id = id;
}
- public Property getItemProperty(Object id) {
+ public Property<?> getItemProperty(Object id) {
if (id instanceof String && id != null) {
for (ColumnProperty cp : properties) {
if (id.equals(cp.getPropertyId())) {
@@ -113,7 +113,8 @@ public final class RowItem implements Item {
s.append("|");
s.append(propId.toString());
s.append(":");
- s.append(getItemProperty(propId).toString());
+ Object value = getItemProperty(propId).getValue();
+ s.append((null != value) ? value.toString() : null);
}
return s.toString();
}
diff --git a/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
index 7eb67437e0..3bf33defd5 100644
--- a/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
+++ b/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
@@ -227,7 +227,7 @@ public class SQLContainer implements Container, Container.Filterable,
* @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
* java.lang.Object)
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
Item item = getItem(itemId);
if (item == null) {
return null;
@@ -1435,7 +1435,7 @@ public class SQLContainer implements Container, Container.Filterable,
* Simple ItemSetChangeEvent implementation.
*/
@SuppressWarnings("serial")
- public class ItemSetChangeEvent extends EventObject implements
+ public static class ItemSetChangeEvent extends EventObject implements
Container.ItemSetChangeEvent {
private ItemSetChangeEvent(SQLContainer source) {
@@ -1640,4 +1640,4 @@ public class SQLContainer implements Container, Container.Filterable,
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java b/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java
index 7dcab29611..56a8455a16 100644
--- a/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java
+++ b/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java
@@ -178,15 +178,14 @@ public class FreeformQuery implements QueryDelegate {
/**
* Fetches the results for the query. This implementation always fetches the
- * entire record set, ignoring the offset and pagelength parameters. In
+ * entire record set, ignoring the offset and page length parameters. In
* order to support lazy loading of records, you must supply a
* FreeformQueryDelegate that implements the
* FreeformQueryDelegate.getQueryString(int,int) method.
*
* @throws SQLException
*
- * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getQueryString(int,
- * int) {@inheritDoc}
+ * @see FreeformQueryDelegate#getQueryString(int, int)
*/
@SuppressWarnings("deprecation")
public ResultSet getResults(int offset, int pagelength) throws SQLException {
@@ -249,8 +248,8 @@ public class FreeformQuery implements QueryDelegate {
* (non-Javadoc)
*
* @see
- * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
- * .List)
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setFilters(java
+ * .util.List)
*/
public void setFilters(List<Filter> filters)
throws UnsupportedOperationException {
@@ -262,6 +261,13 @@ public class FreeformQuery implements QueryDelegate {
}
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setOrderBy(java
+ * .util.List)
+ */
public void setOrderBy(List<OrderBy> orderBys)
throws UnsupportedOperationException {
if (delegate != null) {
@@ -272,6 +278,13 @@ public class FreeformQuery implements QueryDelegate {
}
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin
+ * .data.util.sqlcontainer.RowItem)
+ */
public int storeRow(RowItem row) throws SQLException {
if (activeConnection == null) {
throw new IllegalStateException("No transaction is active!");
@@ -287,6 +300,13 @@ public class FreeformQuery implements QueryDelegate {
}
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin
+ * .data.util.sqlcontainer.RowItem)
+ */
public boolean removeRow(RowItem row) throws SQLException {
if (activeConnection == null) {
throw new IllegalStateException("No transaction is active!");
@@ -302,6 +322,12 @@ public class FreeformQuery implements QueryDelegate {
}
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#beginTransaction()
+ */
public synchronized void beginTransaction()
throws UnsupportedOperationException, SQLException {
if (activeConnection != null) {
@@ -311,6 +337,11 @@ public class FreeformQuery implements QueryDelegate {
activeConnection.setAutoCommit(false);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#commit()
+ */
public synchronized void commit() throws UnsupportedOperationException,
SQLException {
if (activeConnection == null) {
@@ -323,6 +354,11 @@ public class FreeformQuery implements QueryDelegate {
activeConnection = null;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#rollback()
+ */
public synchronized void rollback() throws UnsupportedOperationException,
SQLException {
if (activeConnection == null) {
@@ -333,6 +369,13 @@ public class FreeformQuery implements QueryDelegate {
activeConnection = null;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns
+ * ()
+ */
public List<String> getPrimaryKeyColumns() {
return primaryKeyColumns;
}
@@ -357,9 +400,8 @@ public class FreeformQuery implements QueryDelegate {
* getContainsRowQueryString method in FreeformQueryDelegate and this will
* be used instead of the logic.
*
- * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getContainsRowQueryString(Object...)
+ * @see FreeformQueryDelegate#getContainsRowQueryString(Object...)
*
- * {@inheritDoc}
*/
@SuppressWarnings("deprecation")
public boolean containsRowWithKey(Object... keys) throws SQLException {
diff --git a/src/com/vaadin/data/validator/AbstractStringValidator.java b/src/com/vaadin/data/validator/AbstractStringValidator.java
index 3bc66b3a73..5267cc7b7b 100644
--- a/src/com/vaadin/data/validator/AbstractStringValidator.java
+++ b/src/com/vaadin/data/validator/AbstractStringValidator.java
@@ -4,9 +4,7 @@
package com.vaadin.data.validator;
/**
- * Validator base class for validating strings. See
- * {@link com.vaadin.data.validator.AbstractValidator} for more information.
- *
+ * Validator base class for validating strings.
* <p>
* To include the value that failed validation in the exception message you can
* use "{0}" in the error message. This will be replaced with the failed value
@@ -15,12 +13,11 @@ package com.vaadin.data.validator;
* </p>
*
* @author Vaadin Ltd.
- * @version
- * @VERSION@
+ * @version @VERSION@
* @since 5.4
*/
@SuppressWarnings("serial")
-public abstract class AbstractStringValidator extends AbstractValidator {
+public abstract class AbstractStringValidator extends AbstractValidator<String> {
/**
* Constructs a validator for strings.
@@ -38,35 +35,8 @@ public abstract class AbstractStringValidator extends AbstractValidator {
super(errorMessage);
}
- /**
- * Tests if the given value is a valid string.
- * <p>
- * Null values are always accepted. Values that are not {@link String}s are
- * converted using {@link #toString()}. Then {@link #isValidString(String)}
- * is used to validate the value.
- * </p>
- *
- * @param value
- * the value to check
- * @return true if the value (or its toString()) is a valid string, false
- * otherwise
- */
- public boolean isValid(Object value) {
- if (value == null) {
- return true;
- }
- if (!(value instanceof String)) {
- value = String.valueOf(value);
- }
- return isValidString((String) value);
+ @Override
+ public Class<String> getType() {
+ return String.class;
}
-
- /**
- * Checks if the given string is valid.
- *
- * @param value
- * String to check. Can never be null.
- * @return true if the string is valid, false otherwise
- */
- protected abstract boolean isValidString(String value);
}
diff --git a/src/com/vaadin/data/validator/AbstractValidator.java b/src/com/vaadin/data/validator/AbstractValidator.java
index 5c8dd9b31a..27eaaca485 100644
--- a/src/com/vaadin/data/validator/AbstractValidator.java
+++ b/src/com/vaadin/data/validator/AbstractValidator.java
@@ -7,8 +7,8 @@ import com.vaadin.data.Validator;
/**
* Abstract {@link com.vaadin.data.Validator Validator} implementation that
- * provides a basic Validator implementation except the {@link #isValid(Object)}
- * method. Sub-classes need to implement the {@link #isValid(Object)} method.
+ * provides a basic Validator implementation except the
+ * {@link #isValidValue(Object)} method.
* <p>
* To include the value that failed validation in the exception message you can
* use "{0}" in the error message. This will be replaced with the failed value
@@ -21,14 +21,20 @@ import com.vaadin.data.Validator;
* {@link InvalidValueException#getHtmlMessage()} and throw such exceptions from
* {@link #validate(Object)}.
* </p>
+ * <p>
+ * Since Vaadin 7, subclasses can either implement {@link #validate(Object)}
+ * directly or implement {@link #isValidValue(Object)} when migrating legacy
+ * applications. To check validity, {@link #validate(Object)} should be used.
+ * </p>
*
+ * @param <T>
+ * The type
* @author Vaadin Ltd.
* @version
* @VERSION@
* @since 5.4
*/
-@SuppressWarnings("serial")
-public abstract class AbstractValidator implements Validator {
+public abstract class AbstractValidator<T> implements Validator {
/**
* Error message that is included in an {@link InvalidValueException} if
@@ -47,14 +53,65 @@ public abstract class AbstractValidator implements Validator {
this.errorMessage = errorMessage;
}
+ /**
+ * Since Vaadin 7, subclasses of AbstractValidator should override
+ * {@link #isValidValue(Object)} or {@link #validate(Object)} instead of
+ * {@link #isValid(Object)}. {@link #validate(Object)} should normally be
+ * used to check values.
+ *
+ * @param value
+ * @return true if the value is valid
+ */
+ public boolean isValid(Object value) {
+ try {
+ validate(value);
+ return true;
+ } catch (InvalidValueException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Internally check the validity of a value. This method can be used to
+ * perform validation in subclasses if customization of the error message is
+ * not needed. Otherwise, subclasses should override
+ * {@link #validate(Object)} and the return value of this method is ignored.
+ *
+ * This method should not be called from outside the validator class itself.
+ *
+ * @param value
+ * @return
+ */
+ protected abstract boolean isValidValue(T value);
+
public void validate(Object value) throws InvalidValueException {
- if (!isValid(value)) {
- String message = getErrorMessage().replace("{0}", String.valueOf(value));
+ // isValidType ensures that value can safely be cast to TYPE
+ if (!isValidType(value) || !isValidValue((T) value)) {
+ String message = getErrorMessage().replace("{0}",
+ String.valueOf(value));
throw new InvalidValueException(message);
}
}
/**
+ * Checks the type of the value to validate to ensure it conforms with
+ * getType. Enables sub classes to handle the specific type instead of
+ * Object.
+ *
+ * @param value
+ * The value to check
+ * @return true if the value can safely be cast to the type specified by
+ * {@link #getType()}
+ */
+ protected boolean isValidType(Object value) {
+ if (value == null) {
+ return true;
+ }
+
+ return getType().isAssignableFrom(value.getClass());
+ }
+
+ /**
* Returns the message to be included in the exception in case the value
* does not validate.
*
@@ -76,4 +133,6 @@ public abstract class AbstractValidator implements Validator {
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
+
+ public abstract Class<T> getType();
}
diff --git a/src/com/vaadin/data/validator/BeanValidator.java b/src/com/vaadin/data/validator/BeanValidator.java
new file mode 100644
index 0000000000..817df85248
--- /dev/null
+++ b/src/com/vaadin/data/validator/BeanValidator.java
@@ -0,0 +1,173 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.data.validator;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator.Context;
+import javax.validation.Validation;
+import javax.validation.ValidatorFactory;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import com.vaadin.data.Validator;
+
+/**
+ * Vaadin {@link Validator} using the JSR-303 (javax.validation)
+ * annotation-based bean validation.
+ *
+ * The annotations of the fields of the beans are used to determine the
+ * validation to perform.
+ *
+ * Note that a JSR-303 implementation (e.g. Hibernate Validator or Apache Bean
+ * Validation - formerly agimatec validation) must be present on the project
+ * classpath when using bean validation.
+ *
+ * @since 7.0
+ *
+ * @author Petri Hakala
+ * @author Henri Sara
+ */
+public class BeanValidator implements Validator {
+
+ private static final long serialVersionUID = 1L;
+ private static ValidatorFactory factory;
+
+ private transient javax.validation.Validator javaxBeanValidator;
+ private String propertyName;
+ private Class<?> beanClass;
+ private Locale locale;
+
+ /**
+ * Simple implementation of a message interpolator context that returns
+ * fixed values.
+ */
+ protected static class SimpleContext implements Context, Serializable {
+
+ private final Object value;
+ private final ConstraintDescriptor<?> descriptor;
+
+ /**
+ * Create a simple immutable message interpolator context.
+ *
+ * @param value
+ * value being validated
+ * @param descriptor
+ * ConstraintDescriptor corresponding to the constraint being
+ * validated
+ */
+ public SimpleContext(Object value, ConstraintDescriptor<?> descriptor) {
+ this.value = value;
+ this.descriptor = descriptor;
+ }
+
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return descriptor;
+ }
+
+ public Object getValidatedValue() {
+ return value;
+ }
+
+ }
+
+ /**
+ * Creates a Vaadin {@link Validator} utilizing JSR-303 bean validation.
+ *
+ * @param beanClass
+ * bean class based on which the validation should be performed
+ * @param propertyName
+ * property to validate
+ */
+ public BeanValidator(Class<?> beanClass, String propertyName) {
+ this.beanClass = beanClass;
+ this.propertyName = propertyName;
+ locale = Locale.getDefault();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.Validator#validate(java.lang.Object)
+ */
+ public void validate(final Object value) throws InvalidValueException {
+ Set<?> violations = getJavaxBeanValidator().validateValue(beanClass,
+ propertyName, value);
+ if (violations.size() > 0) {
+ List<String> exceptions = new ArrayList<String>();
+ for (Object v : violations) {
+ final ConstraintViolation<?> violation = (ConstraintViolation<?>) v;
+ String msg = getJavaxBeanValidatorFactory()
+ .getMessageInterpolator().interpolate(
+ violation.getMessageTemplate(),
+ new SimpleContext(value, violation
+ .getConstraintDescriptor()), locale);
+ exceptions.add(msg);
+ }
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < exceptions.size(); i++) {
+ if (i != 0) {
+ b.append("<br/>");
+ }
+ b.append(exceptions.get(i));
+ }
+ throw new InvalidValueException(b.toString());
+ }
+ }
+
+ /**
+ * Sets the locale used for validation error messages.
+ *
+ * Revalidation is not automatically triggered by setting the locale.
+ *
+ * @param locale
+ */
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Gets the locale used for validation error messages.
+ *
+ * @return locale used for validation
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns the underlying JSR-303 bean validator factory used. A factory is
+ * created using {@link Validation} if necessary.
+ *
+ * @return {@link ValidatorFactory} to use
+ */
+ protected static ValidatorFactory getJavaxBeanValidatorFactory() {
+ if (factory == null) {
+ factory = Validation.buildDefaultValidatorFactory();
+ }
+
+ return factory;
+ }
+
+ /**
+ * Returns a shared Validator instance to use. An instance is created using
+ * the validator factory if necessary and thereafter reused by the
+ * {@link BeanValidator} instance.
+ *
+ * @return the JSR-303 {@link javax.validation.Validator} to use
+ */
+ protected javax.validation.Validator getJavaxBeanValidator() {
+ if (javaxBeanValidator == null) {
+ javaxBeanValidator = getJavaxBeanValidatorFactory().getValidator();
+ }
+
+ return javaxBeanValidator;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/data/validator/CompositeValidator.java b/src/com/vaadin/data/validator/CompositeValidator.java
index 6227a3a2d8..956d773032 100644
--- a/src/com/vaadin/data/validator/CompositeValidator.java
+++ b/src/com/vaadin/data/validator/CompositeValidator.java
@@ -19,37 +19,44 @@ import com.vaadin.data.Validator;
* <code>AND</code> and <code>OR</code>.
*
* @author Vaadin Ltd.
- * @version
- * @VERSION@
+ * @version @VERSION@
* @since 3.0
*/
@SuppressWarnings("serial")
-public class CompositeValidator extends AbstractValidator {
+public class CompositeValidator implements Validator {
- /**
- * The validators are combined with <code>AND</code> clause: validity of the
- * composite implies validity of the all validators it is composed of must
- * be valid.
- */
- public static final int MODE_AND = 0;
+ public enum CombinationMode {
+ /**
+ * The validators are combined with <code>AND</code> clause: validity of
+ * the composite implies validity of the all validators it is composed
+ * of must be valid.
+ */
+ AND,
+ /**
+ * The validators are combined with <code>OR</code> clause: validity of
+ * the composite implies that some of validators it is composed of must
+ * be valid.
+ */
+ OR;
+ }
/**
- * The validators are combined with <code>OR</code> clause: validity of the
- * composite implies that some of validators it is composed of must be
- * valid.
+ * @deprecated from 7.0, use {@link CombinationMode#AND} instead    
*/
- public static final int MODE_OR = 1;
-
+ @Deprecated
+ public static final CombinationMode MODE_AND = CombinationMode.AND;
/**
- * The validators are combined with and clause: validity of the composite
- * implies validity of the all validators it is composed of
+ * @deprecated from 7.0, use {@link CombinationMode#OR} instead    
*/
- public static final int MODE_DEFAULT = MODE_AND;
+ @Deprecated
+ public static final CombinationMode MODE_OR = CombinationMode.OR;
+
+ private String errorMessage;
/**
* Operation mode.
*/
- private int mode = MODE_DEFAULT;
+ private CombinationMode mode = CombinationMode.AND;
/**
* List of contained validators.
@@ -61,14 +68,17 @@ public class CompositeValidator extends AbstractValidator {
* message.
*/
public CompositeValidator() {
- super("");
+ this(CombinationMode.AND, "");
}
/**
* Constructs a composite validator in given mode.
+ *
+ * @param mode
+ * @param errorMessage
*/
- public CompositeValidator(int mode, String errorMessage) {
- super(errorMessage);
+ public CompositeValidator(CombinationMode mode, String errorMessage) {
+ setErrorMessage(errorMessage);
setMode(mode);
}
@@ -91,16 +101,15 @@ public class CompositeValidator extends AbstractValidator {
* @throws Validator.InvalidValueException
* if the value is not valid.
*/
- @Override
public void validate(Object value) throws Validator.InvalidValueException {
switch (mode) {
- case MODE_AND:
+ case AND:
for (Validator validator : validators) {
validator.validate(value);
}
return;
- case MODE_OR:
+ case OR:
Validator.InvalidValueException first = null;
for (Validator v : validators) {
try {
@@ -122,65 +131,32 @@ public class CompositeValidator extends AbstractValidator {
throw first;
}
}
- throw new IllegalStateException(
- "The validator is in unsupported operation mode");
- }
-
- /**
- * Checks the validity of the the given value. The value is valid, if:
- * <ul>
- * <li><code>MODE_AND</code>: All of the sub-validators are valid
- * <li><code>MODE_OR</code>: Any of the sub-validators are valid
- * </ul>
- *
- * @param value
- * the value to check.
- */
- public boolean isValid(Object value) {
- switch (mode) {
- case MODE_AND:
- for (Validator v : validators) {
- if (!v.isValid(value)) {
- return false;
- }
- }
- return true;
-
- case MODE_OR:
- for (Validator v : validators) {
- if (v.isValid(value)) {
- return true;
- }
- }
- return false;
- }
- throw new IllegalStateException(
- "The valitor is in unsupported operation mode");
}
/**
* Gets the mode of the validator.
*
- * @return Operation mode of the validator: <code>MODE_AND</code> or
- * <code>MODE_OR</code>.
+ * @return Operation mode of the validator: {@link CombinationMode#AND} or
+ * {@link CombinationMode#OR}.
*/
- public final int getMode() {
+ public final CombinationMode getMode() {
return mode;
}
/**
* Sets the mode of the validator. The valid modes are:
* <ul>
- * <li><code>MODE_AND</code> (default)
- * <li><code>MODE_OR</code>
+ * <li>{@link CombinationMode#AND} (default)
+ * <li>{@link CombinationMode#OR}
* </ul>
*
* @param mode
* the mode to set.
*/
- public void setMode(int mode) {
- if (mode != MODE_AND && mode != MODE_OR) {
- throw new IllegalArgumentException("Mode " + mode + " unsupported");
+ public void setMode(CombinationMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException(
+ "The validator can't be set to null");
}
this.mode = mode;
}
@@ -189,10 +165,9 @@ public class CompositeValidator extends AbstractValidator {
* Gets the error message for the composite validator. If the error message
* is null, original error messages of the sub-validators are used instead.
*/
- @Override
public String getErrorMessage() {
- if (super.getErrorMessage() != null) {
- return super.getErrorMessage();
+ if (errorMessage != null) {
+ return errorMessage;
}
// TODO Return composite error message
@@ -240,11 +215,14 @@ public class CompositeValidator extends AbstractValidator {
* validators of given type null is returned.
* </p>
*
+ * @param validatorType
+ * The type of validators to return
+ *
* @return Collection<Validator> of validators compatible with given type
- * that must apply or null if none fould.
+ * that must apply or null if none found.
*/
public Collection<Validator> getSubValidators(Class validatorType) {
- if (mode != MODE_AND) {
+ if (mode != CombinationMode.AND) {
return null;
}
@@ -266,4 +244,15 @@ public class CompositeValidator extends AbstractValidator {
return found.isEmpty() ? null : found;
}
+ /**
+ * Sets the message to be included in the exception in case the value does
+ * not validate. The exception message is typically shown to the end user.
+ *
+ * @param errorMessage
+ * the error message.
+ */
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
}
diff --git a/src/com/vaadin/data/validator/DateRangeValidator.java b/src/com/vaadin/data/validator/DateRangeValidator.java
new file mode 100644
index 0000000000..24f3d3ce10
--- /dev/null
+++ b/src/com/vaadin/data/validator/DateRangeValidator.java
@@ -0,0 +1,51 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+import java.util.Date;
+
+import com.vaadin.ui.DateField.Resolution;
+
+/**
+ * Validator for validating that a Date is inside a given range.
+ *
+ * <p>
+ * Note that the comparison is done directly on the Date object so take care
+ * that the hours/minutes/seconds/milliseconds of the min/max values are
+ * properly set.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class DateRangeValidator extends RangeValidator<Date> {
+
+ /**
+ * Creates a validator for checking that an Date is within a given range.
+ * <p>
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ * </p>
+ * <p>
+ * Note that the comparison is done directly on the Date object so take care
+ * that the hours/minutes/seconds/milliseconds of the min/max values are
+ * properly set.
+ * </p>
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public DateRangeValidator(String errorMessage, Date minValue,
+ Date maxValue, Resolution resolution) {
+ super(errorMessage, Date.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/DoubleRangeValidator.java b/src/com/vaadin/data/validator/DoubleRangeValidator.java
new file mode 100644
index 0000000000..05ae2f827e
--- /dev/null
+++ b/src/com/vaadin/data/validator/DoubleRangeValidator.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+/**
+ * Validator for validating that a {@link Double} is inside a given range.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+@SuppressWarnings("serial")
+public class DoubleRangeValidator extends RangeValidator<Double> {
+
+ /**
+ * Creates a validator for checking that an Double is within a given range.
+ *
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ *
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public DoubleRangeValidator(String errorMessage, Double minValue,
+ Double maxValue) {
+ super(errorMessage, Double.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/DoubleValidator.java b/src/com/vaadin/data/validator/DoubleValidator.java
index e90919c17d..18f1909add 100644
--- a/src/com/vaadin/data/validator/DoubleValidator.java
+++ b/src/com/vaadin/data/validator/DoubleValidator.java
@@ -12,7 +12,9 @@ package com.vaadin.data.validator;
* @version
* @VERSION@
* @since 5.4
+ * @deprecated in Vaadin 7.0. Use an Double converter on the field instead.
*/
+@Deprecated
@SuppressWarnings("serial")
public class DoubleValidator extends AbstractStringValidator {
@@ -22,13 +24,17 @@ public class DoubleValidator extends AbstractStringValidator {
*
* @param errorMessage
* the message to display in case the value does not validate.
+ * @deprecated in Vaadin 7.0. Use a Double converter on the field instead
+ * and/or use a {@link DoubleRangeValidator} for validating that
+ * the value is inside a given range.
*/
+ @Deprecated
public DoubleValidator(String errorMessage) {
super(errorMessage);
}
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
try {
Double.parseDouble(value);
return true;
@@ -37,4 +43,16 @@ public class DoubleValidator extends AbstractStringValidator {
}
}
+ @Override
+ public void validate(Object value) throws InvalidValueException {
+ if (value != null && value instanceof Double) {
+ // Allow Doubles to pass through the validator for easier
+ // migration. Otherwise a TextField connected to an double property
+ // with a DoubleValidator will fail.
+ return;
+ }
+
+ super.validate(value);
+ }
+
}
diff --git a/src/com/vaadin/data/validator/IntegerRangeValidator.java b/src/com/vaadin/data/validator/IntegerRangeValidator.java
new file mode 100644
index 0000000000..c171dd97d8
--- /dev/null
+++ b/src/com/vaadin/data/validator/IntegerRangeValidator.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+/**
+ * Validator for validating that an {@link Integer} is inside a given range.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 5.4
+ */
+@SuppressWarnings("serial")
+public class IntegerRangeValidator extends RangeValidator<Integer> {
+
+ /**
+ * Creates a validator for checking that an Integer is within a given range.
+ *
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ *
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public IntegerRangeValidator(String errorMessage, Integer minValue,
+ Integer maxValue) {
+ super(errorMessage, Integer.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/IntegerValidator.java b/src/com/vaadin/data/validator/IntegerValidator.java
index 50b45b90ce..88ae9f3f0b 100644
--- a/src/com/vaadin/data/validator/IntegerValidator.java
+++ b/src/com/vaadin/data/validator/IntegerValidator.java
@@ -12,8 +12,10 @@ package com.vaadin.data.validator;
* @version
* @VERSION@
* @since 5.4
+ * @deprecated in Vaadin 7.0. Use an Integer converter on the field instead.
*/
@SuppressWarnings("serial")
+@Deprecated
public class IntegerValidator extends AbstractStringValidator {
/**
@@ -22,14 +24,18 @@ public class IntegerValidator extends AbstractStringValidator {
*
* @param errorMessage
* the message to display in case the value does not validate.
+ * @deprecated in Vaadin 7.0. Use an Integer converter on the field instead
+ * and/or use an {@link IntegerRangeValidator} for validating
+ * that the value is inside a given range.
*/
+ @Deprecated
public IntegerValidator(String errorMessage) {
super(errorMessage);
}
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
try {
Integer.parseInt(value);
return true;
@@ -38,4 +44,15 @@ public class IntegerValidator extends AbstractStringValidator {
}
}
+ @Override
+ public void validate(Object value) throws InvalidValueException {
+ if (value != null && value instanceof Integer) {
+ // Allow Integers to pass through the validator for easier
+ // migration. Otherwise a TextField connected to an integer property
+ // with an IntegerValidator will fail.
+ return;
+ }
+
+ super.validate(value);
+ }
}
diff --git a/src/com/vaadin/data/validator/NullValidator.java b/src/com/vaadin/data/validator/NullValidator.java
index 77f9a606b4..62b2580d48 100644
--- a/src/com/vaadin/data/validator/NullValidator.java
+++ b/src/com/vaadin/data/validator/NullValidator.java
@@ -51,17 +51,6 @@ public class NullValidator implements Validator {
}
/**
- * Tests if the given value is valid.
- *
- * @param value
- * the value to validate.
- * @returns <code>true</code> for valid value, otherwise <code>false</code>.
- */
- public boolean isValid(Object value) {
- return onlyNullAllowed ? value == null : value != null;
- }
-
- /**
* Returns <code>true</code> if nulls are allowed otherwise
* <code>false</code>.
*/
diff --git a/src/com/vaadin/data/validator/RangeValidator.java b/src/com/vaadin/data/validator/RangeValidator.java
new file mode 100644
index 0000000000..433271274f
--- /dev/null
+++ b/src/com/vaadin/data/validator/RangeValidator.java
@@ -0,0 +1,186 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+/**
+ * An base implementation for validating any objects that implement
+ * {@link Comparable}.
+ *
+ * Verifies that the value is of the given type and within the (optionally)
+ * given limits. Typically you want to use a sub class of this like
+ * {@link IntegerRangeValidator}, {@link DoubleRangeValidator} or
+ * {@link DateRangeValidator} in applications.
+ * <p>
+ * Note that {@link RangeValidator} always accept null values. Make a field
+ * required to ensure that no empty values are accepted or override
+ * {@link #isValidValue(Comparable)}.
+ * </p>
+ *
+ * @param <T>
+ * The type of Number to validate. Must implement Comparable so that
+ * minimum and maximum checks work.
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class RangeValidator<T extends Comparable> extends AbstractValidator<T> {
+
+ private T minValue = null;
+ private boolean minValueIncluded = true;
+ private T maxValue = null;
+ private boolean maxValueIncluded = true;
+ private Class<T> type;
+
+ /**
+ * Creates a new range validator of the given type.
+ *
+ * @param errorMessage
+ * The error message to use if validation fails
+ * @param type
+ * The type of object the validator can validate.
+ * @param minValue
+ * The minimum value that should be accepted or null for no limit
+ * @param maxValue
+ * The maximum value that should be accepted or null for no limit
+ */
+ public RangeValidator(String errorMessage, Class<T> type, T minValue,
+ T maxValue) {
+ super(errorMessage);
+ this.type = type;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ }
+
+ /**
+ * Checks if the minimum value is part of the accepted range
+ *
+ * @return true if the minimum value is part of the range, false otherwise
+ */
+ public boolean isMinValueIncluded() {
+ return minValueIncluded;
+ }
+
+ /**
+ * Sets if the minimum value is part of the accepted range
+ *
+ * @param minValueIncluded
+ * true if the minimum value should be part of the range, false
+ * otherwise
+ */
+ public void setMinValueIncluded(boolean minValueIncluded) {
+ this.minValueIncluded = minValueIncluded;
+ }
+
+ /**
+ * Checks if the maximum value is part of the accepted range
+ *
+ * @return true if the maximum value is part of the range, false otherwise
+ */
+ public boolean isMaxValueIncluded() {
+ return maxValueIncluded;
+ }
+
+ /**
+ * Sets if the maximum value is part of the accepted range
+ *
+ * @param maxValueIncluded
+ * true if the maximum value should be part of the range, false
+ * otherwise
+ */
+ public void setMaxValueIncluded(boolean maxValueIncluded) {
+ this.maxValueIncluded = maxValueIncluded;
+ }
+
+ /**
+ * Gets the minimum value of the range
+ *
+ * @return the minimum value
+ */
+ public T getMinValue() {
+ return minValue;
+ }
+
+ /**
+ * Sets the minimum value of the range. Use
+ * {@link #setMinValueIncluded(boolean)} to control whether this value is
+ * part of the range or not.
+ *
+ * @param minValue
+ * the minimum value
+ */
+ public void setMinValue(T minValue) {
+ this.minValue = minValue;
+ }
+
+ /**
+ * Gets the maximum value of the range
+ *
+ * @return the maximum value
+ */
+ public T getMaxValue() {
+ return maxValue;
+ }
+
+ /**
+ * Sets the maximum value of the range. Use
+ * {@link #setMaxValueIncluded(boolean)} to control whether this value is
+ * part of the range or not.
+ *
+ * @param maxValue
+ * the maximum value
+ */
+ public void setMaxValue(T maxValue) {
+ this.maxValue = maxValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object
+ * )
+ */
+ @Override
+ protected boolean isValidValue(T value) {
+ if (value == null) {
+ return true;
+ }
+
+ if (getMinValue() != null) {
+ // Ensure that the min limit is ok
+ int result = value.compareTo(getMinValue());
+ if (result < 0) {
+ // value less than min value
+ return false;
+ } else if (result == 0 && !isMinValueIncluded()) {
+ // values equal and min value not included
+ return false;
+ }
+ }
+ if (getMaxValue() != null) {
+ // Ensure that the Max limit is ok
+ int result = value.compareTo(getMaxValue());
+ if (result > 0) {
+ // value greater than max value
+ return false;
+ } else if (result == 0 && !isMaxValueIncluded()) {
+ // values equal and max value not included
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.validator.AbstractValidator#getType()
+ */
+ @Override
+ public Class<T> getType() {
+ return type;
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/RegexpValidator.java b/src/com/vaadin/data/validator/RegexpValidator.java
index 684de7697c..8143d54c97 100644
--- a/src/com/vaadin/data/validator/RegexpValidator.java
+++ b/src/com/vaadin/data/validator/RegexpValidator.java
@@ -62,8 +62,15 @@ public class RegexpValidator extends AbstractStringValidator {
this.complete = complete;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object
+ * )
+ */
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
if (complete) {
return getMatcher(value).matches();
} else {
diff --git a/src/com/vaadin/data/validator/StringLengthValidator.java b/src/com/vaadin/data/validator/StringLengthValidator.java
index d7edea216d..54b2d28f58 100644
--- a/src/com/vaadin/data/validator/StringLengthValidator.java
+++ b/src/com/vaadin/data/validator/StringLengthValidator.java
@@ -14,11 +14,11 @@ package com.vaadin.data.validator;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class StringLengthValidator extends AbstractValidator {
+public class StringLengthValidator extends AbstractStringValidator {
- private int minLength = -1;
+ private Integer minLength = null;
- private int maxLength = -1;
+ private Integer maxLength = null;
private boolean allowNull = true;
@@ -33,21 +33,25 @@ public class StringLengthValidator extends AbstractValidator {
}
/**
- * Creates a new StringLengthValidator with a given error message,
- * permissable lengths and null-string allowance.
+ * Creates a new StringLengthValidator with a given error message and
+ * minimum and maximum length limits.
*
* @param errorMessage
* the message to display in case the value does not validate.
* @param minLength
- * the minimum permissible length of the string.
+ * the minimum permissible length of the string or null for no
+ * limit. A negative value for no limit is also supported for
+ * backwards compatibility.
* @param maxLength
- * the maximum permissible length of the string.
+ * the maximum permissible length of the string or null for no
+ * limit. A negative value for no limit is also supported for
+ * backwards compatibility.
* @param allowNull
* Are null strings permissible? This can be handled better by
* setting a field as required or not.
*/
- public StringLengthValidator(String errorMessage, int minLength,
- int maxLength, boolean allowNull) {
+ public StringLengthValidator(String errorMessage, Integer minLength,
+ Integer maxLength, boolean allowNull) {
this(errorMessage);
setMinLength(minLength);
setMaxLength(maxLength);
@@ -61,17 +65,14 @@ public class StringLengthValidator extends AbstractValidator {
* the value to validate.
* @return <code>true</code> for valid value, otherwise <code>false</code>.
*/
- public boolean isValid(Object value) {
+ @Override
+ protected boolean isValidValue(String value) {
if (value == null) {
return allowNull;
}
- final String s = value.toString();
- if (s == null) {
- return allowNull;
- }
- final int len = s.length();
- if ((minLength >= 0 && len < minLength)
- || (maxLength >= 0 && len > maxLength)) {
+ final int len = value.length();
+ if ((minLength != null && minLength > -1 && len < minLength)
+ || (maxLength != null && maxLength > -1 && len > maxLength)) {
return false;
}
return true;
@@ -91,18 +92,18 @@ public class StringLengthValidator extends AbstractValidator {
/**
* Gets the maximum permissible length of the string.
*
- * @return the maximum length of the string.
+ * @return the maximum length of the string or null if there is no limit
*/
- public final int getMaxLength() {
+ public Integer getMaxLength() {
return maxLength;
}
/**
* Gets the minimum permissible length of the string.
*
- * @return the minimum length of the string.
+ * @return the minimum length of the string or null if there is no limit
*/
- public final int getMinLength() {
+ public Integer getMinLength() {
return minLength;
}
@@ -119,12 +120,9 @@ public class StringLengthValidator extends AbstractValidator {
* Sets the maximum permissible length of the string.
*
* @param maxLength
- * the length to set.
+ * the maximum length to accept or null for no limit
*/
- public void setMaxLength(int maxLength) {
- if (maxLength < -1) {
- maxLength = -1;
- }
+ public void setMaxLength(Integer maxLength) {
this.maxLength = maxLength;
}
@@ -132,12 +130,9 @@ public class StringLengthValidator extends AbstractValidator {
* Sets the minimum permissible length.
*
* @param minLength
- * the length to set.
+ * the minimum length to accept or null for no limit
*/
- public void setMinLength(int minLength) {
- if (minLength < -1) {
- minLength = -1;
- }
+ public void setMinLength(Integer minLength) {
this.minLength = minLength;
}
diff --git a/src/com/vaadin/event/ActionManager.java b/src/com/vaadin/event/ActionManager.java
index 13496c1deb..08e9c85043 100644
--- a/src/com/vaadin/event/ActionManager.java
+++ b/src/com/vaadin/event/ActionManager.java
@@ -11,6 +11,7 @@ import com.vaadin.event.Action.Handler;
import com.vaadin.terminal.KeyMapper;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.VariableOwner;
import com.vaadin.ui.Component;
/**
@@ -37,7 +38,7 @@ public class ActionManager implements Action.Container, Action.Handler,
protected HashSet<Handler> actionHandlers = null;
/** Action mapper */
- protected KeyMapper actionMapper = null;
+ protected KeyMapper<Action> actionMapper = null;
protected Component viewer;
@@ -47,7 +48,8 @@ public class ActionManager implements Action.Container, Action.Handler,
}
- public <T extends Component & Container> ActionManager(T viewer) {
+ public <T extends Component & Container & VariableOwner> ActionManager(
+ T viewer) {
this.viewer = viewer;
}
@@ -57,7 +59,8 @@ public class ActionManager implements Action.Container, Action.Handler,
}
}
- public <T extends Component & Container> void setViewer(T viewer) {
+ public <T extends Component & Container & VariableOwner> void setViewer(
+ T viewer) {
if (viewer == this.viewer) {
return;
}
@@ -151,9 +154,9 @@ public class ActionManager implements Action.Container, Action.Handler,
* removed but still exist on client side
*/
if (!actions.isEmpty() || clientHasActions) {
- actionMapper = new KeyMapper();
+ actionMapper = new KeyMapper<Action>();
- paintTarget.addVariable(viewer, "action", "");
+ paintTarget.addVariable((VariableOwner) viewer, "action", "");
paintTarget.startTag("actions");
for (final Action a : actions) {
@@ -195,7 +198,7 @@ public class ActionManager implements Action.Container, Action.Handler,
public void handleActions(Map<String, Object> variables, Container sender) {
if (variables.containsKey("action") && actionMapper != null) {
final String key = (String) variables.get("action");
- final Action action = (Action) actionMapper.get(key);
+ final Action action = actionMapper.get(key);
final Object target = variables.get("actiontarget");
if (action != null) {
handleAction(action, sender, target);
diff --git a/src/com/vaadin/event/FieldEvents.java b/src/com/vaadin/event/FieldEvents.java
index 28fd6bb4f7..20e9fabb36 100644
--- a/src/com/vaadin/event/FieldEvents.java
+++ b/src/com/vaadin/event/FieldEvents.java
@@ -8,8 +8,10 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import com.vaadin.terminal.gwt.client.EventId;
+import com.vaadin.terminal.gwt.client.communication.FieldRpc.FocusAndBlurServerRpc;
import com.vaadin.tools.ReflectTools;
import com.vaadin.ui.Component;
+import com.vaadin.ui.Component.Event;
import com.vaadin.ui.Field;
import com.vaadin.ui.Field.ValueChangeEvent;
import com.vaadin.ui.TextField;
@@ -247,4 +249,25 @@ public interface FieldEvents {
public void removeListener(TextChangeListener listener);
}
+
+ public static abstract class FocusAndBlurServerRpcImpl implements
+ FocusAndBlurServerRpc {
+
+ private Component component;
+
+ public FocusAndBlurServerRpcImpl(Component component) {
+ this.component = component;
+ }
+
+ protected abstract void fireEvent(Event event);
+
+ public void blur() {
+ fireEvent(new BlurEvent(component));
+ }
+
+ public void focus() {
+ fireEvent(new FocusEvent(component));
+ }
+ };
+
}
diff --git a/src/com/vaadin/event/ItemClickEvent.java b/src/com/vaadin/event/ItemClickEvent.java
index c503f8f9c9..bb41398e8d 100644
--- a/src/com/vaadin/event/ItemClickEvent.java
+++ b/src/com/vaadin/event/ItemClickEvent.java
@@ -6,7 +6,6 @@ package com.vaadin.event;
import java.io.Serializable;
import java.lang.reflect.Method;
-import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.event.MouseEvents.ClickEvent;
@@ -84,19 +83,6 @@ public class ItemClickEvent extends ClickEvent implements Serializable {
}
/**
- * Components implementing
- *
- * @link {@link Container} interface may support emitting
- * {@link ItemClickEvent}s.
- *
- * @deprecated Use {@link ItemClickNotifier} instead. ItemClickSource was
- * deprecated in version 6.5.
- */
- @Deprecated
- public interface ItemClickSource extends ItemClickNotifier {
- }
-
- /**
* The interface for adding and removing <code>ItemClickEvent</code>
* listeners. By implementing this interface a class explicitly announces
* that it will generate an <code>ItemClickEvent</code> when one of its
diff --git a/src/com/vaadin/event/LayoutEvents.java b/src/com/vaadin/event/LayoutEvents.java
index 2dda15fbf5..960fff00c0 100644
--- a/src/com/vaadin/event/LayoutEvents.java
+++ b/src/com/vaadin/event/LayoutEvents.java
@@ -7,9 +7,11 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import com.vaadin.event.MouseEvents.ClickEvent;
+import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
import com.vaadin.tools.ReflectTools;
import com.vaadin.ui.Component;
+import com.vaadin.ui.ComponentContainer;
public interface LayoutEvents {
@@ -120,5 +122,17 @@ public interface LayoutEvents {
return childComponent;
}
+ public static LayoutClickEvent createEvent(ComponentContainer layout,
+ MouseEventDetails mouseDetails, Connector clickedConnector) {
+ Component clickedComponent = (Component) clickedConnector;
+ Component childComponent = clickedComponent;
+ while (childComponent != null
+ && childComponent.getParent() != layout) {
+ childComponent = childComponent.getParent();
+ }
+
+ return new LayoutClickEvent(layout, mouseDetails, clickedComponent,
+ childComponent);
+ }
}
} \ No newline at end of file
diff --git a/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java b/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java
index 920112360e..8b64106be7 100644
--- a/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java
+++ b/src/com/vaadin/event/dd/acceptcriteria/ClientCriterion.java
@@ -9,13 +9,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterion;
-import com.vaadin.ui.ClientWidget;
/**
* An annotation type used to point the client side counterpart for server side
- * a {@link AcceptCriterion} class. Usage is pretty similar to
- * {@link ClientWidget} which is used with Vaadin components that have a
- * specialized client side counterpart.
+ * a {@link AcceptCriterion} class.
* <p>
* Annotations are used at GWT compilation phase, so remember to rebuild your
* widgetset if you do changes for {@link ClientCriterion} mappings.
diff --git a/src/com/vaadin/external/json/JSONArray.java b/src/com/vaadin/external/json/JSONArray.java
new file mode 100644
index 0000000000..2307749ffc
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONArray.java
@@ -0,0 +1,963 @@
+package com.vaadin.external.json;
+
+/*
+ Copyright (c) 2002 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A JSONArray is an ordered sequence of values. Its external text form is a
+ * string wrapped in square brackets with commas separating the values. The
+ * internal form is an object having <code>get</code> and <code>opt</code>
+ * methods for accessing the values by index, and <code>put</code> methods for
+ * adding or replacing values. The values can be any of these types:
+ * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
+ * <code>Number</code>, <code>String</code>, or the
+ * <code>JSONObject.NULL object</code>.
+ * <p>
+ * The constructor can convert a JSON text into a Java object. The
+ * <code>toString</code> method converts to JSON text.
+ * <p>
+ * A <code>get</code> method returns a value if one can be found, and throws an
+ * exception if one cannot be found. An <code>opt</code> method returns a
+ * default value instead of throwing an exception, and so is useful for
+ * obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * JSON syntax rules. The constructors are more forgiving in the texts they will
+ * accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ * before the closing bracket.</li>
+ * <li>The <code>null</code> value will be inserted when there is <code>,</code>
+ * &nbsp;<small>(comma)</small> elision.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ * quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ * or single quote, and if they do not contain leading or trailing spaces, and
+ * if they do not contain any of these characters:
+ * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
+ * if they are not the reserved words <code>true</code>, <code>false</code>, or
+ * <code>null</code>.</li>
+ * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
+ * well as by <code>,</code> <small>(comma)</small>.</li>
+ * <li>Numbers may have the <code>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+ *
+ * @author JSON.org
+ * @version 2011-08-25
+ */
+public class JSONArray implements Serializable {
+
+ /**
+ * The arrayList where the JSONArray's properties are kept.
+ */
+ private ArrayList myArrayList;
+
+ /**
+ * Construct an empty JSONArray.
+ */
+ public JSONArray() {
+ myArrayList = new ArrayList();
+ }
+
+ /**
+ * Construct a JSONArray from a JSONTokener.
+ *
+ * @param x
+ * A JSONTokener
+ * @throws JSONException
+ * If there is a syntax error.
+ */
+ public JSONArray(JSONTokener x) throws JSONException {
+ this();
+ if (x.nextClean() != '[') {
+ throw x.syntaxError("A JSONArray text must start with '['");
+ }
+ if (x.nextClean() != ']') {
+ x.back();
+ for (;;) {
+ if (x.nextClean() == ',') {
+ x.back();
+ myArrayList.add(JSONObject.NULL);
+ } else {
+ x.back();
+ myArrayList.add(x.nextValue());
+ }
+ switch (x.nextClean()) {
+ case ';':
+ case ',':
+ if (x.nextClean() == ']') {
+ return;
+ }
+ x.back();
+ break;
+ case ']':
+ return;
+ default:
+ throw x.syntaxError("Expected a ',' or ']'");
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONArray from a source JSON text.
+ *
+ * @param source
+ * A string that begins with <code>[</code>&nbsp;<small>(left
+ * bracket)</small> and ends with <code>]</code>
+ * &nbsp;<small>(right bracket)</small>.
+ * @throws JSONException
+ * If there is a syntax error.
+ */
+ public JSONArray(String source) throws JSONException {
+ this(new JSONTokener(source));
+ }
+
+ /**
+ * Construct a JSONArray from a Collection.
+ *
+ * @param collection
+ * A Collection.
+ */
+ public JSONArray(Collection collection) {
+ myArrayList = new ArrayList();
+ if (collection != null) {
+ Iterator iter = collection.iterator();
+ while (iter.hasNext()) {
+ myArrayList.add(JSONObject.wrap(iter.next()));
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONArray from an array
+ *
+ * @throws JSONException
+ * If not an array.
+ */
+ public JSONArray(Object array) throws JSONException {
+ this();
+ if (array.getClass().isArray()) {
+ int length = Array.getLength(array);
+ for (int i = 0; i < length; i += 1) {
+ this.put(JSONObject.wrap(Array.get(array, i)));
+ }
+ } else {
+ throw new JSONException(
+ "JSONArray initial value should be a string or collection or array.");
+ }
+ }
+
+ /**
+ * Get the object value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return An object value.
+ * @throws JSONException
+ * If there is no value for the index.
+ */
+ public Object get(int index) throws JSONException {
+ Object object = opt(index);
+ if (object == null) {
+ throw new JSONException("JSONArray[" + index + "] not found.");
+ }
+ return object;
+ }
+
+ /**
+ * Get the boolean value associated with an index. The string values "true"
+ * and "false" are converted to boolean.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The truth.
+ * @throws JSONException
+ * If there is no value for the index or if the value is not
+ * convertible to boolean.
+ */
+ public boolean getBoolean(int index) throws JSONException {
+ Object object = get(index);
+ if (object.equals(Boolean.FALSE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("false"))) {
+ return false;
+ } else if (object.equals(Boolean.TRUE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("true"))) {
+ return true;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a boolean.");
+ }
+
+ /**
+ * Get the double value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value cannot be converted
+ * to a number.
+ */
+ public double getDouble(int index) throws JSONException {
+ Object object = get(index);
+ try {
+ return object instanceof Number ? ((Number) object).doubleValue()
+ : Double.parseDouble((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the int value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value is not a number.
+ */
+ public int getInt(int index) throws JSONException {
+ Object object = get(index);
+ try {
+ return object instanceof Number ? ((Number) object).intValue()
+ : Integer.parseInt((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the JSONArray associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A JSONArray value.
+ * @throws JSONException
+ * If there is no value for the index. or if the value is not a
+ * JSONArray
+ */
+ public JSONArray getJSONArray(int index) throws JSONException {
+ Object object = get(index);
+ if (object instanceof JSONArray) {
+ return (JSONArray) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
+ }
+
+ /**
+ * Get the JSONObject associated with an index.
+ *
+ * @param index
+ * subscript
+ * @return A JSONObject value.
+ * @throws JSONException
+ * If there is no value for the index or if the value is not a
+ * JSONObject
+ */
+ public JSONObject getJSONObject(int index) throws JSONException {
+ Object object = get(index);
+ if (object instanceof JSONObject) {
+ return (JSONObject) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
+ }
+
+ /**
+ * Get the long value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value cannot be converted
+ * to a number.
+ */
+ public long getLong(int index) throws JSONException {
+ Object object = get(index);
+ try {
+ return object instanceof Number ? ((Number) object).longValue()
+ : Long.parseLong((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the string associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A string value.
+ * @throws JSONException
+ * If there is no string value for the index.
+ */
+ public String getString(int index) throws JSONException {
+ Object object = get(index);
+ if (object instanceof String) {
+ return (String) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] not a string.");
+ }
+
+ /**
+ * Determine if the value is null.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return true if the value at the index is null, or if there is no value.
+ */
+ public boolean isNull(int index) {
+ return JSONObject.NULL.equals(opt(index));
+ }
+
+ /**
+ * Make a string from the contents of this JSONArray. The
+ * <code>separator</code> string is inserted between each element. Warning:
+ * This method assumes that the data structure is acyclical.
+ *
+ * @param separator
+ * A string that will be inserted between the elements.
+ * @return a string.
+ * @throws JSONException
+ * If the array contains an invalid number.
+ */
+ public String join(String separator) throws JSONException {
+ int len = length();
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < len; i += 1) {
+ if (i > 0) {
+ sb.append(separator);
+ }
+ sb.append(JSONObject.valueToString(myArrayList.get(i)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get the number of elements in the JSONArray, included nulls.
+ *
+ * @return The length (or size).
+ */
+ public int length() {
+ return myArrayList.size();
+ }
+
+ /**
+ * Get the optional object value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return An object value, or null if there is no object at that index.
+ */
+ public Object opt(int index) {
+ return (index < 0 || index >= length()) ? null : myArrayList.get(index);
+ }
+
+ /**
+ * Get the optional boolean value associated with an index. It returns false
+ * if there is no value at that index, or if the value is not Boolean.TRUE
+ * or the String "true".
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The truth.
+ */
+ public boolean optBoolean(int index) {
+ return optBoolean(index, false);
+ }
+
+ /**
+ * Get the optional boolean value associated with an index. It returns the
+ * defaultValue if there is no value at that index or if it is not a Boolean
+ * or the String "true" or "false" (case insensitive).
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * A boolean default.
+ * @return The truth.
+ */
+ public boolean optBoolean(int index, boolean defaultValue) {
+ try {
+ return getBoolean(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional double value associated with an index. NaN is returned
+ * if there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public double optDouble(int index) {
+ return optDouble(index, Double.NaN);
+ }
+
+ /**
+ * Get the optional double value associated with an index. The defaultValue
+ * is returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * subscript
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public double optDouble(int index, double defaultValue) {
+ try {
+ return getDouble(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional int value associated with an index. Zero is returned if
+ * there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public int optInt(int index) {
+ return optInt(index, 0);
+ }
+
+ /**
+ * Get the optional int value associated with an index. The defaultValue is
+ * returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public int optInt(int index, int defaultValue) {
+ try {
+ return getInt(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional JSONArray associated with an index.
+ *
+ * @param index
+ * subscript
+ * @return A JSONArray value, or null if the index has no value, or if the
+ * value is not a JSONArray.
+ */
+ public JSONArray optJSONArray(int index) {
+ Object o = opt(index);
+ return o instanceof JSONArray ? (JSONArray) o : null;
+ }
+
+ /**
+ * Get the optional JSONObject associated with an index. Null is returned if
+ * the key is not found, or null if the index has no value, or if the value
+ * is not a JSONObject.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A JSONObject value.
+ */
+ public JSONObject optJSONObject(int index) {
+ Object o = opt(index);
+ return o instanceof JSONObject ? (JSONObject) o : null;
+ }
+
+ /**
+ * Get the optional long value associated with an index. Zero is returned if
+ * there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public long optLong(int index) {
+ return optLong(index, 0);
+ }
+
+ /**
+ * Get the optional long value associated with an index. The defaultValue is
+ * returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public long optLong(int index, long defaultValue) {
+ try {
+ return getLong(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional string value associated with an index. It returns an
+ * empty string if there is no value at that index. If the value is not a
+ * string and is not null, then it is coverted to a string.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A String value.
+ */
+ public String optString(int index) {
+ return optString(index, "");
+ }
+
+ /**
+ * Get the optional string associated with an index. The defaultValue is
+ * returned if the key is not found.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return A String value.
+ */
+ public String optString(int index, String defaultValue) {
+ Object object = opt(index);
+ return JSONObject.NULL.equals(object) ? object.toString()
+ : defaultValue;
+ }
+
+ /**
+ * Append a boolean value. This increases the array's length by one.
+ *
+ * @param value
+ * A boolean value.
+ * @return this.
+ */
+ public JSONArray put(boolean value) {
+ put(value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONArray which
+ * is produced from a Collection.
+ *
+ * @param value
+ * A Collection value.
+ * @return this.
+ */
+ public JSONArray put(Collection value) {
+ put(new JSONArray(value));
+ return this;
+ }
+
+ /**
+ * Append a double value. This increases the array's length by one.
+ *
+ * @param value
+ * A double value.
+ * @throws JSONException
+ * if the value is not finite.
+ * @return this.
+ */
+ public JSONArray put(double value) throws JSONException {
+ Double d = new Double(value);
+ JSONObject.testValidity(d);
+ put(d);
+ return this;
+ }
+
+ /**
+ * Append an int value. This increases the array's length by one.
+ *
+ * @param value
+ * An int value.
+ * @return this.
+ */
+ public JSONArray put(int value) {
+ put(new Integer(value));
+ return this;
+ }
+
+ /**
+ * Append an long value. This increases the array's length by one.
+ *
+ * @param value
+ * A long value.
+ * @return this.
+ */
+ public JSONArray put(long value) {
+ put(new Long(value));
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONObject which
+ * is produced from a Map.
+ *
+ * @param value
+ * A Map value.
+ * @return this.
+ */
+ public JSONArray put(Map value) {
+ put(new JSONObject(value));
+ return this;
+ }
+
+ /**
+ * Append an object value. This increases the array's length by one.
+ *
+ * @param value
+ * An object value. The value should be a Boolean, Double,
+ * Integer, JSONArray, JSONObject, Long, or String, or the
+ * JSONObject.NULL object.
+ * @return this.
+ */
+ public JSONArray put(Object value) {
+ myArrayList.add(value);
+ return this;
+ }
+
+ /**
+ * Put or replace a boolean value in the JSONArray. If the index is greater
+ * than the length of the JSONArray, then null elements will be added as
+ * necessary to pad it out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A boolean value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, boolean value) throws JSONException {
+ put(index, value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONArray which
+ * is produced from a Collection.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A Collection value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the value is not finite.
+ */
+ public JSONArray put(int index, Collection value) throws JSONException {
+ put(index, new JSONArray(value));
+ return this;
+ }
+
+ /**
+ * Put or replace a double value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A double value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the value is not finite.
+ */
+ public JSONArray put(int index, double value) throws JSONException {
+ put(index, new Double(value));
+ return this;
+ }
+
+ /**
+ * Put or replace an int value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * An int value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, int value) throws JSONException {
+ put(index, new Integer(value));
+ return this;
+ }
+
+ /**
+ * Put or replace a long value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A long value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, long value) throws JSONException {
+ put(index, new Long(value));
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONObject that
+ * is produced from a Map.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * The Map value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the the value is an invalid
+ * number.
+ */
+ public JSONArray put(int index, Map value) throws JSONException {
+ put(index, new JSONObject(value));
+ return this;
+ }
+
+ /**
+ * Put or replace an object value in the JSONArray. If the index is greater
+ * than the length of the JSONArray, then null elements will be added as
+ * necessary to pad it out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * The value to put into the array. The value should be a
+ * Boolean, Double, Integer, JSONArray, JSONObject, Long, or
+ * String, or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the the value is an invalid
+ * number.
+ */
+ public JSONArray put(int index, Object value) throws JSONException {
+ JSONObject.testValidity(value);
+ if (index < 0) {
+ throw new JSONException("JSONArray[" + index + "] not found.");
+ }
+ if (index < length()) {
+ myArrayList.set(index, value);
+ } else {
+ while (index != length()) {
+ put(JSONObject.NULL);
+ }
+ put(value);
+ }
+ return this;
+ }
+
+ /**
+ * Remove an index and close the hole.
+ *
+ * @param index
+ * The index of the element to be removed.
+ * @return The value that was associated with the index, or null if there
+ * was no value.
+ */
+ public Object remove(int index) {
+ Object o = opt(index);
+ myArrayList.remove(index);
+ return o;
+ }
+
+ /**
+ * Produce a JSONObject by combining a JSONArray of names with the values of
+ * this JSONArray.
+ *
+ * @param names
+ * A JSONArray containing a list of key strings. These will be
+ * paired with the values.
+ * @return A JSONObject, or null if there are no names or if this JSONArray
+ * has no values.
+ * @throws JSONException
+ * If any of the names are null.
+ */
+ public JSONObject toJSONObject(JSONArray names) throws JSONException {
+ if (names == null || names.length() == 0 || length() == 0) {
+ return null;
+ }
+ JSONObject jo = new JSONObject();
+ for (int i = 0; i < names.length(); i += 1) {
+ jo.put(names.getString(i), opt(i));
+ }
+ return jo;
+ }
+
+ /**
+ * Make a JSON text of this JSONArray. For compactness, no unnecessary
+ * whitespace is added. If it is not possible to produce a syntactically
+ * correct JSON text then null will be returned instead. This could occur if
+ * the array contains an invalid number.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return a printable, displayable, transmittable representation of the
+ * array.
+ */
+ @Override
+ public String toString() {
+ try {
+ return '[' + join(",") + ']';
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Make a prettyprinted JSON text of this JSONArray. Warning: This method
+ * assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @return a printable, displayable, transmittable representation of the
+ * object, beginning with <code>[</code>&nbsp;<small>(left
+ * bracket)</small> and ending with <code>]</code>
+ * &nbsp;<small>(right bracket)</small>.
+ * @throws JSONException
+ */
+ public String toString(int indentFactor) throws JSONException {
+ return toString(indentFactor, 0);
+ }
+
+ /**
+ * Make a prettyprinted JSON text of this JSONArray. Warning: This method
+ * assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @param indent
+ * The indention of the top level.
+ * @return a printable, displayable, transmittable representation of the
+ * array.
+ * @throws JSONException
+ */
+ String toString(int indentFactor, int indent) throws JSONException {
+ int len = length();
+ if (len == 0) {
+ return "[]";
+ }
+ int i;
+ StringBuffer sb = new StringBuffer("[");
+ if (len == 1) {
+ sb.append(JSONObject.valueToString(myArrayList.get(0),
+ indentFactor, indent));
+ } else {
+ int newindent = indent + indentFactor;
+ sb.append('\n');
+ for (i = 0; i < len; i += 1) {
+ if (i > 0) {
+ sb.append(",\n");
+ }
+ for (int j = 0; j < newindent; j += 1) {
+ sb.append(' ');
+ }
+ sb.append(JSONObject.valueToString(myArrayList.get(i),
+ indentFactor, newindent));
+ }
+ sb.append('\n');
+ for (i = 0; i < indent; i += 1) {
+ sb.append(' ');
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ /**
+ * Write the contents of the JSONArray as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ public Writer write(Writer writer) throws JSONException {
+ try {
+ boolean b = false;
+ int len = length();
+
+ writer.write('[');
+
+ for (int i = 0; i < len; i += 1) {
+ if (b) {
+ writer.write(',');
+ }
+ Object v = myArrayList.get(i);
+ if (v instanceof JSONObject) {
+ ((JSONObject) v).write(writer);
+ } else if (v instanceof JSONArray) {
+ ((JSONArray) v).write(writer);
+ } else {
+ writer.write(JSONObject.valueToString(v));
+ }
+ b = true;
+ }
+ writer.write(']');
+ return writer;
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/external/json/JSONException.java b/src/com/vaadin/external/json/JSONException.java
new file mode 100644
index 0000000000..fecc38974e
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONException.java
@@ -0,0 +1,31 @@
+package com.vaadin.external.json;
+
+/**
+ * The JSONException is thrown by the JSON.org classes when things are amiss.
+ *
+ * @author JSON.org
+ * @version 2010-12-24
+ */
+public class JSONException extends Exception {
+ private static final long serialVersionUID = 0;
+ private Throwable cause;
+
+ /**
+ * Constructs a JSONException with an explanatory message.
+ *
+ * @param message
+ * Detail about the reason for the exception.
+ */
+ public JSONException(String message) {
+ super(message);
+ }
+
+ public JSONException(Throwable cause) {
+ super(cause.getMessage());
+ this.cause = cause;
+ }
+
+ public Throwable getCause() {
+ return this.cause;
+ }
+}
diff --git a/src/com/vaadin/external/json/JSONObject.java b/src/com/vaadin/external/json/JSONObject.java
new file mode 100644
index 0000000000..ba772933be
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONObject.java
@@ -0,0 +1,1693 @@
+package com.vaadin.external.json;
+
+/*
+ Copyright (c) 2002 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+/**
+ * A JSONObject is an unordered collection of name/value pairs. Its external
+ * form is a string wrapped in curly braces with colons between the names and
+ * values, and commas between the values and names. The internal form is an
+ * object having <code>get</code> and <code>opt</code> methods for accessing the
+ * values by name, and <code>put</code> methods for adding or replacing values
+ * by name. The values can be any of these types: <code>Boolean</code>,
+ * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
+ * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
+ * constructor can be used to convert an external form JSON text into an
+ * internal form whose values can be retrieved with the <code>get</code> and
+ * <code>opt</code> methods, or to convert values into a JSON text using the
+ * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
+ * returns a value if one can be found, and throws an exception if one cannot be
+ * found. An <code>opt</code> method returns a default value instead of throwing
+ * an exception, and so is useful for obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object, which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you. The opt methods differ from the get methods in that they do
+ * not throw. Instead, they return a specified value, such as null.
+ * <p>
+ * The <code>put</code> methods add or replace values in an object. For example,
+ *
+ * <pre>
+ * myString = new JSONObject().put(&quot;JSON&quot;, &quot;Hello, World!&quot;).toString();
+ * </pre>
+ *
+ * produces the string <code>{"JSON": "Hello, World"}</code>.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * the JSON syntax rules. The constructors are more forgiving in the texts they
+ * will accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ * before the closing brace.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ * quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ * or single quote, and if they do not contain leading or trailing spaces, and
+ * if they do not contain any of these characters:
+ * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
+ * if they are not the reserved words <code>true</code>, <code>false</code>, or
+ * <code>null</code>.</li>
+ * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
+ * <code>:</code>.</li>
+ * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
+ * well as by <code>,</code> <small>(comma)</small>.</li>
+ * <li>Numbers may have the <code>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+ *
+ * @author JSON.org
+ * @version 2011-10-16
+ */
+public class JSONObject implements Serializable {
+
+ /**
+ * JSONObject.NULL is equivalent to the value that JavaScript calls null,
+ * whilst Java's null is equivalent to the value that JavaScript calls
+ * undefined.
+ */
+ private static final class Null implements Serializable {
+
+ /**
+ * There is only intended to be a single instance of the NULL object, so
+ * the clone method returns itself.
+ *
+ * @return NULL.
+ */
+ @Override
+ protected final Object clone() {
+ return this;
+ }
+
+ /**
+ * A Null object is equal to the null value and to itself.
+ *
+ * @param object
+ * An object to test for nullness.
+ * @return true if the object parameter is the JSONObject.NULL object or
+ * null.
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object == null || object == this;
+ }
+
+ /**
+ * Get the "null" string value.
+ *
+ * @return The string "null".
+ */
+ @Override
+ public String toString() {
+ return "null";
+ }
+ }
+
+ /**
+ * The map where the JSONObject's properties are kept.
+ */
+ private Map map;
+
+ /**
+ * It is sometimes more convenient and less ambiguous to have a
+ * <code>NULL</code> object than to use Java's <code>null</code> value.
+ * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
+ * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
+ */
+ public static final Object NULL = new Null();
+
+ /**
+ * Construct an empty JSONObject.
+ */
+ public JSONObject() {
+ map = new HashMap();
+ }
+
+ /**
+ * Construct a JSONObject from a subset of another JSONObject. An array of
+ * strings is used to identify the keys that should be copied. Missing keys
+ * are ignored.
+ *
+ * @param jo
+ * A JSONObject.
+ * @param names
+ * An array of strings.
+ * @throws JSONException
+ * @exception JSONException
+ * If a value is a non-finite number or if a name is
+ * duplicated.
+ */
+ public JSONObject(JSONObject jo, String[] names) {
+ this();
+ for (int i = 0; i < names.length; i += 1) {
+ try {
+ putOnce(names[i], jo.opt(names[i]));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONObject from a JSONTokener.
+ *
+ * @param x
+ * A JSONTokener object containing the source string.
+ * @throws JSONException
+ * If there is a syntax error in the source string or a
+ * duplicated key.
+ */
+ public JSONObject(JSONTokener x) throws JSONException {
+ this();
+ char c;
+ String key;
+
+ if (x.nextClean() != '{') {
+ throw x.syntaxError("A JSONObject text must begin with '{'");
+ }
+ for (;;) {
+ c = x.nextClean();
+ switch (c) {
+ case 0:
+ throw x.syntaxError("A JSONObject text must end with '}'");
+ case '}':
+ return;
+ default:
+ x.back();
+ key = x.nextValue().toString();
+ }
+
+ // The key is followed by ':'. We will also tolerate '=' or '=>'.
+
+ c = x.nextClean();
+ if (c == '=') {
+ if (x.next() != '>') {
+ x.back();
+ }
+ } else if (c != ':') {
+ throw x.syntaxError("Expected a ':' after a key");
+ }
+ putOnce(key, x.nextValue());
+
+ // Pairs are separated by ','. We will also tolerate ';'.
+
+ switch (x.nextClean()) {
+ case ';':
+ case ',':
+ if (x.nextClean() == '}') {
+ return;
+ }
+ x.back();
+ break;
+ case '}':
+ return;
+ default:
+ throw x.syntaxError("Expected a ',' or '}'");
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONObject from a Map.
+ *
+ * @param map
+ * A map object that can be used to initialize the contents of
+ * the JSONObject.
+ * @throws JSONException
+ */
+ public JSONObject(Map map) {
+ this.map = new HashMap();
+ if (map != null) {
+ Iterator i = map.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ Object value = e.getValue();
+ if (value != null) {
+ this.map.put(e.getKey(), wrap(value));
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONObject from an Object using bean getters. It reflects on
+ * all of the public methods of the object. For each of the methods with no
+ * parameters and a name starting with <code>"get"</code> or
+ * <code>"is"</code> followed by an uppercase letter, the method is invoked,
+ * and a key and the value returned from the getter method are put into the
+ * new JSONObject.
+ *
+ * The key is formed by removing the <code>"get"</code> or <code>"is"</code>
+ * prefix. If the second remaining character is not upper case, then the
+ * first character is converted to lower case.
+ *
+ * For example, if an object has a method named <code>"getName"</code>, and
+ * if the result of calling <code>object.getName()</code> is
+ * <code>"Larry Fine"</code>, then the JSONObject will contain
+ * <code>"name": "Larry Fine"</code>.
+ *
+ * @param bean
+ * An object that has getter methods that should be used to make
+ * a JSONObject.
+ */
+ public JSONObject(Object bean) {
+ this();
+ populateMap(bean);
+ }
+
+ /**
+ * Construct a JSONObject from an Object, using reflection to find the
+ * public members. The resulting JSONObject's keys will be the strings from
+ * the names array, and the values will be the field values associated with
+ * those keys in the object. If a key is not found or not visible, then it
+ * will not be copied into the new JSONObject.
+ *
+ * @param object
+ * An object that has fields that should be used to make a
+ * JSONObject.
+ * @param names
+ * An array of strings, the names of the fields to be obtained
+ * from the object.
+ */
+ public JSONObject(Object object, String names[]) {
+ this();
+ Class c = object.getClass();
+ for (int i = 0; i < names.length; i += 1) {
+ String name = names[i];
+ try {
+ putOpt(name, c.getField(name).get(object));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONObject from a source JSON text string. This is the most
+ * commonly used JSONObject constructor.
+ *
+ * @param source
+ * A string beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>
+ * &nbsp;<small>(right brace)</small>.
+ * @exception JSONException
+ * If there is a syntax error in the source string or a
+ * duplicated key.
+ */
+ public JSONObject(String source) throws JSONException {
+ this(new JSONTokener(source));
+ }
+
+ /**
+ * Construct a JSONObject from a ResourceBundle.
+ *
+ * @param baseName
+ * The ResourceBundle base name.
+ * @param locale
+ * The Locale to load the ResourceBundle for.
+ * @throws JSONException
+ * If any JSONExceptions are detected.
+ */
+ public JSONObject(String baseName, Locale locale) throws JSONException {
+ this();
+ ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
+ Thread.currentThread().getContextClassLoader());
+
+ // Iterate through the keys in the bundle.
+
+ Enumeration keys = bundle.getKeys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ if (key instanceof String) {
+
+ // Go through the path, ensuring that there is a nested
+ // JSONObject for each
+ // segment except the last. Add the value using the last
+ // segment's name into
+ // the deepest nested JSONObject.
+
+ String[] path = ((String) key).split("\\.");
+ int last = path.length - 1;
+ JSONObject target = this;
+ for (int i = 0; i < last; i += 1) {
+ String segment = path[i];
+ JSONObject nextTarget = target.optJSONObject(segment);
+ if (nextTarget == null) {
+ nextTarget = new JSONObject();
+ target.put(segment, nextTarget);
+ }
+ target = nextTarget;
+ }
+ target.put(path[last], bundle.getString((String) key));
+ }
+ }
+ }
+
+ /**
+ * Accumulate values under a key. It is similar to the put method except
+ * that if there is already an object stored under the key then a JSONArray
+ * is stored under the key to hold all of the accumulated values. If there
+ * is already a JSONArray, then the new value is appended to it. In
+ * contrast, the put method replaces the previous value.
+ *
+ * If only one value is accumulated that is not a JSONArray, then the result
+ * will be the same as using put. But if multiple values are accumulated,
+ * then the result will be like append.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException
+ * If the value is an invalid number or if the key is null.
+ */
+ public JSONObject accumulate(String key, Object value) throws JSONException {
+ testValidity(value);
+ Object object = opt(key);
+ if (object == null) {
+ put(key, value instanceof JSONArray ? new JSONArray().put(value)
+ : value);
+ } else if (object instanceof JSONArray) {
+ ((JSONArray) object).put(value);
+ } else {
+ put(key, new JSONArray().put(object).put(value));
+ }
+ return this;
+ }
+
+ /**
+ * Append values to the array under a key. If the key does not exist in the
+ * JSONObject, then the key is put in the JSONObject with its value being a
+ * JSONArray containing the value parameter. If the key was already
+ * associated with a JSONArray, then the value parameter is appended to it.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException
+ * If the key is null or if the current value associated with
+ * the key is not a JSONArray.
+ */
+ public JSONObject append(String key, Object value) throws JSONException {
+ testValidity(value);
+ Object object = opt(key);
+ if (object == null) {
+ put(key, new JSONArray().put(value));
+ } else if (object instanceof JSONArray) {
+ put(key, ((JSONArray) object).put(value));
+ } else {
+ throw new JSONException("JSONObject[" + key
+ + "] is not a JSONArray.");
+ }
+ return this;
+ }
+
+ /**
+ * Produce a string from a double. The string "null" will be returned if the
+ * number is not finite.
+ *
+ * @param d
+ * A double.
+ * @return A String.
+ */
+ public static String doubleToString(double d) {
+ if (Double.isInfinite(d) || Double.isNaN(d)) {
+ return "null";
+ }
+
+ // Shave off trailing zeros and decimal point, if possible.
+
+ String string = Double.toString(d);
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0
+ && string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+ /**
+ * Get the value object associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return The object associated with the key.
+ * @throws JSONException
+ * if the key is not found.
+ */
+ public Object get(String key) throws JSONException {
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ Object object = opt(key);
+ if (object == null) {
+ throw new JSONException("JSONObject[" + quote(key) + "] not found.");
+ }
+ return object;
+ }
+
+ /**
+ * Get the boolean value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return The truth.
+ * @throws JSONException
+ * if the value is not a Boolean or the String "true" or
+ * "false".
+ */
+ public boolean getBoolean(String key) throws JSONException {
+ Object object = get(key);
+ if (object.equals(Boolean.FALSE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("false"))) {
+ return false;
+ } else if (object.equals(Boolean.TRUE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("true"))) {
+ return true;
+ }
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not a Boolean.");
+ }
+
+ /**
+ * Get the double value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return The numeric value.
+ * @throws JSONException
+ * if the key is not found or if the value is not a Number
+ * object and cannot be converted to a number.
+ */
+ public double getDouble(String key) throws JSONException {
+ Object object = get(key);
+ try {
+ return object instanceof Number ? ((Number) object).doubleValue()
+ : Double.parseDouble((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the int value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return The integer value.
+ * @throws JSONException
+ * if the key is not found or if the value cannot be converted
+ * to an integer.
+ */
+ public int getInt(String key) throws JSONException {
+ Object object = get(key);
+ try {
+ return object instanceof Number ? ((Number) object).intValue()
+ : Integer.parseInt((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not an int.");
+ }
+ }
+
+ /**
+ * Get the JSONArray value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return A JSONArray which is the value.
+ * @throws JSONException
+ * if the key is not found or if the value is not a JSONArray.
+ */
+ public JSONArray getJSONArray(String key) throws JSONException {
+ Object object = get(key);
+ if (object instanceof JSONArray) {
+ return (JSONArray) object;
+ }
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not a JSONArray.");
+ }
+
+ /**
+ * Get the JSONObject value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return A JSONObject which is the value.
+ * @throws JSONException
+ * if the key is not found or if the value is not a JSONObject.
+ */
+ public JSONObject getJSONObject(String key) throws JSONException {
+ Object object = get(key);
+ if (object instanceof JSONObject) {
+ return (JSONObject) object;
+ }
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not a JSONObject.");
+ }
+
+ /**
+ * Get the long value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return The long value.
+ * @throws JSONException
+ * if the key is not found or if the value cannot be converted
+ * to a long.
+ */
+ public long getLong(String key) throws JSONException {
+ Object object = get(key);
+ try {
+ return object instanceof Number ? ((Number) object).longValue()
+ : Long.parseLong((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key)
+ + "] is not a long.");
+ }
+ }
+
+ /**
+ * Get an array of field names from a JSONObject.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(JSONObject jo) {
+ int length = jo.length();
+ if (length == 0) {
+ return null;
+ }
+ Iterator iterator = jo.keys();
+ String[] names = new String[length];
+ int i = 0;
+ while (iterator.hasNext()) {
+ names[i] = (String) iterator.next();
+ i += 1;
+ }
+ return names;
+ }
+
+ /**
+ * Get an array of field names from an Object.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(Object object) {
+ if (object == null) {
+ return null;
+ }
+ Class klass = object.getClass();
+ Field[] fields = klass.getFields();
+ int length = fields.length;
+ if (length == 0) {
+ return null;
+ }
+ String[] names = new String[length];
+ for (int i = 0; i < length; i += 1) {
+ names[i] = fields[i].getName();
+ }
+ return names;
+ }
+
+ /**
+ * Get the string associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return A string which is the value.
+ * @throws JSONException
+ * if there is no string value for the key.
+ */
+ public String getString(String key) throws JSONException {
+ Object object = get(key);
+ if (object instanceof String) {
+ return (String) object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) + "] not a string.");
+ }
+
+ /**
+ * Determine if the JSONObject contains a specific key.
+ *
+ * @param key
+ * A key string.
+ * @return true if the key exists in the JSONObject.
+ */
+ public boolean has(String key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Increment a property of a JSONObject. If there is no such property,
+ * create one with a value of 1. If there is such a property, and if it is
+ * an Integer, Long, Double, or Float, then add one to it.
+ *
+ * @param key
+ * A key string.
+ * @return this.
+ * @throws JSONException
+ * If there is already a property with this name that is not an
+ * Integer, Long, Double, or Float.
+ */
+ public JSONObject increment(String key) throws JSONException {
+ Object value = opt(key);
+ if (value == null) {
+ put(key, 1);
+ } else if (value instanceof Integer) {
+ put(key, ((Integer) value).intValue() + 1);
+ } else if (value instanceof Long) {
+ put(key, ((Long) value).longValue() + 1);
+ } else if (value instanceof Double) {
+ put(key, ((Double) value).doubleValue() + 1);
+ } else if (value instanceof Float) {
+ put(key, ((Float) value).floatValue() + 1);
+ } else {
+ throw new JSONException("Unable to increment [" + quote(key) + "].");
+ }
+ return this;
+ }
+
+ /**
+ * Determine if the value associated with the key is null or if there is no
+ * value.
+ *
+ * @param key
+ * A key string.
+ * @return true if there is no value associated with the key or if the value
+ * is the JSONObject.NULL object.
+ */
+ public boolean isNull(String key) {
+ return JSONObject.NULL.equals(opt(key));
+ }
+
+ /**
+ * Get an enumeration of the keys of the JSONObject.
+ *
+ * @return An iterator of the keys.
+ */
+ public Iterator keys() {
+ return map.keySet().iterator();
+ }
+
+ /**
+ * Get the number of keys stored in the JSONObject.
+ *
+ * @return The number of keys in the JSONObject.
+ */
+ public int length() {
+ return map.size();
+ }
+
+ /**
+ * Produce a JSONArray containing the names of the elements of this
+ * JSONObject.
+ *
+ * @return A JSONArray containing the key strings, or null if the JSONObject
+ * is empty.
+ */
+ public JSONArray names() {
+ JSONArray ja = new JSONArray();
+ Iterator keys = keys();
+ while (keys.hasNext()) {
+ ja.put(keys.next());
+ }
+ return ja.length() == 0 ? null : ja;
+ }
+
+ /**
+ * Produce a string from a Number.
+ *
+ * @param number
+ * A Number
+ * @return A String.
+ * @throws JSONException
+ * If n is a non-finite number.
+ */
+ public static String numberToString(Number number) throws JSONException {
+ if (number == null) {
+ throw new JSONException("Null pointer");
+ }
+ testValidity(number);
+
+ // Shave off trailing zeros and decimal point, if possible.
+
+ String string = number.toString();
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0
+ && string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+ /**
+ * Get an optional value associated with a key.
+ *
+ * @param key
+ * A key string.
+ * @return An object which is the value, or null if there is no value.
+ */
+ public Object opt(String key) {
+ return key == null ? null : map.get(key);
+ }
+
+ /**
+ * Get an optional boolean associated with a key. It returns false if there
+ * is no such key, or if the value is not Boolean.TRUE or the String "true".
+ *
+ * @param key
+ * A key string.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key) {
+ return optBoolean(key, false);
+ }
+
+ /**
+ * Get an optional boolean associated with a key. It returns the
+ * defaultValue if there is no such key, or if it is not a Boolean or the
+ * String "true" or "false" (case insensitive).
+ *
+ * @param key
+ * A key string.
+ * @param defaultValue
+ * The default.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key, boolean defaultValue) {
+ try {
+ return getBoolean(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get an optional double associated with a key, or NaN if there is no such
+ * key or if its value is not a number. If the value is a string, an attempt
+ * will be made to evaluate it as a number.
+ *
+ * @param key
+ * A string which is the key.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key) {
+ return optDouble(key, Double.NaN);
+ }
+
+ /**
+ * Get an optional double associated with a key, or the defaultValue if
+ * there is no such key or if its value is not a number. If the value is a
+ * string, an attempt will be made to evaluate it as a number.
+ *
+ * @param key
+ * A key string.
+ * @param defaultValue
+ * The default.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key, double defaultValue) {
+ try {
+ return getDouble(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get an optional int value associated with a key, or zero if there is no
+ * such key or if the value is not a number. If the value is a string, an
+ * attempt will be made to evaluate it as a number.
+ *
+ * @param key
+ * A key string.
+ * @return An object which is the value.
+ */
+ public int optInt(String key) {
+ return optInt(key, 0);
+ }
+
+ /**
+ * Get an optional int value associated with a key, or the default if there
+ * is no such key or if the value is not a number. If the value is a string,
+ * an attempt will be made to evaluate it as a number.
+ *
+ * @param key
+ * A key string.
+ * @param defaultValue
+ * The default.
+ * @return An object which is the value.
+ */
+ public int optInt(String key, int defaultValue) {
+ try {
+ return getInt(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get an optional JSONArray associated with a key. It returns null if there
+ * is no such key, or if its value is not a JSONArray.
+ *
+ * @param key
+ * A key string.
+ * @return A JSONArray which is the value.
+ */
+ public JSONArray optJSONArray(String key) {
+ Object o = opt(key);
+ return o instanceof JSONArray ? (JSONArray) o : null;
+ }
+
+ /**
+ * Get an optional JSONObject associated with a key. It returns null if
+ * there is no such key, or if its value is not a JSONObject.
+ *
+ * @param key
+ * A key string.
+ * @return A JSONObject which is the value.
+ */
+ public JSONObject optJSONObject(String key) {
+ Object object = opt(key);
+ return object instanceof JSONObject ? (JSONObject) object : null;
+ }
+
+ /**
+ * Get an optional long value associated with a key, or zero if there is no
+ * such key or if the value is not a number. If the value is a string, an
+ * attempt will be made to evaluate it as a number.
+ *
+ * @param key
+ * A key string.
+ * @return An object which is the value.
+ */
+ public long optLong(String key) {
+ return optLong(key, 0);
+ }
+
+ /**
+ * Get an optional long value associated with a key, or the default if there
+ * is no such key or if the value is not a number. If the value is a string,
+ * an attempt will be made to evaluate it as a number.
+ *
+ * @param key
+ * A key string.
+ * @param defaultValue
+ * The default.
+ * @return An object which is the value.
+ */
+ public long optLong(String key, long defaultValue) {
+ try {
+ return getLong(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get an optional string associated with a key. It returns an empty string
+ * if there is no such key. If the value is not a string and is not null,
+ * then it is converted to a string.
+ *
+ * @param key
+ * A key string.
+ * @return A string which is the value.
+ */
+ public String optString(String key) {
+ return optString(key, "");
+ }
+
+ /**
+ * Get an optional string associated with a key. It returns the defaultValue
+ * if there is no such key.
+ *
+ * @param key
+ * A key string.
+ * @param defaultValue
+ * The default.
+ * @return A string which is the value.
+ */
+ public String optString(String key, String defaultValue) {
+ Object object = opt(key);
+ return NULL.equals(object) ? defaultValue : object.toString();
+ }
+
+ private void populateMap(Object bean) {
+ Class klass = bean.getClass();
+
+ // If klass is a System class then set includeSuperClass to false.
+
+ boolean includeSuperClass = klass.getClassLoader() != null;
+
+ Method[] methods = (includeSuperClass) ? klass.getMethods() : klass
+ .getDeclaredMethods();
+ for (int i = 0; i < methods.length; i += 1) {
+ try {
+ Method method = methods[i];
+ if (Modifier.isPublic(method.getModifiers())) {
+ String name = method.getName();
+ String key = "";
+ if (name.startsWith("get")) {
+ if (name.equals("getClass")
+ || name.equals("getDeclaringClass")) {
+ key = "";
+ } else {
+ key = name.substring(3);
+ }
+ } else if (name.startsWith("is")) {
+ key = name.substring(2);
+ }
+ if (key.length() > 0
+ && Character.isUpperCase(key.charAt(0))
+ && method.getParameterTypes().length == 0) {
+ if (key.length() == 1) {
+ key = key.toLowerCase();
+ } else if (!Character.isUpperCase(key.charAt(1))) {
+ key = key.substring(0, 1).toLowerCase()
+ + key.substring(1);
+ }
+
+ Object result = method.invoke(bean, (Object[]) null);
+ if (result != null) {
+ map.put(key, wrap(result));
+ }
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ /**
+ * Put a key/boolean pair in the JSONObject.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * A boolean which is the value.
+ * @return this.
+ * @throws JSONException
+ * If the key is null.
+ */
+ public JSONObject put(String key, boolean value) throws JSONException {
+ put(key, value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONArray which is produced from a Collection.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * A Collection value.
+ * @return this.
+ * @throws JSONException
+ */
+ public JSONObject put(String key, Collection value) throws JSONException {
+ put(key, new JSONArray(value));
+ return this;
+ }
+
+ /**
+ * Put a key/double pair in the JSONObject.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * A double which is the value.
+ * @return this.
+ * @throws JSONException
+ * If the key is null or if the number is invalid.
+ */
+ public JSONObject put(String key, double value) throws JSONException {
+ put(key, new Double(value));
+ return this;
+ }
+
+ /**
+ * Put a key/int pair in the JSONObject.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * An int which is the value.
+ * @return this.
+ * @throws JSONException
+ * If the key is null.
+ */
+ public JSONObject put(String key, int value) throws JSONException {
+ put(key, new Integer(value));
+ return this;
+ }
+
+ /**
+ * Put a key/long pair in the JSONObject.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * A long which is the value.
+ * @return this.
+ * @throws JSONException
+ * If the key is null.
+ */
+ public JSONObject put(String key, long value) throws JSONException {
+ put(key, new Long(value));
+ return this;
+ }
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONObject which is produced from a Map.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * A Map value.
+ * @return this.
+ * @throws JSONException
+ */
+ public JSONObject put(String key, Map value) throws JSONException {
+ put(key, new JSONObject(value));
+ return this;
+ }
+
+ /**
+ * Put a key/value pair in the JSONObject. If the value is null, then the
+ * key will be removed from the JSONObject if it is present.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
+ * String, or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException
+ * If the value is non-finite number or if the key is null.
+ */
+ public JSONObject put(String key, Object value) throws JSONException {
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ if (value != null) {
+ testValidity(value);
+ map.put(key, value);
+ } else {
+ remove(key);
+ }
+ return this;
+ }
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the key and the value
+ * are both non-null, and only if there is not already a member with that
+ * name.
+ *
+ * @param key
+ * @param value
+ * @return his.
+ * @throws JSONException
+ * if the key is a duplicate
+ */
+ public JSONObject putOnce(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ if (opt(key) != null) {
+ throw new JSONException("Duplicate key \"" + key + "\"");
+ }
+ put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the key and the value
+ * are both non-null.
+ *
+ * @param key
+ * A key string.
+ * @param value
+ * An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
+ * String, or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException
+ * If the value is a non-finite number.
+ */
+ public JSONObject putOpt(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * Produce a string in double quotes with backslash sequences in all the
+ * right places. A backslash will be inserted within </, producing <\/,
+ * allowing JSON text to be delivered in HTML. In JSON text, a string cannot
+ * contain a control character or an unescaped quote or backslash.
+ *
+ * @param string
+ * A String
+ * @return A String correctly formatted for insertion in a JSON text.
+ */
+ public static String quote(String string) {
+ if (string == null || string.length() == 0) {
+ return "\"\"";
+ }
+
+ char b;
+ char c = 0;
+ String hhhh;
+ int i;
+ int len = string.length();
+ StringBuffer sb = new StringBuffer(len + 4);
+
+ sb.append('"');
+ for (i = 0; i < len; i += 1) {
+ b = c;
+ c = string.charAt(i);
+ switch (c) {
+ case '\\':
+ case '"':
+ sb.append('\\');
+ sb.append(c);
+ break;
+ case '/':
+ if (b == '<') {
+ sb.append('\\');
+ }
+ sb.append(c);
+ break;
+ case '\b':
+ sb.append("\\b");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\f':
+ sb.append("\\f");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ default:
+ if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
+ || (c >= '\u2000' && c < '\u2100')) {
+ hhhh = "000" + Integer.toHexString(c);
+ sb.append("\\u" + hhhh.substring(hhhh.length() - 4));
+ } else {
+ sb.append(c);
+ }
+ }
+ }
+ sb.append('"');
+ return sb.toString();
+ }
+
+ /**
+ * Remove a name and its value, if present.
+ *
+ * @param key
+ * The name to be removed.
+ * @return The value that was associated with the name, or null if there was
+ * no value.
+ */
+ public Object remove(String key) {
+ return map.remove(key);
+ }
+
+ /**
+ * Try to convert a string into a number, boolean, or null. If the string
+ * can't be converted, return the string.
+ *
+ * @param string
+ * A String.
+ * @return A simple JSON value.
+ */
+ public static Object stringToValue(String string) {
+ Double d;
+ if (string.equals("")) {
+ return string;
+ }
+ if (string.equalsIgnoreCase("true")) {
+ return Boolean.TRUE;
+ }
+ if (string.equalsIgnoreCase("false")) {
+ return Boolean.FALSE;
+ }
+ if (string.equalsIgnoreCase("null")) {
+ return JSONObject.NULL;
+ }
+
+ /*
+ * If it might be a number, try converting it. We support the
+ * non-standard 0x- convention. If a number cannot be produced, then the
+ * value will just be a string. Note that the 0x-, plus, and implied
+ * string conventions are non-standard. A JSON parser may accept
+ * non-JSON forms as long as it accepts all correct JSON forms.
+ */
+
+ char b = string.charAt(0);
+ if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
+ if (b == '0' && string.length() > 2
+ && (string.charAt(1) == 'x' || string.charAt(1) == 'X')) {
+ try {
+ return new Integer(
+ Integer.parseInt(string.substring(2), 16));
+ } catch (Exception ignore) {
+ }
+ }
+ try {
+ if (string.indexOf('.') > -1 || string.indexOf('e') > -1
+ || string.indexOf('E') > -1) {
+ d = Double.valueOf(string);
+ if (!d.isInfinite() && !d.isNaN()) {
+ return d;
+ }
+ } else {
+ Long myLong = new Long(string);
+ if (myLong.longValue() == myLong.intValue()) {
+ return new Integer(myLong.intValue());
+ } else {
+ return myLong;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ return string;
+ }
+
+ /**
+ * Throw an exception if the object is a NaN or infinite number.
+ *
+ * @param o
+ * The object to test.
+ * @throws JSONException
+ * If o is a non-finite number.
+ */
+ public static void testValidity(Object o) throws JSONException {
+ if (o != null) {
+ if (o instanceof Double) {
+ if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ } else if (o instanceof Float) {
+ if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Produce a JSONArray containing the values of the members of this
+ * JSONObject.
+ *
+ * @param names
+ * A JSONArray containing a list of key strings. This determines
+ * the sequence of the values in the result.
+ * @return A JSONArray of values.
+ * @throws JSONException
+ * If any of the values are non-finite numbers.
+ */
+ public JSONArray toJSONArray(JSONArray names) throws JSONException {
+ if (names == null || names.length() == 0) {
+ return null;
+ }
+ JSONArray ja = new JSONArray();
+ for (int i = 0; i < names.length(); i += 1) {
+ ja.put(opt(names.getString(i)));
+ }
+ return ja;
+ }
+
+ /**
+ * Make a JSON text of this JSONObject. For compactness, no whitespace is
+ * added. If this would not result in a syntactically correct JSON text,
+ * then null will be returned instead.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return a printable, displayable, portable, transmittable representation
+ * of the object, beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
+ * brace)</small>.
+ */
+ @Override
+ public String toString() {
+ try {
+ Iterator keys = keys();
+ StringBuffer sb = new StringBuffer("{");
+
+ while (keys.hasNext()) {
+ if (sb.length() > 1) {
+ sb.append(',');
+ }
+ Object o = keys.next();
+ sb.append(quote(o.toString()));
+ sb.append(':');
+ sb.append(valueToString(map.get(o)));
+ }
+ sb.append('}');
+ return sb.toString();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Make a prettyprinted JSON text of this JSONObject.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @return a printable, displayable, portable, transmittable representation
+ * of the object, beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
+ * brace)</small>.
+ * @throws JSONException
+ * If the object contains an invalid number.
+ */
+ public String toString(int indentFactor) throws JSONException {
+ return toString(indentFactor, 0);
+ }
+
+ /**
+ * Make a prettyprinted JSON text of this JSONObject.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @param indent
+ * The indentation of the top level.
+ * @return a printable, displayable, transmittable representation of the
+ * object, beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
+ * brace)</small>.
+ * @throws JSONException
+ * If the object contains an invalid number.
+ */
+ String toString(int indentFactor, int indent) throws JSONException {
+ int i;
+ int length = length();
+ if (length == 0) {
+ return "{}";
+ }
+ Iterator keys = keys();
+ int newindent = indent + indentFactor;
+ Object object;
+ StringBuffer sb = new StringBuffer("{");
+ if (length == 1) {
+ object = keys.next();
+ sb.append(quote(object.toString()));
+ sb.append(": ");
+ sb.append(valueToString(map.get(object), indentFactor, indent));
+ } else {
+ while (keys.hasNext()) {
+ object = keys.next();
+ if (sb.length() > 1) {
+ sb.append(",\n");
+ } else {
+ sb.append('\n');
+ }
+ for (i = 0; i < newindent; i += 1) {
+ sb.append(' ');
+ }
+ sb.append(quote(object.toString()));
+ sb.append(": ");
+ sb.append(valueToString(map.get(object), indentFactor,
+ newindent));
+ }
+ if (sb.length() > 1) {
+ sb.append('\n');
+ for (i = 0; i < indent; i += 1) {
+ sb.append(' ');
+ }
+ }
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Make a JSON text of an Object value. If the object has an
+ * value.toJSONString() method, then that method will be used to produce the
+ * JSON text. The method is required to produce a strictly conforming text.
+ * If the object does not contain a toJSONString method (which is the most
+ * common case), then a text will be produced by other means. If the value
+ * is an array or Collection, then a JSONArray will be made from it and its
+ * toJSONString method will be called. If the value is a MAP, then a
+ * JSONObject will be made from it and its toJSONString method will be
+ * called. Otherwise, the value's toString method will be called, and the
+ * result will be quoted.
+ *
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @param value
+ * The value to be serialized.
+ * @return a printable, displayable, transmittable representation of the
+ * object, beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
+ * brace)</small>.
+ * @throws JSONException
+ * If the value is or contains an invalid number.
+ */
+ public static String valueToString(Object value) throws JSONException {
+ if (value == null || value.equals(null)) {
+ return "null";
+ }
+ if (value instanceof JSONString) {
+ Object object;
+ try {
+ object = ((JSONString) value).toJSONString();
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+ if (object instanceof String) {
+ return (String) object;
+ }
+ throw new JSONException("Bad value from toJSONString: " + object);
+ }
+ if (value instanceof Number) {
+ return numberToString((Number) value);
+ }
+ if (value instanceof Boolean || value instanceof JSONObject
+ || value instanceof JSONArray) {
+ return value.toString();
+ }
+ if (value instanceof Map) {
+ return new JSONObject((Map) value).toString();
+ }
+ if (value instanceof Collection) {
+ return new JSONArray((Collection) value).toString();
+ }
+ if (value.getClass().isArray()) {
+ return new JSONArray(value).toString();
+ }
+ return quote(value.toString());
+ }
+
+ /**
+ * Make a prettyprinted JSON text of an object value.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @param value
+ * The value to be serialized.
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @param indent
+ * The indentation of the top level.
+ * @return a printable, displayable, transmittable representation of the
+ * object, beginning with <code>{</code>&nbsp;<small>(left
+ * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
+ * brace)</small>.
+ * @throws JSONException
+ * If the object contains an invalid number.
+ */
+ static String valueToString(Object value, int indentFactor, int indent)
+ throws JSONException {
+ if (value == null || value.equals(null)) {
+ return "null";
+ }
+ try {
+ if (value instanceof JSONString) {
+ Object o = ((JSONString) value).toJSONString();
+ if (o instanceof String) {
+ return (String) o;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ if (value instanceof Number) {
+ return numberToString((Number) value);
+ }
+ if (value instanceof Boolean) {
+ return value.toString();
+ }
+ if (value instanceof JSONObject) {
+ return ((JSONObject) value).toString(indentFactor, indent);
+ }
+ if (value instanceof JSONArray) {
+ return ((JSONArray) value).toString(indentFactor, indent);
+ }
+ if (value instanceof Map) {
+ return new JSONObject((Map) value).toString(indentFactor, indent);
+ }
+ if (value instanceof Collection) {
+ return new JSONArray((Collection) value).toString(indentFactor,
+ indent);
+ }
+ if (value.getClass().isArray()) {
+ return new JSONArray(value).toString(indentFactor, indent);
+ }
+ return quote(value.toString());
+ }
+
+ /**
+ * Wrap an object, if necessary. If the object is null, return the NULL
+ * object. If it is an array or collection, wrap it in a JSONArray. If it is
+ * a map, wrap it in a JSONObject. If it is a standard property (Double,
+ * String, et al) then it is already wrapped. Otherwise, if it comes from
+ * one of the java packages, turn it into a string. And if it doesn't, try
+ * to wrap it in a JSONObject. If the wrapping fails, then null is returned.
+ *
+ * @param object
+ * The object to wrap
+ * @return The wrapped value
+ */
+ public static Object wrap(Object object) {
+ try {
+ if (object == null) {
+ return NULL;
+ }
+ if (object instanceof JSONObject || object instanceof JSONArray
+ || NULL.equals(object) || object instanceof JSONString
+ || object instanceof Byte || object instanceof Character
+ || object instanceof Short || object instanceof Integer
+ || object instanceof Long || object instanceof Boolean
+ || object instanceof Float || object instanceof Double
+ || object instanceof String) {
+ return object;
+ }
+
+ if (object instanceof Collection) {
+ return new JSONArray((Collection) object);
+ }
+ if (object.getClass().isArray()) {
+ return new JSONArray(object);
+ }
+ if (object instanceof Map) {
+ return new JSONObject((Map) object);
+ }
+ Package objectPackage = object.getClass().getPackage();
+ String objectPackageName = objectPackage != null ? objectPackage
+ .getName() : "";
+ if (objectPackageName.startsWith("java.")
+ || objectPackageName.startsWith("javax.")
+ || object.getClass().getClassLoader() == null) {
+ return object.toString();
+ }
+ return new JSONObject(object);
+ } catch (Exception exception) {
+ return null;
+ }
+ }
+
+ /**
+ * Write the contents of the JSONObject as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ public Writer write(Writer writer) throws JSONException {
+ try {
+ boolean commanate = false;
+ Iterator keys = keys();
+ writer.write('{');
+
+ while (keys.hasNext()) {
+ if (commanate) {
+ writer.write(',');
+ }
+ Object key = keys.next();
+ writer.write(quote(key.toString()));
+ writer.write(':');
+ Object value = map.get(key);
+ if (value instanceof JSONObject) {
+ ((JSONObject) value).write(writer);
+ } else if (value instanceof JSONArray) {
+ ((JSONArray) value).write(writer);
+ } else {
+ writer.write(valueToString(value));
+ }
+ commanate = true;
+ }
+ writer.write('}');
+ return writer;
+ } catch (IOException exception) {
+ throw new JSONException(exception);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/external/json/JSONString.java b/src/com/vaadin/external/json/JSONString.java
new file mode 100644
index 0000000000..cc7e4d8c07
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONString.java
@@ -0,0 +1,21 @@
+package com.vaadin.external.json;
+
+import java.io.Serializable;
+
+/**
+ * The <code>JSONString</code> interface allows a <code>toJSONString()</code>
+ * method so that a class can change the behavior of
+ * <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>, and
+ * <code>JSONWriter.value(</code>Object<code>)</code>. The
+ * <code>toJSONString</code> method will be used instead of the default behavior
+ * of using the Object's <code>toString()</code> method and quoting the result.
+ */
+public interface JSONString extends Serializable {
+ /**
+ * The <code>toJSONString</code> method allows a class to produce its own
+ * JSON serialization.
+ *
+ * @return A strictly syntactically correct JSON text.
+ */
+ public String toJSONString();
+}
diff --git a/src/com/vaadin/external/json/JSONStringer.java b/src/com/vaadin/external/json/JSONStringer.java
new file mode 100644
index 0000000000..e4ccc8e195
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONStringer.java
@@ -0,0 +1,83 @@
+package com.vaadin.external.json;
+
+/*
+ Copyright (c) 2006 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import java.io.StringWriter;
+
+/**
+ * JSONStringer provides a quick and convenient way of producing JSON text. The
+ * texts produced strictly conform to JSON syntax rules. No whitespace is added,
+ * so the results are ready for transmission or storage. Each instance of
+ * JSONStringer can produce one JSON text.
+ * <p>
+ * A JSONStringer instance provides a <code>value</code> method for appending
+ * values to the text, and a <code>key</code> method for adding keys before
+ * values in objects. There are <code>array</code> and <code>endArray</code>
+ * methods that make and bound array values, and <code>object</code> and
+ * <code>endObject</code> methods which make and bound object values. All of
+ * these methods return the JSONWriter instance, permitting cascade style. For
+ * example,
+ *
+ * <pre>
+ * myString = new JSONStringer().object().key(&quot;JSON&quot;).value(&quot;Hello, World!&quot;)
+ * .endObject().toString();
+ * </pre>
+ *
+ * which produces the string
+ *
+ * <pre>
+ * {"JSON":"Hello, World!"}
+ * </pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONStringer adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ *
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class JSONStringer extends JSONWriter {
+ /**
+ * Make a fresh JSONStringer. It can be used to build one JSON text.
+ */
+ public JSONStringer() {
+ super(new StringWriter());
+ }
+
+ /**
+ * Return the JSON text. This method is used to obtain the product of the
+ * JSONStringer instance. It will return <code>null</code> if there was a
+ * problem in the construction of the JSON text (such as the calls to
+ * <code>array</code> were not properly balanced with calls to
+ * <code>endArray</code>).
+ *
+ * @return The JSON text.
+ */
+ public String toString() {
+ return this.mode == 'd' ? this.writer.toString() : null;
+ }
+}
diff --git a/src/com/vaadin/external/json/JSONTokener.java b/src/com/vaadin/external/json/JSONTokener.java
new file mode 100644
index 0000000000..c3531cae1d
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONTokener.java
@@ -0,0 +1,451 @@
+package com.vaadin.external.json;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+
+/*
+ Copyright (c) 2002 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+/**
+ * A JSONTokener takes a source string and extracts characters and tokens from
+ * it. It is used by the JSONObject and JSONArray constructors to parse JSON
+ * source strings.
+ *
+ * @author JSON.org
+ * @version 2010-12-24
+ */
+public class JSONTokener implements Serializable {
+
+ private int character;
+ private boolean eof;
+ private int index;
+ private int line;
+ private char previous;
+ private Reader reader;
+ private boolean usePrevious;
+
+ /**
+ * Construct a JSONTokener from a Reader.
+ *
+ * @param reader
+ * A reader.
+ */
+ public JSONTokener(Reader reader) {
+ this.reader = reader.markSupported() ? reader : new BufferedReader(
+ reader);
+ eof = false;
+ usePrevious = false;
+ previous = 0;
+ index = 0;
+ character = 1;
+ line = 1;
+ }
+
+ /**
+ * Construct a JSONTokener from an InputStream.
+ */
+ public JSONTokener(InputStream inputStream) throws JSONException {
+ this(new InputStreamReader(inputStream));
+ }
+
+ /**
+ * Construct a JSONTokener from a string.
+ *
+ * @param s
+ * A source string.
+ */
+ public JSONTokener(String s) {
+ this(new StringReader(s));
+ }
+
+ /**
+ * Back up one character. This provides a sort of lookahead capability, so
+ * that you can test for a digit or letter before attempting to parse the
+ * next number or identifier.
+ */
+ public void back() throws JSONException {
+ if (usePrevious || index <= 0) {
+ throw new JSONException("Stepping back two steps is not supported");
+ }
+ index -= 1;
+ character -= 1;
+ usePrevious = true;
+ eof = false;
+ }
+
+ /**
+ * Get the hex value of a character (base16).
+ *
+ * @param c
+ * A character between '0' and '9' or between 'A' and 'F' or
+ * between 'a' and 'f'.
+ * @return An int between 0 and 15, or -1 if c was not a hex digit.
+ */
+ public static int dehexchar(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ if (c >= 'A' && c <= 'F') {
+ return c - ('A' - 10);
+ }
+ if (c >= 'a' && c <= 'f') {
+ return c - ('a' - 10);
+ }
+ return -1;
+ }
+
+ public boolean end() {
+ return eof && !usePrevious;
+ }
+
+ /**
+ * Determine if the source string still contains characters that next() can
+ * consume.
+ *
+ * @return true if not yet at the end of the source.
+ */
+ public boolean more() throws JSONException {
+ next();
+ if (end()) {
+ return false;
+ }
+ back();
+ return true;
+ }
+
+ /**
+ * Get the next character in the source string.
+ *
+ * @return The next character, or 0 if past the end of the source string.
+ */
+ public char next() throws JSONException {
+ int c;
+ if (usePrevious) {
+ usePrevious = false;
+ c = previous;
+ } else {
+ try {
+ c = reader.read();
+ } catch (IOException exception) {
+ throw new JSONException(exception);
+ }
+
+ if (c <= 0) { // End of stream
+ eof = true;
+ c = 0;
+ }
+ }
+ index += 1;
+ if (previous == '\r') {
+ line += 1;
+ character = c == '\n' ? 0 : 1;
+ } else if (c == '\n') {
+ line += 1;
+ character = 0;
+ } else {
+ character += 1;
+ }
+ previous = (char) c;
+ return previous;
+ }
+
+ /**
+ * Consume the next character, and check that it matches a specified
+ * character.
+ *
+ * @param c
+ * The character to match.
+ * @return The character.
+ * @throws JSONException
+ * if the character does not match.
+ */
+ public char next(char c) throws JSONException {
+ char n = next();
+ if (n != c) {
+ throw syntaxError("Expected '" + c + "' and instead saw '" + n
+ + "'");
+ }
+ return n;
+ }
+
+ /**
+ * Get the next n characters.
+ *
+ * @param n
+ * The number of characters to take.
+ * @return A string of n characters.
+ * @throws JSONException
+ * Substring bounds error if there are not n characters
+ * remaining in the source string.
+ */
+ public String next(int n) throws JSONException {
+ if (n == 0) {
+ return "";
+ }
+
+ char[] chars = new char[n];
+ int pos = 0;
+
+ while (pos < n) {
+ chars[pos] = next();
+ if (end()) {
+ throw syntaxError("Substring bounds error");
+ }
+ pos += 1;
+ }
+ return new String(chars);
+ }
+
+ /**
+ * Get the next char in the string, skipping whitespace.
+ *
+ * @throws JSONException
+ * @return A character, or 0 if there are no more characters.
+ */
+ public char nextClean() throws JSONException {
+ for (;;) {
+ char c = next();
+ if (c == 0 || c > ' ') {
+ return c;
+ }
+ }
+ }
+
+ /**
+ * Return the characters up to the next close quote character. Backslash
+ * processing is done. The formal JSON format does not allow strings in
+ * single quotes, but an implementation is allowed to accept them.
+ *
+ * @param quote
+ * The quoting character, either <code>"</code>
+ * &nbsp;<small>(double quote)</small> or <code>'</code>
+ * &nbsp;<small>(single quote)</small>.
+ * @return A String.
+ * @throws JSONException
+ * Unterminated string.
+ */
+ public String nextString(char quote) throws JSONException {
+ char c;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = next();
+ switch (c) {
+ case 0:
+ case '\n':
+ case '\r':
+ throw syntaxError("Unterminated string");
+ case '\\':
+ c = next();
+ switch (c) {
+ case 'b':
+ sb.append('\b');
+ break;
+ case 't':
+ sb.append('\t');
+ break;
+ case 'n':
+ sb.append('\n');
+ break;
+ case 'f':
+ sb.append('\f');
+ break;
+ case 'r':
+ sb.append('\r');
+ break;
+ case 'u':
+ sb.append((char) Integer.parseInt(next(4), 16));
+ break;
+ case '"':
+ case '\'':
+ case '\\':
+ case '/':
+ sb.append(c);
+ break;
+ default:
+ throw syntaxError("Illegal escape.");
+ }
+ break;
+ default:
+ if (c == quote) {
+ return sb.toString();
+ }
+ sb.append(c);
+ }
+ }
+ }
+
+ /**
+ * Get the text up but not including the specified character or the end of
+ * line, whichever comes first.
+ *
+ * @param delimiter
+ * A delimiter character.
+ * @return A string.
+ */
+ public String nextTo(char delimiter) throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ char c = next();
+ if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
+ if (c != 0) {
+ back();
+ }
+ return sb.toString().trim();
+ }
+ sb.append(c);
+ }
+ }
+
+ /**
+ * Get the text up but not including one of the specified delimiter
+ * characters or the end of line, whichever comes first.
+ *
+ * @param delimiters
+ * A set of delimiter characters.
+ * @return A string, trimmed.
+ */
+ public String nextTo(String delimiters) throws JSONException {
+ char c;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = next();
+ if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
+ if (c != 0) {
+ back();
+ }
+ return sb.toString().trim();
+ }
+ sb.append(c);
+ }
+ }
+
+ /**
+ * Get the next value. The value can be a Boolean, Double, Integer,
+ * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
+ *
+ * @throws JSONException
+ * If syntax error.
+ *
+ * @return An object.
+ */
+ public Object nextValue() throws JSONException {
+ char c = nextClean();
+ String string;
+
+ switch (c) {
+ case '"':
+ case '\'':
+ return nextString(c);
+ case '{':
+ back();
+ return new JSONObject(this);
+ case '[':
+ back();
+ return new JSONArray(this);
+ }
+
+ /*
+ * Handle unquoted text. This could be the values true, false, or null,
+ * or it can be a number. An implementation (such as this one) is
+ * allowed to also accept non-standard forms.
+ *
+ * Accumulate characters until we reach the end of the text or a
+ * formatting character.
+ */
+
+ StringBuffer sb = new StringBuffer();
+ while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+ sb.append(c);
+ c = next();
+ }
+ back();
+
+ string = sb.toString().trim();
+ if (string.equals("")) {
+ throw syntaxError("Missing value");
+ }
+ return JSONObject.stringToValue(string);
+ }
+
+ /**
+ * Skip characters until the next character is the requested character. If
+ * the requested character is not found, no characters are skipped.
+ *
+ * @param to
+ * A character to skip to.
+ * @return The requested character, or zero if the requested character is
+ * not found.
+ */
+ public char skipTo(char to) throws JSONException {
+ char c;
+ try {
+ int startIndex = index;
+ int startCharacter = character;
+ int startLine = line;
+ reader.mark(Integer.MAX_VALUE);
+ do {
+ c = next();
+ if (c == 0) {
+ reader.reset();
+ index = startIndex;
+ character = startCharacter;
+ line = startLine;
+ return c;
+ }
+ } while (c != to);
+ } catch (IOException exc) {
+ throw new JSONException(exc);
+ }
+
+ back();
+ return c;
+ }
+
+ /**
+ * Make a JSONException to signal a syntax error.
+ *
+ * @param message
+ * The error message.
+ * @return A JSONException object, suitable for throwing
+ */
+ public JSONException syntaxError(String message) {
+ return new JSONException(message + toString());
+ }
+
+ /**
+ * Make a printable string of this JSONTokener.
+ *
+ * @return " at {index} [character {character} line {line}]"
+ */
+ @Override
+ public String toString() {
+ return " at " + index + " [character " + character + " line " + line
+ + "]";
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/external/json/JSONWriter.java b/src/com/vaadin/external/json/JSONWriter.java
new file mode 100644
index 0000000000..5f9ddeeae2
--- /dev/null
+++ b/src/com/vaadin/external/json/JSONWriter.java
@@ -0,0 +1,355 @@
+package com.vaadin.external.json;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+
+/*
+ Copyright (c) 2006 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+/**
+ * JSONWriter provides a quick and convenient way of producing JSON text. The
+ * texts produced strictly conform to JSON syntax rules. No whitespace is added,
+ * so the results are ready for transmission or storage. Each instance of
+ * JSONWriter can produce one JSON text.
+ * <p>
+ * A JSONWriter instance provides a <code>value</code> method for appending
+ * values to the text, and a <code>key</code> method for adding keys before
+ * values in objects. There are <code>array</code> and <code>endArray</code>
+ * methods that make and bound array values, and <code>object</code> and
+ * <code>endObject</code> methods which make and bound object values. All of
+ * these methods return the JSONWriter instance, permitting a cascade style. For
+ * example,
+ *
+ * <pre>
+ * new JSONWriter(myWriter).object().key(&quot;JSON&quot;).value(&quot;Hello, World!&quot;)
+ * .endObject();
+ * </pre>
+ *
+ * which writes
+ *
+ * <pre>
+ * {"JSON":"Hello, World!"}
+ * </pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONWriter adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ *
+ * @author JSON.org
+ * @version 2011-11-14
+ */
+public class JSONWriter implements Serializable {
+ private static final int maxdepth = 200;
+
+ /**
+ * The comma flag determines if a comma should be output before the next
+ * value.
+ */
+ private boolean comma;
+
+ /**
+ * The current mode. Values: 'a' (array), 'd' (done), 'i' (initial), 'k'
+ * (key), 'o' (object).
+ */
+ protected char mode;
+
+ /**
+ * The object/array stack.
+ */
+ private final JSONObject stack[];
+
+ /**
+ * The stack top index. A value of 0 indicates that the stack is empty.
+ */
+ private int top;
+
+ /**
+ * The writer that will receive the output.
+ */
+ protected Writer writer;
+
+ /**
+ * Make a fresh JSONWriter. It can be used to build one JSON text.
+ */
+ public JSONWriter(Writer w) {
+ comma = false;
+ mode = 'i';
+ stack = new JSONObject[maxdepth];
+ top = 0;
+ writer = w;
+ }
+
+ /**
+ * Append a value.
+ *
+ * @param string
+ * A string value.
+ * @return this
+ * @throws JSONException
+ * If the value is out of sequence.
+ */
+ private JSONWriter append(String string) throws JSONException {
+ if (string == null) {
+ throw new JSONException("Null pointer");
+ }
+ if (mode == 'o' || mode == 'a') {
+ try {
+ if (comma && mode == 'a') {
+ writer.write(',');
+ }
+ writer.write(string);
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ if (mode == 'o') {
+ mode = 'k';
+ }
+ comma = true;
+ return this;
+ }
+ throw new JSONException("Value out of sequence.");
+ }
+
+ /**
+ * Begin appending a new array. All values until the balancing
+ * <code>endArray</code> will be appended to this array. The
+ * <code>endArray</code> method must be called to mark the array's end.
+ *
+ * @return this
+ * @throws JSONException
+ * If the nesting is too deep, or if the object is started in
+ * the wrong place (for example as a key or after the end of the
+ * outermost array or object).
+ */
+ public JSONWriter array() throws JSONException {
+ if (mode == 'i' || mode == 'o' || mode == 'a') {
+ push(null);
+ append("[");
+ comma = false;
+ return this;
+ }
+ throw new JSONException("Misplaced array.");
+ }
+
+ /**
+ * End something.
+ *
+ * @param mode
+ * Mode
+ * @param c
+ * Closing character
+ * @return this
+ * @throws JSONException
+ * If unbalanced.
+ */
+ private JSONWriter end(char mode, char c) throws JSONException {
+ if (this.mode != mode) {
+ throw new JSONException(mode == 'a' ? "Misplaced endArray."
+ : "Misplaced endObject.");
+ }
+ pop(mode);
+ try {
+ writer.write(c);
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ comma = true;
+ return this;
+ }
+
+ /**
+ * End an array. This method most be called to balance calls to
+ * <code>array</code>.
+ *
+ * @return this
+ * @throws JSONException
+ * If incorrectly nested.
+ */
+ public JSONWriter endArray() throws JSONException {
+ return end('a', ']');
+ }
+
+ /**
+ * End an object. This method most be called to balance calls to
+ * <code>object</code>.
+ *
+ * @return this
+ * @throws JSONException
+ * If incorrectly nested.
+ */
+ public JSONWriter endObject() throws JSONException {
+ return end('k', '}');
+ }
+
+ /**
+ * Append a key. The key will be associated with the next value. In an
+ * object, every value must be preceded by a key.
+ *
+ * @param string
+ * A key string.
+ * @return this
+ * @throws JSONException
+ * If the key is out of place. For example, keys do not belong
+ * in arrays or if the key is null.
+ */
+ public JSONWriter key(String string) throws JSONException {
+ if (string == null) {
+ throw new JSONException("Null key.");
+ }
+ if (mode == 'k') {
+ try {
+ stack[top - 1].putOnce(string, Boolean.TRUE);
+ if (comma) {
+ writer.write(',');
+ }
+ writer.write(JSONObject.quote(string));
+ writer.write(':');
+ comma = false;
+ mode = 'o';
+ return this;
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ }
+ throw new JSONException("Misplaced key.");
+ }
+
+ /**
+ * Begin appending a new object. All keys and values until the balancing
+ * <code>endObject</code> will be appended to this object. The
+ * <code>endObject</code> method must be called to mark the object's end.
+ *
+ * @return this
+ * @throws JSONException
+ * If the nesting is too deep, or if the object is started in
+ * the wrong place (for example as a key or after the end of the
+ * outermost array or object).
+ */
+ public JSONWriter object() throws JSONException {
+ if (mode == 'i') {
+ mode = 'o';
+ }
+ if (mode == 'o' || mode == 'a') {
+ append("{");
+ push(new JSONObject());
+ comma = false;
+ return this;
+ }
+ throw new JSONException("Misplaced object.");
+
+ }
+
+ /**
+ * Pop an array or object scope.
+ *
+ * @param c
+ * The scope to close.
+ * @throws JSONException
+ * If nesting is wrong.
+ */
+ private void pop(char c) throws JSONException {
+ if (top <= 0) {
+ throw new JSONException("Nesting error.");
+ }
+ char m = stack[top - 1] == null ? 'a' : 'k';
+ if (m != c) {
+ throw new JSONException("Nesting error.");
+ }
+ top -= 1;
+ mode = top == 0 ? 'd' : stack[top - 1] == null ? 'a' : 'k';
+ }
+
+ /**
+ * Push an array or object scope.
+ *
+ * @param c
+ * The scope to open.
+ * @throws JSONException
+ * If nesting is too deep.
+ */
+ private void push(JSONObject jo) throws JSONException {
+ if (top >= maxdepth) {
+ throw new JSONException("Nesting too deep.");
+ }
+ stack[top] = jo;
+ mode = jo == null ? 'a' : 'k';
+ top += 1;
+ }
+
+ /**
+ * Append either the value <code>true</code> or the value <code>false</code>
+ * .
+ *
+ * @param b
+ * A boolean.
+ * @return this
+ * @throws JSONException
+ */
+ public JSONWriter value(boolean b) throws JSONException {
+ return append(b ? "true" : "false");
+ }
+
+ /**
+ * Append a double value.
+ *
+ * @param d
+ * A double.
+ * @return this
+ * @throws JSONException
+ * If the number is not finite.
+ */
+ public JSONWriter value(double d) throws JSONException {
+ return this.value(new Double(d));
+ }
+
+ /**
+ * Append a long value.
+ *
+ * @param l
+ * A long.
+ * @return this
+ * @throws JSONException
+ */
+ public JSONWriter value(long l) throws JSONException {
+ return append(Long.toString(l));
+ }
+
+ /**
+ * Append an object value.
+ *
+ * @param object
+ * The object to append. It can be null, or a Boolean, Number,
+ * String, JSONObject, or JSONArray, or an object that implements
+ * JSONString.
+ * @return this
+ * @throws JSONException
+ * If the value is out of sequence.
+ */
+ public JSONWriter value(Object object) throws JSONException {
+ return append(JSONObject.valueToString(object));
+ }
+}
diff --git a/src/com/vaadin/external/json/README b/src/com/vaadin/external/json/README
new file mode 100644
index 0000000000..ca6dc11764
--- /dev/null
+++ b/src/com/vaadin/external/json/README
@@ -0,0 +1,68 @@
+JSON in Java [package org.json]
+
+Douglas Crockford
+douglas@crockford.com
+
+2011-02-02
+
+
+JSON is a light-weight, language independent, data interchange format.
+See http://www.JSON.org/
+
+The files in this package implement JSON encoders/decoders in Java.
+It also includes the capability to convert between JSON and XML, HTTP
+headers, Cookies, and CDL.
+
+This is a reference implementation. There is a large number of JSON packages
+in Java. Perhaps someday the Java community will standardize on one. Until
+then, choose carefully.
+
+The license includes this restriction: "The software shall be used for good,
+not evil." If your conscience cannot live with that, then choose a different
+package.
+
+The package compiles on Java 1.2 thru Java 1.4.
+
+
+JSONObject.java: The JSONObject can parse text from a String or a JSONTokener
+to produce a map-like object. The object provides methods for manipulating its
+contents, and for producing a JSON compliant object serialization.
+
+JSONArray.java: The JSONObject can parse text from a String or a JSONTokener
+to produce a vector-like object. The object provides methods for manipulating
+its contents, and for producing a JSON compliant array serialization.
+
+JSONTokener.java: The JSONTokener breaks a text into a sequence of individual
+tokens. It can be constructed from a String, Reader, or InputStream.
+
+JSONException.java: The JSONException is the standard exception type thrown
+by this package.
+
+
+JSONString.java: The JSONString interface requires a toJSONString method,
+allowing an object to provide its own serialization.
+
+JSONStringer.java: The JSONStringer provides a convenient facility for
+building JSON strings.
+
+JSONWriter.java: The JSONWriter provides a convenient facility for building
+JSON text through a writer.
+
+
+CDL.java: CDL provides support for converting between JSON and comma
+delimited lists.
+
+Cookie.java: Cookie provides support for converting between JSON and cookies.
+
+CookieList.java: CookieList provides support for converting between JSON and
+cookie lists.
+
+HTTP.java: HTTP provides support for converting between JSON and HTTP headers.
+
+HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers.
+
+XML.java: XML provides support for converting between JSON and XML.
+
+JSONML.java: JSONML provides support for converting between JSONML and XML.
+
+XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text. \ No newline at end of file
diff --git a/src/com/vaadin/terminal/AbstractErrorMessage.java b/src/com/vaadin/terminal/AbstractErrorMessage.java
new file mode 100644
index 0000000000..1a625fc0e6
--- /dev/null
+++ b/src/com/vaadin/terminal/AbstractErrorMessage.java
@@ -0,0 +1,169 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vaadin.data.Buffered;
+import com.vaadin.data.Validator;
+import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+
+/**
+ * Base class for component error messages.
+ *
+ * This class is used on the server side to construct the error messages to send
+ * to the client.
+ *
+ * @since 7.0
+ */
+public abstract class AbstractErrorMessage implements ErrorMessage {
+
+ public enum ContentMode {
+ /**
+ * Content mode, where the error contains only plain text.
+ */
+ TEXT,
+ /**
+ * Content mode, where the error contains preformatted text.
+ */
+ PREFORMATTED,
+ /**
+ * Content mode, where the error contains XHTML.
+ */
+ XHTML;
+ }
+
+ /**
+ * Content mode.
+ */
+ private ContentMode mode = ContentMode.TEXT;
+
+ /**
+ * Message in content mode.
+ */
+ private String message;
+
+ /**
+ * Error level.
+ */
+ private ErrorLevel level = ErrorLevel.ERROR;
+
+ private List<ErrorMessage> causes = new ArrayList<ErrorMessage>();
+
+ protected AbstractErrorMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ protected void setMessage(String message) {
+ this.message = message;
+ }
+
+ /* Documented in interface */
+ public ErrorLevel getErrorLevel() {
+ return level;
+ }
+
+ protected void setErrorLevel(ErrorLevel level) {
+ this.level = level;
+ }
+
+ protected ContentMode getMode() {
+ return mode;
+ }
+
+ protected void setMode(ContentMode mode) {
+ this.mode = mode;
+ }
+
+ protected List<ErrorMessage> getCauses() {
+ return causes;
+ }
+
+ protected void addCause(ErrorMessage cause) {
+ causes.add(cause);
+ }
+
+ public String getFormattedHtmlMessage() {
+ String result = null;
+ switch (getMode()) {
+ case TEXT:
+ result = AbstractApplicationServlet.safeEscapeForHtml(getMessage());
+ break;
+ case PREFORMATTED:
+ result = "<pre>"
+ + AbstractApplicationServlet
+ .safeEscapeForHtml(getMessage()) + "</pre>";
+ break;
+ case XHTML:
+ result = getMessage();
+ break;
+ }
+ // if no message, combine the messages of all children
+ if (null == result && null != getCauses() && getCauses().size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ for (ErrorMessage cause : getCauses()) {
+ String childMessage = cause.getFormattedHtmlMessage();
+ if (null != childMessage) {
+ sb.append("<div>");
+ sb.append(childMessage);
+ sb.append("</div>\n");
+ }
+ }
+ if (sb.length() > 0) {
+ result = sb.toString();
+ }
+ }
+ // still no message? use an empty string for backwards compatibility
+ if (null == result) {
+ result = "";
+ }
+ return result;
+ }
+
+ // TODO replace this with a helper method elsewhere?
+ public static ErrorMessage getErrorMessageForException(Throwable t) {
+ if (null == t) {
+ return null;
+ } else if (t instanceof ErrorMessage) {
+ // legacy case for custom error messages
+ return (ErrorMessage) t;
+ } else if (t instanceof Validator.InvalidValueException) {
+ UserError error = new UserError(
+ ((Validator.InvalidValueException) t).getHtmlMessage(),
+ ContentMode.XHTML, ErrorLevel.ERROR);
+ for (Validator.InvalidValueException nestedException : ((Validator.InvalidValueException) t)
+ .getCauses()) {
+ error.addCause(getErrorMessageForException(nestedException));
+ }
+ return error;
+ } else if (t instanceof Buffered.SourceException) {
+ // no message, only the causes to be painted
+ UserError error = new UserError(null);
+ // in practice, this was always ERROR in Vaadin 6 unless tweaked in
+ // custom exceptions implementing ErrorMessage
+ error.setErrorLevel(ErrorLevel.ERROR);
+ // causes
+ for (Throwable nestedException : ((Buffered.SourceException) t)
+ .getCauses()) {
+ error.addCause(getErrorMessageForException(nestedException));
+ }
+ return error;
+ } else {
+ return new SystemError(t);
+ }
+ }
+
+ /* Documented in superclass */
+ @Override
+ public String toString() {
+ return getMessage();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/CombinedRequest.java b/src/com/vaadin/terminal/CombinedRequest.java
new file mode 100644
index 0000000000..ccef6d8963
--- /dev/null
+++ b/src/com/vaadin/terminal/CombinedRequest.java
@@ -0,0 +1,167 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import com.vaadin.Application;
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.gwt.server.WebApplicationContext;
+import com.vaadin.terminal.gwt.server.WebBrowser;
+
+/**
+ * A {@link WrappedRequest} with path and parameters from one request and
+ * {@link WrappedRequest.BrowserDetails} extracted from another request.
+ *
+ * This class is intended to be used for a two request initialization where the
+ * first request fetches the actual application page and the second request
+ * contains information extracted from the browser using javascript.
+ *
+ */
+public class CombinedRequest implements WrappedRequest {
+
+ private final WrappedRequest secondRequest;
+ private Map<String, String[]> parameterMap;
+
+ /**
+ * Creates a new combined request based on the second request and some
+ * details from the first request.
+ *
+ * @param secondRequest
+ * the second request which will be used as the foundation of the
+ * combined request
+ * @throws JSONException
+ * if the initialParams parameter can not be decoded
+ */
+ public CombinedRequest(WrappedRequest secondRequest) throws JSONException {
+ this.secondRequest = secondRequest;
+
+ HashMap<String, String[]> map = new HashMap<String, String[]>();
+ JSONObject initialParams = new JSONObject(
+ secondRequest.getParameter("initialParams"));
+ for (Iterator<?> keys = initialParams.keys(); keys.hasNext();) {
+ String name = (String) keys.next();
+ JSONArray jsonValues = initialParams.getJSONArray(name);
+ String[] values = new String[jsonValues.length()];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = jsonValues.getString(i);
+ }
+ map.put(name, values);
+ }
+
+ parameterMap = Collections.unmodifiableMap(map);
+
+ }
+
+ public String getParameter(String parameter) {
+ String[] strings = getParameterMap().get(parameter);
+ if (strings == null || strings.length == 0) {
+ return null;
+ } else {
+ return strings[0];
+ }
+ }
+
+ public Map<String, String[]> getParameterMap() {
+ return parameterMap;
+ }
+
+ public int getContentLength() {
+ return secondRequest.getContentLength();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return secondRequest.getInputStream();
+ }
+
+ public Object getAttribute(String name) {
+ return secondRequest.getAttribute(name);
+ }
+
+ public void setAttribute(String name, Object value) {
+ secondRequest.setAttribute(name, value);
+ }
+
+ public String getRequestPathInfo() {
+ return secondRequest.getParameter("initialPath");
+ }
+
+ public int getSessionMaxInactiveInterval() {
+ return secondRequest.getSessionMaxInactiveInterval();
+ }
+
+ public Object getSessionAttribute(String name) {
+ return secondRequest.getSessionAttribute(name);
+ }
+
+ public void setSessionAttribute(String name, Object attribute) {
+ secondRequest.setSessionAttribute(name, attribute);
+ }
+
+ public String getContentType() {
+ return secondRequest.getContentType();
+ }
+
+ public BrowserDetails getBrowserDetails() {
+ return new BrowserDetails() {
+ public String getUriFragment() {
+ String fragment = secondRequest.getParameter("fr");
+ if (fragment == null) {
+ return "";
+ } else {
+ return fragment;
+ }
+ }
+
+ public String getWindowName() {
+ return secondRequest.getParameter("wn");
+ }
+
+ public WebBrowser getWebBrowser() {
+ WebApplicationContext context = (WebApplicationContext) Application
+ .getCurrentApplication().getContext();
+ return context.getBrowser();
+ }
+ };
+ }
+
+ /**
+ * Gets the original second request. This can be used e.g. if a request
+ * parameter from the second request is required.
+ *
+ * @return the original second wrapped request
+ */
+ public WrappedRequest getSecondRequest() {
+ return secondRequest;
+ }
+
+ public Locale getLocale() {
+ return secondRequest.getLocale();
+ }
+
+ public String getRemoteAddr() {
+ return secondRequest.getRemoteAddr();
+ }
+
+ public boolean isSecure() {
+ return secondRequest.isSecure();
+ }
+
+ public String getHeader(String name) {
+ return secondRequest.getHeader(name);
+ }
+
+ public DeploymentConfiguration getDeploymentConfiguration() {
+ return secondRequest.getDeploymentConfiguration();
+ }
+}
diff --git a/src/com/vaadin/terminal/CompositeErrorMessage.java b/src/com/vaadin/terminal/CompositeErrorMessage.java
index aae231739e..b82b622f54 100644
--- a/src/com/vaadin/terminal/CompositeErrorMessage.java
+++ b/src/com/vaadin/terminal/CompositeErrorMessage.java
@@ -4,11 +4,8 @@
package com.vaadin.terminal;
-import java.io.Serializable;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
/**
* Class for combining multiple error messages together.
@@ -19,17 +16,7 @@ import java.util.List;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class CompositeErrorMessage implements ErrorMessage, Serializable {
-
- /**
- * Array of all the errors.
- */
- private final List<ErrorMessage> errors;
-
- /**
- * Level of the error.
- */
- private int level;
+public class CompositeErrorMessage extends AbstractErrorMessage {
/**
* Constructor for CompositeErrorMessage.
@@ -39,14 +26,14 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
* ignored, but at least one message is required.
*/
public CompositeErrorMessage(ErrorMessage[] errorMessages) {
- errors = new ArrayList<ErrorMessage>(errorMessages.length);
- level = Integer.MIN_VALUE;
+ super(null);
+ setErrorLevel(ErrorLevel.INFORMATION);
for (int i = 0; i < errorMessages.length; i++) {
addErrorMessage(errorMessages[i]);
}
- if (errors.size() == 0) {
+ if (getCauses().size() == 0) {
throw new IllegalArgumentException(
"Composite error message must have at least one error");
}
@@ -62,30 +49,21 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
*/
public CompositeErrorMessage(
Collection<? extends ErrorMessage> errorMessages) {
- errors = new ArrayList<ErrorMessage>(errorMessages.size());
- level = Integer.MIN_VALUE;
+ super(null);
+ setErrorLevel(ErrorLevel.INFORMATION);
for (final Iterator<? extends ErrorMessage> i = errorMessages
.iterator(); i.hasNext();) {
addErrorMessage(i.next());
}
- if (errors.size() == 0) {
+ if (getCauses().size() == 0) {
throw new IllegalArgumentException(
"Composite error message must have at least one error");
}
}
/**
- * The error level is the largest error level in
- *
- * @see com.vaadin.terminal.ErrorMessage#getErrorLevel()
- */
- public final int getErrorLevel() {
- return level;
- }
-
- /**
* Adds a error message into this composite message. Updates the level
* field.
*
@@ -93,11 +71,10 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
* the error message to be added. Duplicate errors are ignored.
*/
private void addErrorMessage(ErrorMessage error) {
- if (error != null && !errors.contains(error)) {
- errors.add(error);
- final int l = error.getErrorLevel();
- if (l > level) {
- level = l;
+ if (error != null && !getCauses().contains(error)) {
+ addCause(error);
+ if (error.getErrorLevel().intValue() > getErrorLevel().intValue()) {
+ setErrorLevel(error.getErrorLevel());
}
}
}
@@ -108,55 +85,7 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
* @return the error iterator.
*/
public Iterator<ErrorMessage> iterator() {
- return errors.iterator();
- }
-
- /**
- * @see com.vaadin.terminal.Paintable#paint(com.vaadin.terminal.PaintTarget)
- */
- public void paint(PaintTarget target) throws PaintException {
-
- if (errors.size() == 1) {
- (errors.iterator().next()).paint(target);
- } else {
- target.startTag("error");
-
- if (level > 0 && level <= ErrorMessage.INFORMATION) {
- target.addAttribute("level", "info");
- } else if (level <= ErrorMessage.WARNING) {
- target.addAttribute("level", "warning");
- } else if (level <= ErrorMessage.ERROR) {
- target.addAttribute("level", "error");
- } else if (level <= ErrorMessage.CRITICAL) {
- target.addAttribute("level", "critical");
- } else {
- target.addAttribute("level", "system");
- }
-
- // Paint all the exceptions
- for (final Iterator<ErrorMessage> i = errors.iterator(); i
- .hasNext();) {
- i.next().paint(target);
- }
-
- target.endTag("error");
- }
- }
-
- /* Documented in super interface */
- public void addListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void removeListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void requestRepaint() {
- }
-
- /* Documented in super interface */
- public void requestRepaintRequests() {
+ return getCauses().iterator();
}
/**
@@ -168,7 +97,8 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
public String toString() {
String retval = "[";
int pos = 0;
- for (final Iterator<ErrorMessage> i = errors.iterator(); i.hasNext();) {
+ for (final Iterator<ErrorMessage> i = getCauses().iterator(); i
+ .hasNext();) {
if (pos > 0) {
retval += ",";
}
@@ -179,13 +109,4 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
return retval;
}
-
- public String getDebugId() {
- return null;
- }
-
- public void setDebugId(String id) {
- throw new UnsupportedOperationException(
- "Setting testing id for this Paintable is not implemented");
- }
}
diff --git a/src/com/vaadin/terminal/DeploymentConfiguration.java b/src/com/vaadin/terminal/DeploymentConfiguration.java
new file mode 100644
index 0000000000..02a3f0200f
--- /dev/null
+++ b/src/com/vaadin/terminal/DeploymentConfiguration.java
@@ -0,0 +1,86 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.Serializable;
+
+/**
+ * Provide deployment specific settings that are required outside terminal
+ * specific code.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @since 7.0
+ */
+public interface DeploymentConfiguration extends Serializable {
+
+ /**
+ * Gets the base URL of the location of Vaadin's static files.
+ *
+ * @param request
+ * the request for which the location should be determined
+ *
+ * @return a string with the base URL for static files
+ */
+ public String getStaticFileLocation(WrappedRequest request);
+
+ /**
+ * Gets the widgetset that is configured for this deployment, e.g. from a
+ * parameter in web.xml.
+ *
+ * @param request
+ * the request for which a widgetset is required
+ * @return the name of the widgetset
+ */
+ public String getConfiguredWidgetset(WrappedRequest request);
+
+ /**
+ * Gets the theme that is configured for this deployment, e.g. from a portal
+ * parameter or just some sensible default value.
+ *
+ * @param request
+ * the request for which a theme is required
+ * @return the name of the theme
+ */
+ public String getConfiguredTheme(WrappedRequest request);
+
+ /**
+ * Checks whether the Vaadin application will be rendered on its own in the
+ * browser or whether it will be included into some other context. A
+ * standalone application may do things that might interfere with other
+ * parts of a page, e.g. changing the page title and requesting focus upon
+ * loading.
+ *
+ * @param request
+ * the request for which the application is loaded
+ * @return a boolean indicating whether the application should be standalone
+ */
+ public boolean isStandalone(WrappedRequest request);
+
+ /**
+ * Gets a configured property. The properties are typically read from e.g.
+ * web.xml or from system properties of the JVM.
+ *
+ * @param propertyName
+ * The simple of the property, in some contexts, lookup might be
+ * performed using variations of the provided name.
+ * @param defaultValue
+ * the default value that should be used if no value has been
+ * defined
+ * @return the property value, or the passed default value if no property
+ * value is found
+ */
+ public String getApplicationOrSystemProperty(String propertyName,
+ String defaultValue);
+
+ /**
+ * Get the class loader to use for loading classes loaded by name, e.g.
+ * custom Root classes. <code>null</code> indicates that the default class
+ * loader should be used.
+ *
+ * @return the class loader to use, or <code>null</code>
+ */
+ public ClassLoader getClassLoader();
+}
diff --git a/src/com/vaadin/terminal/DownloadStream.java b/src/com/vaadin/terminal/DownloadStream.java
index 2db2a1f20d..9853b0eee2 100644
--- a/src/com/vaadin/terminal/DownloadStream.java
+++ b/src/com/vaadin/terminal/DownloadStream.java
@@ -4,12 +4,18 @@
package com.vaadin.terminal;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+
+import com.vaadin.terminal.gwt.server.Constants;
+
/**
* Downloadable stream.
*
@@ -198,9 +204,132 @@ public class DownloadStream implements Serializable {
*
* @param bufferSize
* the size of the buffer in bytes.
+ *
+ * @since 7.0
*/
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
+ /**
+ * Writes this download stream to a wrapped response. This takes care of
+ * setting response headers according to what is defined in this download
+ * stream ({@link #getContentType()}, {@link #getCacheTime()},
+ * {@link #getFileName()}) and transferring the data from the stream (
+ * {@link #getStream()}) to the response. Defined parameters (
+ * {@link #getParameterNames()}) are also included as headers in the
+ * response. If there's is a parameter named <code>Location</code>, a
+ * redirect (302 Moved temporarily) is sent instead of the contents of this
+ * stream.
+ *
+ * @param response
+ * the wrapped response to write this download stream to
+ * @throws IOException
+ * passed through from the wrapped response
+ *
+ * @since 7.0
+ */
+ public void writeTo(WrappedResponse response) throws IOException {
+ if (getParameter("Location") != null) {
+ response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ response.setHeader("Location", getParameter("Location"));
+ return;
+ }
+
+ // Download from given stream
+ final InputStream data = getStream();
+ if (data != null) {
+
+ OutputStream out = null;
+ try {
+ // Sets content type
+ response.setContentType(getContentType());
+
+ // Sets cache headers
+ response.setCacheTime(getCacheTime());
+
+ // Copy download stream parameters directly
+ // to HTTP headers.
+ final Iterator<String> i = getParameterNames();
+ if (i != null) {
+ while (i.hasNext()) {
+ final String param = i.next();
+ response.setHeader(param, getParameter(param));
+ }
+ }
+
+ // suggest local filename from DownloadStream if
+ // Content-Disposition
+ // not explicitly set
+ String contentDispositionValue = getParameter("Content-Disposition");
+ if (contentDispositionValue == null) {
+ contentDispositionValue = "filename=\"" + getFileName()
+ + "\"";
+ response.setHeader("Content-Disposition",
+ contentDispositionValue);
+ }
+
+ int bufferSize = getBufferSize();
+ if (bufferSize <= 0 || bufferSize > Constants.MAX_BUFFER_SIZE) {
+ bufferSize = Constants.DEFAULT_BUFFER_SIZE;
+ }
+ final byte[] buffer = new byte[bufferSize];
+ int bytesRead = 0;
+
+ out = response.getOutputStream();
+
+ long totalWritten = 0;
+ while ((bytesRead = data.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+
+ totalWritten += bytesRead;
+ if (totalWritten >= buffer.length) {
+ // Avoid chunked encoding for small resources
+ out.flush();
+ }
+ }
+ } finally {
+ tryToCloseStream(out);
+ tryToCloseStream(data);
+ }
+ }
+ }
+
+ /**
+ * Helper method that tries to close an output stream and ignores any
+ * exceptions.
+ *
+ * @param out
+ * the output stream to close, <code>null</code> is also
+ * supported
+ */
+ static void tryToCloseStream(OutputStream out) {
+ try {
+ // try to close output stream (e.g. file handle)
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e1) {
+ // NOP
+ }
+ }
+
+ /**
+ * Helper method that tries to close an input stream and ignores any
+ * exceptions.
+ *
+ * @param in
+ * the input stream to close, <code>null</code> is also supported
+ */
+ static void tryToCloseStream(InputStream in) {
+ try {
+ // try to close output stream (e.g. file handle)
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e1) {
+ // NOP
+ }
+ }
+
}
diff --git a/src/com/vaadin/terminal/ErrorMessage.java b/src/com/vaadin/terminal/ErrorMessage.java
index b3be407743..60a0780a72 100644
--- a/src/com/vaadin/terminal/ErrorMessage.java
+++ b/src/com/vaadin/terminal/ErrorMessage.java
@@ -15,66 +15,112 @@ import java.io.Serializable;
* @VERSION@
* @since 3.0
*/
-public interface ErrorMessage extends Paintable, Serializable {
+public interface ErrorMessage extends Serializable {
+
+ public enum ErrorLevel {
+ /**
+ * Error code for informational messages.
+ */
+ INFORMATION("info", 0),
+ /**
+ * Error code for warning messages.
+ */
+ WARNING("warning", 1),
+ /**
+ * Error code for regular error messages.
+ */
+ ERROR("error", 2),
+ /**
+ * Error code for critical error messages.
+ */
+ CRITICAL("critical", 3),
+ /**
+ * Error code for system errors and bugs.
+ */
+ SYSTEMERROR("system", 4);
+
+ String text;
+ int errorLevel;
+
+ private ErrorLevel(String text, int errorLevel) {
+ this.text = text;
+ this.errorLevel = errorLevel;
+ }
+
+ /**
+ * Textual representation for server-client communication of level
+ *
+ * @return String for error severity
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Integer representation of error severity for comparison
+ *
+ * @return integer for error severity
+ */
+ public int intValue() {
+ return errorLevel;
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+
+ }
/**
- * Error code for system errors and bugs.
+ * @deprecated from 7.0, use {@link ErrorLevel#SYSTEMERROR} instead    
*/
- public static final int SYSTEMERROR = 5000;
+ @Deprecated
+ public static final ErrorLevel SYSTEMERROR = ErrorLevel.SYSTEMERROR;
/**
- * Error code for critical error messages.
+ * @deprecated from 7.0, use {@link ErrorLevel#CRITICAL} instead    
*/
- public static final int CRITICAL = 4000;
+ @Deprecated
+ public static final ErrorLevel CRITICAL = ErrorLevel.CRITICAL;
/**
- * Error code for regular error messages.
+ * @deprecated from 7.0, use {@link ErrorLevel#ERROR} instead    
*/
- public static final int ERROR = 3000;
+
+ @Deprecated
+ public static final ErrorLevel ERROR = ErrorLevel.ERROR;
/**
- * Error code for warning messages.
+ * @deprecated from 7.0, use {@link ErrorLevel#WARNING} instead    
*/
- public static final int WARNING = 2000;
+ @Deprecated
+ public static final ErrorLevel WARNING = ErrorLevel.WARNING;
/**
- * Error code for informational messages.
+ * @deprecated from 7.0, use {@link ErrorLevel#INFORMATION} instead    
*/
- public static final int INFORMATION = 1000;
+ @Deprecated
+ public static final ErrorLevel INFORMATION = ErrorLevel.INFORMATION;
/**
* Gets the errors level.
*
* @return the level of error as an integer.
*/
- public int getErrorLevel();
+ public ErrorLevel getErrorLevel();
/**
- * Error messages are inmodifiable and thus listeners are not needed. This
- * method should be implemented as empty.
+ * Returns the HTML formatted message to show in as the error message on the
+ * client.
*
- * @param listener
- * the listener to be added.
- * @see com.vaadin.terminal.Paintable#addListener(Paintable.RepaintRequestListener)
- */
- public void addListener(RepaintRequestListener listener);
-
- /**
- * Error messages are inmodifiable and thus listeners are not needed. This
- * method should be implemented as empty.
+ * This method should perform any necessary escaping to avoid XSS attacks.
*
- * @param listener
- * the listener to be removed.
- * @see com.vaadin.terminal.Paintable#removeListener(Paintable.RepaintRequestListener)
- */
- public void removeListener(RepaintRequestListener listener);
-
- /**
- * Error messages are inmodifiable and thus listeners are not needed. This
- * method should be implemented as empty.
+ * TODO this API may still change to use a separate data transfer object
*
- * @see com.vaadin.terminal.Paintable#requestRepaint()
+ * @return HTML formatted string for the error message
+ * @since 7.0
*/
- public void requestRepaint();
+ public String getFormattedHtmlMessage();
}
diff --git a/src/com/vaadin/terminal/KeyMapper.java b/src/com/vaadin/terminal/KeyMapper.java
index d44cd0de3a..3f19692ef1 100644
--- a/src/com/vaadin/terminal/KeyMapper.java
+++ b/src/com/vaadin/terminal/KeyMapper.java
@@ -5,7 +5,7 @@
package com.vaadin.terminal;
import java.io.Serializable;
-import java.util.Hashtable;
+import java.util.HashMap;
/**
* <code>KeyMapper</code> is the simple two-way map for generating textual keys
@@ -16,14 +16,13 @@ import java.util.Hashtable;
* @VERSION@
* @since 3.0
*/
-@SuppressWarnings("serial")
-public class KeyMapper implements Serializable {
+public class KeyMapper<V> implements Serializable {
private int lastKey = 0;
- private final Hashtable<Object, String> objectKeyMap = new Hashtable<Object, String>();
+ private final HashMap<V, String> objectKeyMap = new HashMap<V, String>();
- private final Hashtable<String, Object> keyObjectMap = new Hashtable<String, Object>();
+ private final HashMap<String, V> keyObjectMap = new HashMap<String, V>();
/**
* Gets key for an object.
@@ -31,7 +30,7 @@ public class KeyMapper implements Serializable {
* @param o
* the object.
*/
- public String key(Object o) {
+ public String key(V o) {
if (o == null) {
return "null";
@@ -58,8 +57,7 @@ public class KeyMapper implements Serializable {
* the name with the desired value.
* @return the object with the key.
*/
- public Object get(String key) {
-
+ public V get(String key) {
return keyObjectMap.get(key);
}
@@ -69,7 +67,7 @@ public class KeyMapper implements Serializable {
* @param removeobj
* the object to be removed.
*/
- public void remove(Object removeobj) {
+ public void remove(V removeobj) {
final String key = objectKeyMap.get(removeobj);
if (key != null) {
diff --git a/src/com/vaadin/terminal/LegacyPaint.java b/src/com/vaadin/terminal/LegacyPaint.java
new file mode 100644
index 0000000000..ea93e3db7f
--- /dev/null
+++ b/src/com/vaadin/terminal/LegacyPaint.java
@@ -0,0 +1,85 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.PaintTarget.PaintStatus;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.HasComponents;
+
+public class LegacyPaint implements Serializable {
+ /**
+ *
+ * <p>
+ * Paints the Paintable into a UIDL stream. This method creates the UIDL
+ * sequence describing it and outputs it to the given UIDL stream.
+ * </p>
+ *
+ * <p>
+ * It is called when the contents of the component should be painted in
+ * response to the component first being shown or having been altered so
+ * that its visual representation is changed.
+ * </p>
+ *
+ * <p>
+ * <b>Do not override this to paint your component.</b> Override
+ * {@link #paintContent(PaintTarget)} instead.
+ * </p>
+ *
+ *
+ * @param target
+ * the target UIDL stream where the component should paint itself
+ * to.
+ * @throws PaintException
+ * if the paint operation failed.
+ */
+ public static void paint(Component component, PaintTarget target)
+ throws PaintException {
+ // Only paint content of visible components.
+ if (!isVisibleInContext(component)) {
+ return;
+ }
+
+ final String tag = target.getTag(component);
+ final PaintStatus status = target.startPaintable(component, tag);
+ if (PaintStatus.CACHED == status) {
+ // nothing to do but flag as cached and close the paintable tag
+ target.addAttribute("cached", true);
+ } else {
+ // Paint the contents of the component
+ if (component instanceof Vaadin6Component) {
+ ((Vaadin6Component) component).paintContent(target);
+ }
+
+ }
+ target.endPaintable(component);
+
+ }
+
+ /**
+ * Checks if the component is visible and its parent is visible,
+ * recursively.
+ * <p>
+ * This is only a helper until paint is moved away from this class.
+ *
+ * @return
+ */
+ protected static boolean isVisibleInContext(Component c) {
+ HasComponents p = c.getParent();
+ while (p != null) {
+ if (!p.isVisible()) {
+ return false;
+ }
+ p = p.getParent();
+ }
+ if (c.getParent() != null && !c.getParent().isComponentVisible(c)) {
+ return false;
+ }
+
+ // All parents visible, return this state
+ return c.isVisible();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/PaintTarget.java b/src/com/vaadin/terminal/PaintTarget.java
index 68f68e2b51..9cfa324133 100644
--- a/src/com/vaadin/terminal/PaintTarget.java
+++ b/src/com/vaadin/terminal/PaintTarget.java
@@ -9,6 +9,7 @@ import java.util.Map;
import com.vaadin.terminal.StreamVariable.StreamingStartEvent;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.server.ClientConnector;
/**
* This interface defines the methods for painting XML to the UIDL stream.
@@ -37,6 +38,26 @@ public interface PaintTarget extends Serializable {
throws PaintException;
/**
+ * Result of starting to paint a Paintable (
+ * {@link PaintTarget#startPaintable(ClientConnector, String)}).
+ *
+ * @since 7.0
+ */
+ public enum PaintStatus {
+ /**
+ * Painting started, addVariable() and addAttribute() etc. methods may
+ * be called.
+ */
+ PAINTING,
+ /**
+ * A previously unpainted or painted {@link Paintable} has been queued
+ * be created/update later in a separate change in the same set of
+ * changes.
+ */
+ CACHED
+ };
+
+ /**
* Prints element start tag of a paintable section. Starts a paintable
* section using the given tag. The PaintTarget may implement a caching
* scheme, that checks the paintable has actually changed or can a cached
@@ -46,43 +67,45 @@ public interface PaintTarget extends Serializable {
* omit the content and close the tag, in which case cached content should
* be used.
* </p>
+ * <p>
+ * This method may also add only a reference to the paintable and queue the
+ * paintable to be painted separately.
+ * </p>
+ * <p>
+ * Each paintable being painted should be closed by a matching
+ * {@link #endPaintable(ClientConnector)} regardless of the
+ * {@link PaintStatus} returned.
+ * </p>
*
* @param paintable
* the paintable to start.
* @param tag
* the name of the start tag.
- * @return <code>true</code> if paintable found in cache, <code>false</code>
- * otherwise.
+ * @return {@link PaintStatus} - ready to paint or already cached on the
+ * client (also used for sub paintables that are painted later
+ * separately)
* @throws PaintException
* if the paint operation failed.
* @see #startTag(String)
- * @since 3.1
+ * @since 7.0 (previously using startTag(Paintable, String))
*/
- public boolean startTag(Paintable paintable, String tag)
+ public PaintStatus startPaintable(ClientConnector paintable, String tag)
throws PaintException;
/**
- * Paints a component reference as an attribute to current tag. This method
- * is meant to enable component interactions on client side. With reference
- * the client side component can communicate directly to other component.
+ * Prints paintable element end tag.
*
- * Note! This was experimental api and got replaced by
- * {@link #addAttribute(String, Paintable)} and
- * {@link #addVariable(VariableOwner, String, Paintable)}.
+ * Calls to {@link #startPaintable(ClientConnector, String)}should be
+ * matched by {@link #endPaintable(ClientConnector)}. If the parent tag is
+ * closed before every child tag is closed a PaintException is raised.
*
* @param paintable
- * the Paintable to reference
- * @param referenceName
+ * the paintable to close.
* @throws PaintException
- *
- * @since 5.2
- * @deprecated use {@link #addAttribute(String, Paintable)} or
- * {@link #addVariable(VariableOwner, String, Paintable)}
- * instead
+ * if the paint operation failed.
+ * @since 7.0 (previously using engTag(String))
*/
- @Deprecated
- public void paintReference(Paintable paintable, String referenceName)
- throws PaintException;
+ public void endPaintable(ClientConnector paintable) throws PaintException;
/**
* Prints element start tag.
@@ -266,7 +289,7 @@ public interface PaintTarget extends Serializable {
* the Paintable to be referenced on client side
* @throws PaintException
*/
- public void addAttribute(String name, Paintable value)
+ public void addAttribute(String name, ClientConnector value)
throws PaintException;
/**
@@ -398,8 +421,8 @@ public interface PaintTarget extends Serializable {
* @throws PaintException
* if the paint oparation fails
*/
- public void addVariable(VariableOwner owner, String name, Paintable value)
- throws PaintException;
+ public void addVariable(VariableOwner owner, String name,
+ ClientConnector value) throws PaintException;
/**
* Adds a upload stream type variable.
@@ -470,14 +493,15 @@ public interface PaintTarget extends Serializable {
/**
* @return the "tag" string used in communication to present given
- * {@link Paintable} type. Terminal may define how to present
- * paintable.
+ * {@link ClientConnector} type. Terminal may define how to present
+ * the connector.
*/
- public String getTag(Paintable paintable);
+ public String getTag(ClientConnector paintable);
/**
* @return true if a full repaint has been requested. E.g. refresh in a
* browser window or such.
*/
public boolean isFullRepaint();
+
}
diff --git a/src/com/vaadin/terminal/Paintable.java b/src/com/vaadin/terminal/Paintable.java
deleted file mode 100644
index d043cb2606..0000000000
--- a/src/com/vaadin/terminal/Paintable.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal;
-
-import java.io.Serializable;
-import java.util.EventObject;
-
-/**
- * Interface implemented by all classes that can be painted. Classes
- * implementing this interface know how to output themselves to a UIDL stream
- * and that way describing to the terminal how it should be displayed in the UI.
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.0
- */
-public interface Paintable extends java.util.EventListener, Serializable {
-
- /**
- * <p>
- * Paints the Paintable into a UIDL stream. This method creates the UIDL
- * sequence describing it and outputs it to the given UIDL stream.
- * </p>
- *
- * <p>
- * It is called when the contents of the component should be painted in
- * response to the component first being shown or having been altered so
- * that its visual representation is changed.
- * </p>
- *
- * @param target
- * the target UIDL stream where the component should paint itself
- * to.
- * @throws PaintException
- * if the paint operation failed.
- */
- public void paint(PaintTarget target) throws PaintException;
-
- /**
- * Requests that the paintable should be repainted as soon as possible.
- */
- public void requestRepaint();
-
- /**
- * Adds an unique id for component that get's transferred to terminal for
- * testing purposes. Keeping identifiers unique throughout the Application
- * instance is on programmers responsibility.
- * <p>
- * Note, that with the current terminal implementation the identifier cannot
- * be changed while the component is visible. This means that the identifier
- * should be set before the component is painted for the first time and kept
- * the same while visible in the client.
- *
- * @param id
- * A short (< 20 chars) alphanumeric id
- */
- public void setDebugId(String id);
-
- /**
- * Get's currently set debug identifier
- *
- * @return current debug id, null if not set
- */
- public String getDebugId();
-
- /**
- * Repaint request event is thrown when the paintable needs to be repainted.
- * This is typically done when the <code>paint</code> method would return
- * dissimilar UIDL from the previous call of the method.
- */
- @SuppressWarnings("serial")
- public class RepaintRequestEvent extends EventObject {
-
- /**
- * Constructs a new event.
- *
- * @param source
- * the paintable needing repaint.
- */
- public RepaintRequestEvent(Paintable source) {
- super(source);
- }
-
- /**
- * Gets the paintable needing repainting.
- *
- * @return Paintable for which the <code>paint</code> method will return
- * dissimilar UIDL from the previous call of the method.
- */
- public Paintable getPaintable() {
- return (Paintable) getSource();
- }
- }
-
- /**
- * Listens repaint requests. The <code>repaintRequested</code> method is
- * called when the paintable needs to be repainted. This is typically done
- * when the <code>paint</code> method would return dissimilar UIDL from the
- * previous call of the method.
- */
- public interface RepaintRequestListener extends Serializable {
-
- /**
- * Receives repaint request events.
- *
- * @param event
- * the repaint request event specifying the paintable source.
- */
- public void repaintRequested(RepaintRequestEvent event);
- }
-
- /**
- * Adds repaint request listener. In order to assure that no repaint
- * requests are missed, the new repaint listener should paint the paintable
- * right after adding itself as listener.
- *
- * @param listener
- * the listener to be added.
- */
- public void addListener(RepaintRequestListener listener);
-
- /**
- * Removes repaint request listener.
- *
- * @param listener
- * the listener to be removed.
- */
- public void removeListener(RepaintRequestListener listener);
-
- /**
- * Request sending of repaint events on any further visible changes.
- * Normally the paintable only send up to one repaint request for listeners
- * after paint as the paintable as the paintable assumes that the listeners
- * already know about the repaint need. This method resets the assumtion.
- * Paint implicitly does the assumtion reset functionality implemented by
- * this method.
- * <p>
- * This method is normally used only by the terminals to note paintables
- * about implicit repaints (painting the component without actually invoking
- * paint method).
- * </p>
- */
- public void requestRepaintRequests();
-}
diff --git a/src/com/vaadin/terminal/ParameterHandler.java b/src/com/vaadin/terminal/ParameterHandler.java
deleted file mode 100644
index ef8a952e0e..0000000000
--- a/src/com/vaadin/terminal/ParameterHandler.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal;
-
-import java.io.Serializable;
-import java.util.Map;
-
-import com.vaadin.ui.Window;
-
-/**
- * {@code ParameterHandler} is implemented by classes capable of handling
- * external parameters.
- *
- * <p>
- * What parameters are provided depend on what the {@link Terminal} provides and
- * if the application is deployed as a servlet or portlet. URL GET parameters
- * are typically provided to the {@link #handleParameters(Map)} method.
- * </p>
- * <p>
- * A {@code ParameterHandler} must be registered to a {@code Window} using
- * {@link Window#addParameterHandler(ParameterHandler)} to be called when
- * parameters are available.
- * </p>
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.0
- */
-public interface ParameterHandler extends Serializable {
-
- /**
- * Handles the given parameters. All parameters names are of type
- * {@link String} and the values are {@link String} arrays.
- *
- * @param parameters
- * an unmodifiable map which contains the parameter names and
- * values
- *
- */
- public void handleParameters(Map<String, String[]> parameters);
-
- /**
- * An ErrorEvent implementation for ParameterHandler.
- */
- public interface ErrorEvent extends Terminal.ErrorEvent {
-
- /**
- * Gets the ParameterHandler that caused the error.
- *
- * @return the ParameterHandler that caused the error
- */
- public ParameterHandler getParameterHandler();
-
- }
-
-}
diff --git a/src/com/vaadin/terminal/RequestHandler.java b/src/com/vaadin/terminal/RequestHandler.java
new file mode 100644
index 0000000000..f37201715d
--- /dev/null
+++ b/src/com/vaadin/terminal/RequestHandler.java
@@ -0,0 +1,36 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import com.vaadin.Application;
+
+/**
+ * Handler for producing a response to non-UIDL requests. Handlers can be added
+ * to applications using {@link Application#addRequestHandler(RequestHandler)}
+ */
+public interface RequestHandler extends Serializable {
+
+ /**
+ * Handles a non-UIDL request. If a response is written, this method should
+ * return <code>false</code> to indicate that no more request handlers
+ * should be invoked for the request.
+ *
+ * @param application
+ * The application to which the request belongs
+ * @param request
+ * The request to handle
+ * @param response
+ * The response object to which a response can be written.
+ * @return true if a response has been written and no further request
+ * handlers should be called, otherwise false
+ * @throws IOException
+ */
+ boolean handleRequest(Application application, WrappedRequest request,
+ WrappedResponse response) throws IOException;
+
+}
diff --git a/src/com/vaadin/terminal/Scrollable.java b/src/com/vaadin/terminal/Scrollable.java
index dac35c7704..472954c556 100644
--- a/src/com/vaadin/terminal/Scrollable.java
+++ b/src/com/vaadin/terminal/Scrollable.java
@@ -9,8 +9,7 @@ import java.io.Serializable;
/**
* <p>
* This interface is implemented by all visual objects that can be scrolled
- * programmatically from the server-side, or for which it is possible to know
- * the scroll position on the server-side. The unit of scrolling is pixel.
+ * programmatically from the server-side. The unit of scrolling is pixel.
* </p>
*
* @author Vaadin Ltd.
@@ -40,17 +39,10 @@ public interface Scrollable extends Serializable {
* scrolled right.
* </p>
*
- * <p>
- * The method only has effect if programmatic scrolling is enabled for the
- * scrollable. Some implementations may require enabling programmatic before
- * this method can be used. See {@link #setScrollable(boolean)} for more
- * information.
- * </p>
- *
- * @param pixelsScrolled
+ * @param scrollLeft
* the xOffset.
*/
- public void setScrollLeft(int pixelsScrolled);
+ public void setScrollLeft(int scrollLeft);
/**
* Gets scroll top offset.
@@ -73,13 +65,6 @@ public interface Scrollable extends Serializable {
* </p>
*
* <p>
- * The method only has effect if programmatic scrolling is enabled for the
- * scrollable. Some implementations may require enabling programmatic before
- * this method can be used. See {@link #setScrollable(boolean)} for more
- * information.
- * </p>
- *
- * <p>
* The scrolling position is limited by the current height of the content
* area. If the position is below the height, it is scrolled to the bottom.
* However, if the same response also adds height to the content area,
@@ -87,44 +72,9 @@ public interface Scrollable extends Serializable {
* area.
* </p>
*
- * @param pixelsScrolled
+ * @param scrollTop
* the yOffset.
*/
- public void setScrollTop(int pixelsScrolled);
-
- /**
- * Is programmatic scrolling enabled.
- *
- * <p>
- * Whether programmatic scrolling with {@link #setScrollLeft(int)} and
- * {@link #setScrollTop(int)} is enabled.
- * </p>
- *
- * @return <code>true</code> if the scrolling is enabled, otherwise
- * <code>false</code>.
- */
- public boolean isScrollable();
-
- /**
- * Enables or disables programmatic scrolling.
- *
- * <p>
- * Enables setting the scroll position with {@link #setScrollLeft(int)} and
- * {@link #setScrollTop(int)}. Implementations of the interface may have
- * programmatic scrolling disabled by default, in which case you need to
- * enable it to use the mentioned methods.
- * </p>
- *
- * <p>
- * Notice that this does <i>not</i> control whether scroll bars are shown
- * for a scrollable component. That normally happens automatically when the
- * content grows too big for the component, relying on the "overflow: auto"
- * property in CSS.
- * </p>
- *
- * @param isScrollingEnabled
- * true if the scrolling is allowed.
- */
- public void setScrollable(boolean isScrollingEnabled);
+ public void setScrollTop(int scrollTop);
}
diff --git a/src/com/vaadin/terminal/Sizeable.java b/src/com/vaadin/terminal/Sizeable.java
index f5dc28b74c..e3c98e0fa9 100644
--- a/src/com/vaadin/terminal/Sizeable.java
+++ b/src/com/vaadin/terminal/Sizeable.java
@@ -18,71 +18,127 @@ import java.io.Serializable;
public interface Sizeable extends Serializable {
/**
- * Unit code representing pixels.
+ * @deprecated from 7.0, use {@link Unit#PIXELS} instead    
*/
- public static final int UNITS_PIXELS = 0;
+ @Deprecated
+ public static final Unit UNITS_PIXELS = Unit.PIXELS;
/**
- * Unit code representing points (1/72nd of an inch).
+ * @deprecated from 7.0, use {@link Unit#POINTS} instead    
*/
- public static final int UNITS_POINTS = 1;
+ @Deprecated
+ public static final Unit UNITS_POINTS = Unit.POINTS;
/**
- * Unit code representing picas (12 points).
+ * @deprecated from 7.0, use {@link Unit#PICAS} instead    
*/
- public static final int UNITS_PICAS = 2;
+ @Deprecated
+ public static final Unit UNITS_PICAS = Unit.PICAS;
/**
- * Unit code representing the font-size of the relevant font.
+ * @deprecated from 7.0, use {@link Unit#EM} instead    
*/
- public static final int UNITS_EM = 3;
+ @Deprecated
+ public static final Unit UNITS_EM = Unit.EM;
/**
- * Unit code representing the x-height of the relevant font.
+ * @deprecated from 7.0, use {@link Unit#EX} instead    
*/
- public static final int UNITS_EX = 4;
+ @Deprecated
+ public static final Unit UNITS_EX = Unit.EX;
/**
- * Unit code representing millimeters.
+ * @deprecated from 7.0, use {@link Unit#MM} instead    
*/
- public static final int UNITS_MM = 5;
+ @Deprecated
+ public static final Unit UNITS_MM = Unit.MM;
/**
- * Unit code representing centimeters.
+ * @deprecated from 7.0, use {@link Unit#CM} instead    
*/
- public static final int UNITS_CM = 6;
+ @Deprecated
+ public static final Unit UNITS_CM = Unit.CM;
/**
- * Unit code representing inches.
+ * @deprecated from 7.0, use {@link Unit#INCH} instead    
*/
- public static final int UNITS_INCH = 7;
+ @Deprecated
+ public static final Unit UNITS_INCH = Unit.INCH;
/**
- * Unit code representing in percentage of the containing element defined by
- * terminal.
+ * @deprecated from 7.0, use {@link Unit#PERCENTAGE} instead    
*/
- public static final int UNITS_PERCENTAGE = 8;
+ @Deprecated
+ public static final Unit UNITS_PERCENTAGE = Unit.PERCENTAGE;
public static final float SIZE_UNDEFINED = -1;
- /**
- * Textual representations of units symbols. Supported units and their
- * symbols are:
- * <ul>
- * <li>{@link #UNITS_PIXELS}: "px"</li>
- * <li>{@link #UNITS_POINTS}: "pt"</li>
- * <li>{@link #UNITS_PICAS}: "pc"</li>
- * <li>{@link #UNITS_EM}: "em"</li>
- * <li>{@link #UNITS_EX}: "ex"</li>
- * <li>{@link #UNITS_MM}: "mm"</li>
- * <li>{@link #UNITS_CM}. "cm"</li>
- * <li>{@link #UNITS_INCH}: "in"</li>
- * <li>{@link #UNITS_PERCENTAGE}: "%"</li>
- * </ul>
- * These can be used like <code>Sizeable.UNIT_SYMBOLS[UNITS_PIXELS]</code>.
- */
- public static final String[] UNIT_SYMBOLS = { "px", "pt", "pc", "em", "ex",
- "mm", "cm", "in", "%" };
+ public enum Unit {
+ /**
+ * Unit code representing pixels.
+ */
+ PIXELS("px"),
+ /**
+ * Unit code representing points (1/72nd of an inch).
+ */
+ POINTS("pt"),
+ /**
+ * Unit code representing picas (12 points).
+ */
+ PICAS("pc"),
+ /**
+ * Unit code representing the font-size of the relevant font.
+ */
+ EM("em"),
+ /**
+ * Unit code representing the x-height of the relevant font.
+ */
+ EX("ex"),
+ /**
+ * Unit code representing millimeters.
+ */
+ MM("mm"),
+ /**
+ * Unit code representing centimeters.
+ */
+ CM("cm"),
+ /**
+ * Unit code representing inches.
+ */
+ INCH("in"),
+ /**
+ * Unit code representing in percentage of the containing element
+ * defined by terminal.
+ */
+ PERCENTAGE("%");
+
+ private String symbol;
+
+ private Unit(String symbol) {
+ this.symbol = symbol;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ public static Unit getUnitFromSymbol(String symbol) {
+ if (symbol == null) {
+ return Unit.PIXELS; // Defaults to pixels
+ }
+ for (Unit unit : Unit.values()) {
+ if (symbol.equals(unit.getSymbol())) {
+ return unit;
+ }
+ }
+ return Unit.PIXELS; // Defaults to pixels
+ }
+ }
/**
* Gets the width of the object. Negative number implies unspecified size
@@ -93,21 +149,6 @@ public interface Sizeable extends Serializable {
public float getWidth();
/**
- * Sets the width of the object. Negative number implies unspecified size
- * (terminal is free to set the size).
- *
- * @param width
- * the width of the object in units specified by widthUnits
- * property.
- * @deprecated Consider using {@link #setWidth(String)} instead. This method
- * works, but is error-prone since the unit must be set
- * separately (and components might have different default
- * unit).
- */
- @Deprecated
- public void setWidth(float width);
-
- /**
* Gets the height of the object. Negative number implies unspecified size
* (terminal is free to set the size).
*
@@ -116,57 +157,18 @@ public interface Sizeable extends Serializable {
public float getHeight();
/**
- * Sets the height of the object. Negative number implies unspecified size
- * (terminal is free to set the size).
- *
- * @param height
- * the height of the object in units specified by heightUnits
- * property.
- * @deprecated Consider using {@link #setHeight(String)} or
- * {@link #setHeight(float, int)} instead. This method works,
- * but is error-prone since the unit must be set separately (and
- * components might have different default unit).
- */
- @Deprecated
- public void setHeight(float height);
-
- /**
* Gets the width property units.
*
* @return units used in width property.
*/
- public int getWidthUnits();
-
- /**
- * Sets the width property units.
- *
- * @param units
- * the units used in width property.
- * @deprecated Consider setting width and unit simultaneously using
- * {@link #setWidth(String)} or {@link #setWidth(float, int)},
- * which is less error-prone.
- */
- @Deprecated
- public void setWidthUnits(int units);
+ public Unit getWidthUnits();
/**
* Gets the height property units.
*
* @return units used in height property.
*/
- public int getHeightUnits();
-
- /**
- * Sets the height property units.
- *
- * @param units
- * the units used in height property.
- * @deprecated Consider setting height and unit simultaneously using
- * {@link #setHeight(String)} or {@link #setHeight(float, int)},
- * which is less error-prone.
- */
- @Deprecated
- public void setHeightUnits(int units);
+ public Unit getHeightUnits();
/**
* Sets the height of the component using String presentation.
@@ -193,13 +195,9 @@ public interface Sizeable extends Serializable {
* @param width
* the width of the object.
* @param unit
- * the unit used for the width. Possible values include
- * {@link #UNITS_PIXELS}, {@link #UNITS_POINTS},
- * {@link #UNITS_PICAS}, {@link #UNITS_EM}, {@link #UNITS_EX},
- * {@link #UNITS_MM}, {@link #UNITS_CM}, {@link #UNITS_INCH},
- * {@link #UNITS_PERCENTAGE}.
+ * the unit used for the width.
*/
- public void setWidth(float width, int unit);
+ public void setWidth(float width, Unit unit);
/**
* Sets the height of the object. Negative number implies unspecified size
@@ -208,13 +206,9 @@ public interface Sizeable extends Serializable {
* @param height
* the height of the object.
* @param unit
- * the unit used for the width. Possible values include
- * {@link #UNITS_PIXELS}, {@link #UNITS_POINTS},
- * {@link #UNITS_PICAS}, {@link #UNITS_EM}, {@link #UNITS_EX},
- * {@link #UNITS_MM}, {@link #UNITS_CM}, {@link #UNITS_INCH},
- * {@link #UNITS_PERCENTAGE}.
+ * the unit used for the width.
*/
- public void setHeight(float height, int unit);
+ public void setHeight(float height, Unit unit);
/**
* Sets the width of the component using String presentation.
diff --git a/src/com/vaadin/terminal/SystemError.java b/src/com/vaadin/terminal/SystemError.java
index ce1483dbb5..bae135ee6b 100644
--- a/src/com/vaadin/terminal/SystemError.java
+++ b/src/com/vaadin/terminal/SystemError.java
@@ -4,16 +4,12 @@
package com.vaadin.terminal;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
/**
- * <code>SystemError</code> is a runtime exception caused by error in system.
- * The system error can be shown to the user as it implements
- * <code>ErrorMessage</code> interface, but contains technical information such
- * as stack trace and exception.
+ * <code>SystemError</code> is an error message for a problem caused by error in
+ * system, not the user application code. The system error can contain technical
+ * information such as stack trace and exception.
*
* SystemError does not support HTML in error messages or stack traces. If HTML
* messages are required, use {@link UserError} or a custom implementation of
@@ -25,13 +21,7 @@ import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class SystemError extends RuntimeException implements ErrorMessage {
-
- /**
- * The cause of the system error. The cause is stored separately as JDK 1.3
- * does not support causes natively.
- */
- private Throwable cause = null;
+public class SystemError extends AbstractErrorMessage {
/**
* Constructor for SystemError with error message specified.
@@ -41,6 +31,9 @@ public class SystemError extends RuntimeException implements ErrorMessage {
*/
public SystemError(String message) {
super(message);
+ setErrorLevel(ErrorLevel.SYSTEMERROR);
+ setMode(ContentMode.XHTML);
+ setMessage(getHtmlMessage());
}
/**
@@ -52,8 +45,8 @@ public class SystemError extends RuntimeException implements ErrorMessage {
* the throwable causing the system error.
*/
public SystemError(String message, Throwable cause) {
- super(message);
- this.cause = cause;
+ this(message);
+ addCause(AbstractErrorMessage.getErrorMessageForException(cause));
}
/**
@@ -63,31 +56,7 @@ public class SystemError extends RuntimeException implements ErrorMessage {
* the throwable causing the system error.
*/
public SystemError(Throwable cause) {
- this.cause = cause;
- }
-
- /**
- * @see com.vaadin.terminal.ErrorMessage#getErrorLevel()
- */
- public final int getErrorLevel() {
- return ErrorMessage.SYSTEMERROR;
- }
-
- /**
- * @see com.vaadin.terminal.Paintable#paint(com.vaadin.terminal.PaintTarget)
- */
- public void paint(PaintTarget target) throws PaintException {
-
- target.startTag("error");
- target.addAttribute("level", "system");
-
- String message = getHtmlMessage();
-
- target.addXMLSection("div", message,
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
-
- target.endTag("error");
-
+ this(null, cause);
}
/**
@@ -96,61 +65,18 @@ public class SystemError extends RuntimeException implements ErrorMessage {
* Note that this API may change in future versions.
*/
protected String getHtmlMessage() {
+ // TODO wrapping div with namespace? See the old code:
+ // target.addXMLSection("div", message,
+ // "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
+
StringBuilder sb = new StringBuilder();
- final String message = getLocalizedMessage();
- if (message != null) {
+ if (getMessage() != null) {
sb.append("<h2>");
- sb.append(AbstractApplicationServlet.safeEscapeForHtml(message));
+ sb.append(AbstractApplicationServlet
+ .safeEscapeForHtml(getMessage()));
sb.append("</h2>");
}
-
- // Paint the exception
- if (cause != null) {
- sb.append("<h3>Exception</h3>");
- final StringWriter buffer = new StringWriter();
- cause.printStackTrace(new PrintWriter(buffer));
- sb.append("<pre>");
- sb.append(AbstractApplicationServlet.safeEscapeForHtml(buffer
- .toString()));
- sb.append("</pre>");
- }
return sb.toString();
}
- /**
- * Gets cause for the error.
- *
- * @return the cause.
- * @see java.lang.Throwable#getCause()
- */
- @Override
- public Throwable getCause() {
- return cause;
- }
-
- /* Documented in super interface */
- public void addListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void removeListener(RepaintRequestListener listener) {
- }
-
- /* Documented in super interface */
- public void requestRepaint() {
- }
-
- /* Documented in super interface */
- public void requestRepaintRequests() {
- }
-
- public String getDebugId() {
- return null;
- }
-
- public void setDebugId(String id) {
- throw new UnsupportedOperationException(
- "Setting testing id for this Paintable is not implemented");
- }
-
}
diff --git a/src/com/vaadin/terminal/URIHandler.java b/src/com/vaadin/terminal/URIHandler.java
deleted file mode 100644
index b3fea0e3bf..0000000000
--- a/src/com/vaadin/terminal/URIHandler.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal;
-
-import java.io.Serializable;
-import java.net.URL;
-
-/**
- * A URIHandler is used for handling URI:s requested by the user and can
- * optionally provide a {@link DownloadStream}. If a {@link DownloadStream} is
- * returned by {@link #handleURI(URL, String)}, the stream is sent to the
- * client.
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.0
- */
-public interface URIHandler extends Serializable {
-
- /**
- * Handles a given URI. If the URI handler to emit a downloadable stream it
- * should return a {@code DownloadStream} object.
- *
- * @param context
- * the base URL
- * @param relativeUri
- * a URI relative to {@code context}
- * @return A downloadable stream or null if no stream is provided
- */
- public DownloadStream handleURI(URL context, String relativeUri);
-
- /**
- * An {@code ErrorEvent} implementation for URIHandler.
- */
- public interface ErrorEvent extends Terminal.ErrorEvent {
-
- /**
- * Gets the URIHandler that caused this error.
- *
- * @return the URIHandler that caused the error
- */
- public URIHandler getURIHandler();
-
- }
-}
diff --git a/src/com/vaadin/terminal/UserError.java b/src/com/vaadin/terminal/UserError.java
index 170d76610b..baaf331fa0 100644
--- a/src/com/vaadin/terminal/UserError.java
+++ b/src/com/vaadin/terminal/UserError.java
@@ -4,8 +4,6 @@
package com.vaadin.terminal;
-import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
-
/**
* <code>UserError</code> is a controlled error occurred in application. User
* errors are occur in normal usage of the application and guide the user.
@@ -16,43 +14,25 @@ import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class UserError implements ErrorMessage {
-
- /**
- * Content mode, where the error contains only plain text.
- */
- public static final int CONTENT_TEXT = 0;
+public class UserError extends AbstractErrorMessage {
/**
- * Content mode, where the error contains preformatted text.
+ * @deprecated from 7.0, use {@link ContentMode#TEXT} instead    
*/
- public static final int CONTENT_PREFORMATTED = 1;
+ @Deprecated
+ public static final ContentMode CONTENT_TEXT = ContentMode.TEXT;
/**
- * Formatted content mode, where the contents is XML restricted to the UIDL
- * 1.0 formatting markups.
+ * @deprecated from 7.0, use {@link ContentMode#PREFORMATTED} instead    
*/
- public static final int CONTENT_UIDL = 2;
+ @Deprecated
+ public static final ContentMode CONTENT_PREFORMATTED = ContentMode.PREFORMATTED;
/**
- * Content mode, where the error contains XHTML.
+ * @deprecated from 7.0, use {@link ContentMode#XHTML} instead    
*/
- public static final int CONTENT_XHTML = 3;
-
- /**
- * Content mode.
- */
- private int mode = CONTENT_TEXT;
-
- /**
- * Message in content mode.
- */
- private final String msg;
-
- /**
- * Error level.
- */
- private int level = ErrorMessage.ERROR;
+ @Deprecated
+ public static final ContentMode CONTENT_XHTML = ContentMode.XHTML;
/**
* Creates a textual error message of level ERROR.
@@ -61,105 +41,20 @@ public class UserError implements ErrorMessage {
* the text of the error message.
*/
public UserError(String textErrorMessage) {
- msg = textErrorMessage;
+ super(textErrorMessage);
}
- /**
- * Creates a error message with level and content mode.
- *
- * @param message
- * the error message.
- * @param contentMode
- * the content Mode.
- * @param errorLevel
- * the level of error.
- */
- public UserError(String message, int contentMode, int errorLevel) {
-
- // Check the parameters
- if (contentMode < 0 || contentMode > 2) {
- throw new java.lang.IllegalArgumentException(
- "Unsupported content mode: " + contentMode);
+ public UserError(String message, ContentMode contentMode,
+ ErrorLevel errorLevel) {
+ super(message);
+ if (contentMode == null) {
+ contentMode = ContentMode.TEXT;
}
-
- msg = message;
- mode = contentMode;
- level = errorLevel;
- }
-
- /* Documented in interface */
- public int getErrorLevel() {
- return level;
- }
-
- /* Documented in interface */
- public void addListener(RepaintRequestListener listener) {
- }
-
- /* Documented in interface */
- public void removeListener(RepaintRequestListener listener) {
- }
-
- /* Documented in interface */
- public void requestRepaint() {
- }
-
- /* Documented in interface */
- public void paint(PaintTarget target) throws PaintException {
-
- target.startTag("error");
-
- // Error level
- if (level >= ErrorMessage.SYSTEMERROR) {
- target.addAttribute("level", "system");
- } else if (level >= ErrorMessage.CRITICAL) {
- target.addAttribute("level", "critical");
- } else if (level >= ErrorMessage.ERROR) {
- target.addAttribute("level", "error");
- } else if (level >= ErrorMessage.WARNING) {
- target.addAttribute("level", "warning");
- } else {
- target.addAttribute("level", "info");
- }
-
- // Paint the message
- switch (mode) {
- case CONTENT_TEXT:
- target.addText(AbstractApplicationServlet.safeEscapeForHtml(msg));
- break;
- case CONTENT_UIDL:
- target.addUIDL(msg);
- break;
- case CONTENT_PREFORMATTED:
- target.addText("<pre>"
- + AbstractApplicationServlet.safeEscapeForHtml(msg)
- + "</pre>");
- break;
- case CONTENT_XHTML:
- target.addText(msg);
- break;
+ if (errorLevel == null) {
+ errorLevel = ErrorLevel.ERROR;
}
-
- target.endTag("error");
- }
-
- /* Documented in interface */
- public void requestRepaintRequests() {
- }
-
- /* Documented in superclass */
- @Override
- public String toString() {
- return msg;
- }
-
- public String getDebugId() {
- return null;
- }
-
- public void setDebugId(String id) {
- throw new UnsupportedOperationException(
- "Setting testing id for this Paintable is not implemented");
+ setMode(contentMode);
+ setErrorLevel(errorLevel);
}
}
diff --git a/src/com/vaadin/terminal/Vaadin6Component.java b/src/com/vaadin/terminal/Vaadin6Component.java
new file mode 100644
index 0000000000..59cbf956ca
--- /dev/null
+++ b/src/com/vaadin/terminal/Vaadin6Component.java
@@ -0,0 +1,44 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal;
+
+import java.util.EventListener;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Interface provided to ease porting of Vaadin 6 components to Vaadin 7. By
+ * implementing this interface your Component will be able to use
+ * {@link #paintContent(PaintTarget)} and
+ * {@link #changeVariables(Object, java.util.Map)} just like in Vaadin 6.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public interface Vaadin6Component extends VariableOwner, Component,
+ EventListener {
+
+ /**
+ * <p>
+ * Paints the Paintable into a UIDL stream. This method creates the UIDL
+ * sequence describing it and outputs it to the given UIDL stream.
+ * </p>
+ *
+ * <p>
+ * It is called when the contents of the component should be painted in
+ * response to the component first being shown or having been altered so
+ * that its visual representation is changed.
+ * </p>
+ *
+ * @param target
+ * the target UIDL stream where the component should paint itself
+ * to.
+ * @throws PaintException
+ * if the paint operation failed.
+ */
+ public void paintContent(PaintTarget target) throws PaintException;
+
+}
diff --git a/src/com/vaadin/terminal/VariableOwner.java b/src/com/vaadin/terminal/VariableOwner.java
index 49e2179ece..c52e04c008 100644
--- a/src/com/vaadin/terminal/VariableOwner.java
+++ b/src/com/vaadin/terminal/VariableOwner.java
@@ -20,7 +20,10 @@ import java.util.Map;
* @version
* @VERSION@
* @since 3.0
+ * @deprecated in 7.0. Only provided to ease porting of Vaadin 6 components. Do
+ * not implement this directly, implement {@link Vaadin6Component}.
*/
+@Deprecated
public interface VariableOwner extends Serializable {
/**
diff --git a/src/com/vaadin/terminal/WrappedRequest.java b/src/com/vaadin/terminal/WrappedRequest.java
new file mode 100644
index 0000000000..a27213d921
--- /dev/null
+++ b/src/com/vaadin/terminal/WrappedRequest.java
@@ -0,0 +1,277 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.PortletRequest;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import com.vaadin.Application;
+import com.vaadin.RootRequiresMoreInformationException;
+import com.vaadin.annotations.EagerInit;
+import com.vaadin.terminal.gwt.server.WebBrowser;
+import com.vaadin.ui.Root;
+
+/**
+ * A generic request to the server, wrapping a more specific request type, e.g.
+ * HttpServletReqest or PortletRequest.
+ *
+ * @since 7.0
+ */
+public interface WrappedRequest extends Serializable {
+
+ /**
+ * Detailed information extracted from the browser.
+ *
+ * @see WrappedRequest#getBrowserDetails()
+ */
+ public interface BrowserDetails extends Serializable {
+ /**
+ * Gets the URI hash fragment for the request. This is typically used to
+ * encode navigation within an application.
+ *
+ * @return the URI hash fragment
+ */
+ public String getUriFragment();
+
+ /**
+ * Gets the value of window.name from the browser. This can be used to
+ * keep track of the specific window between browser reloads.
+ *
+ * @return the string value of window.name in the browser
+ */
+ public String getWindowName();
+
+ /**
+ * Gets a reference to the {@link WebBrowser} object containing
+ * additional information, e.g. screen size and the time zone offset.
+ *
+ * @return the web browser object
+ */
+ public WebBrowser getWebBrowser();
+ }
+
+ /**
+ * Gets the named request parameter This is typically a HTTP GET or POST
+ * parameter, though other request types might have other ways of
+ * representing parameters.
+ *
+ * @see javax.servlet.ServletRequest#getParameter(String)
+ * @see javax.portlet.PortletRequest#getParameter(String)
+ *
+ * @param parameter
+ * the name of the parameter
+ * @return The paramter value, or <code>null</code> if no parameter with the
+ * given name is present
+ */
+ public String getParameter(String parameter);
+
+ /**
+ * Gets all the parameters of the request.
+ *
+ * @see #getParameter(String)
+ *
+ * @see javax.servlet.ServletRequest#getParameterMap()
+ * @see javax.portlet.PortletRequest#getParameter(String)
+ *
+ * @return A mapping of parameter names to arrays of parameter values
+ */
+ public Map<String, String[]> getParameterMap();
+
+ /**
+ * Returns the length of the request content that can be read from the input
+ * stream returned by {@link #getInputStream()}.
+ *
+ * @see javax.servlet.ServletRequest#getContentLength()
+ * @see javax.portlet.ClientDataRequest#getContentLength()
+ *
+ * @return content length in bytes
+ */
+ public int getContentLength();
+
+ /**
+ * Returns an input stream from which the request content can be read. The
+ * request content length can be obtained with {@link #getContentLength()}
+ * without reading the full stream contents.
+ *
+ * @see javax.servlet.ServletRequest#getInputStream()
+ * @see javax.portlet.ClientDataRequest#getPortletInputStream()
+ *
+ * @return the input stream from which the contents of the request can be
+ * read
+ * @throws IOException
+ * if the input stream can not be opened
+ */
+ public InputStream getInputStream() throws IOException;
+
+ /**
+ * Gets a request attribute.
+ *
+ * @param name
+ * the name of the attribute
+ * @return the value of the attribute, or <code>null</code> if there is no
+ * attribute with the given name
+ *
+ * @see javax.servlet.ServletRequest#getAttribute(String)
+ * @see javax.portlet.PortletRequest#getAttribute(String)
+ */
+ public Object getAttribute(String name);
+
+ /**
+ * Defines a request attribute.
+ *
+ * @param name
+ * the name of the attribute
+ * @param value
+ * the attribute value
+ *
+ * @see javax.servlet.ServletRequest#setAttribute(String, Object)
+ * @see javax.portlet.PortletRequest#setAttribute(String, Object)
+ */
+ public void setAttribute(String name, Object value);
+
+ /**
+ * Gets the path of the requested resource relative to the application. The
+ * path be <code>null</code> if no path information is available. Does
+ * always start with / if the path isn't <code>null</code>.
+ *
+ * @return a string with the path relative to the application.
+ *
+ * @see javax.servlet.http.HttpServletRequest#getPathInfo()
+ */
+ public String getRequestPathInfo();
+
+ /**
+ * Returns the maximum time interval, in seconds, that the session
+ * associated with this request will be kept open between client accesses.
+ *
+ * @return an integer specifying the number of seconds the session
+ * associated with this request remains open between client requests
+ *
+ * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
+ * @see javax.portlet.PortletSession#getMaxInactiveInterval()
+ */
+ public int getSessionMaxInactiveInterval();
+
+ /**
+ * Gets an attribute from the session associated with this request.
+ *
+ * @param name
+ * the name of the attribute
+ * @return the attribute value, or <code>null</code> if the attribute is not
+ * defined in the session
+ *
+ * @see javax.servlet.http.HttpSession#getAttribute(String)
+ * @see javax.portlet.PortletSession#getAttribute(String)
+ */
+ public Object getSessionAttribute(String name);
+
+ /**
+ * Saves an attribute value in the session associated with this request.
+ *
+ * @param name
+ * the name of the attribute
+ * @param attribute
+ * the attribute value
+ *
+ * @see javax.servlet.http.HttpSession#setAttribute(String, Object)
+ * @see javax.portlet.PortletSession#setAttribute(String, Object)
+ */
+ public void setSessionAttribute(String name, Object attribute);
+
+ /**
+ * Returns the MIME type of the body of the request, or null if the type is
+ * not known.
+ *
+ * @return a string containing the name of the MIME type of the request, or
+ * null if the type is not known
+ *
+ * @see javax.servlet.ServletRequest#getContentType()
+ * @see javax.portlet.ResourceRequest#getContentType()
+ *
+ */
+ public String getContentType();
+
+ /**
+ * Gets detailed information about the browser from which the request
+ * originated. This consists of information that is not available from
+ * normal HTTP requests, but requires additional information to be extracted
+ * for instance using javascript in the browser.
+ *
+ * This information is only guaranteed to be available in some special
+ * cases, for instance when {@link Application#getRoot} is called again
+ * after throwing {@link RootRequiresMoreInformationException} or in
+ * {@link Root#init(WrappedRequest)} for a Root class not annotated with
+ * {@link EagerInit}
+ *
+ * @return the browser details, or <code>null</code> if details are not
+ * available
+ *
+ * @see BrowserDetails
+ */
+ public BrowserDetails getBrowserDetails();
+
+ /**
+ * Gets locale information from the query, e.g. using the Accept-Language
+ * header.
+ *
+ * @return the preferred Locale
+ *
+ * @see ServletRequest#getLocale()
+ * @see PortletRequest#getLocale()
+ */
+ public Locale getLocale();
+
+ /**
+ * Returns the IP address from which the request came. This might also be
+ * the address of a proxy between the server and the original requester.
+ *
+ * @return a string containing the IP address, or <code>null</code> if the
+ * address is not available
+ *
+ * @see ServletRequest#getRemoteAddr()
+ */
+ public String getRemoteAddr();
+
+ /**
+ * Checks whether the request was made using a secure channel, e.g. using
+ * https.
+ *
+ * @return a boolean indicating if the request is secure
+ *
+ * @see ServletRequest#isSecure()
+ * @see PortletRequest#isSecure()
+ */
+ public boolean isSecure();
+
+ /**
+ * Gets the value of a request header, e.g. a http header for a
+ * {@link HttpServletRequest}.
+ *
+ * @param headerName
+ * the name of the header
+ * @return the header value, or <code>null</code> if the header is not
+ * present in the request
+ *
+ * @see HttpServletRequest#getHeader(String)
+ */
+ public String getHeader(String headerName);
+
+ /**
+ * Gets the deployment configuration for the context of this request.
+ *
+ * @return the deployment configuration
+ *
+ * @see DeploymentConfiguration
+ */
+ public DeploymentConfiguration getDeploymentConfiguration();
+
+}
diff --git a/src/com/vaadin/terminal/WrappedResponse.java b/src/com/vaadin/terminal/WrappedResponse.java
new file mode 100644
index 0000000000..995133a269
--- /dev/null
+++ b/src/com/vaadin/terminal/WrappedResponse.java
@@ -0,0 +1,147 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Serializable;
+
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletResponse;
+import javax.portlet.ResourceResponse;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A generic response from the server, wrapping a more specific response type,
+ * e.g. HttpServletResponse or PortletResponse.
+ *
+ * @since 7.0
+ */
+public interface WrappedResponse extends Serializable {
+
+ /**
+ * Sets the (http) status code for the response. If you want to include an
+ * error message along the status code, use {@link #sendError(int, String)}
+ * instead.
+ *
+ * @param statusCode
+ * the status code to set
+ * @see HttpServletResponse#setStatus(int)
+ *
+ * @see ResourceResponse#HTTP_STATUS_CODE
+ */
+ public void setStatus(int statusCode);
+
+ /**
+ * Sets the content type of this response. If the content type including a
+ * charset is set before {@link #getWriter()} is invoked, the returned
+ * PrintWriter will automatically use the defined charset.
+ *
+ * @param contentType
+ * a string specifying the MIME type of the content
+ *
+ * @see ServletResponse#setContentType(String)
+ * @see MimeResponse#setContentType(String)
+ */
+ public void setContentType(String contentType);
+
+ /**
+ * Sets the value of a generic response header. If the header had already
+ * been set, the new value overwrites the previous one.
+ *
+ * @param name
+ * the name of the header
+ * @param value
+ * the header value.
+ *
+ * @see HttpServletResponse#setHeader(String, String)
+ * @see PortletResponse#setProperty(String, String)
+ */
+ public void setHeader(String name, String value);
+
+ /**
+ * Properly formats a timestamp as a date header. If the header had already
+ * been set, the new value overwrites the previous one.
+ *
+ * @param name
+ * the name of the header
+ * @param timestamp
+ * the number of milliseconds since epoch
+ *
+ * @see HttpServletResponse#setDateHeader(String, long)
+ */
+ public void setDateHeader(String name, long timestamp);
+
+ /**
+ * Returns a <code>OutputStream</code> for writing binary data in the
+ * response.
+ * <p>
+ * Either this method or getWriter() may be called to write the response,
+ * not both.
+ *
+ * @return a <code>OutputStream</code> for writing binary data
+ * @throws IOException
+ * if an input or output exception occurred
+ *
+ * @see #getWriter()
+ * @see ServletResponse#getOutputStream()
+ * @see MimeResponse#getPortletOutputStream()
+ */
+ public OutputStream getOutputStream() throws IOException;
+
+ /**
+ * Returns a <code>PrintWriter</code> object that can send character text to
+ * the client. The PrintWriter uses the character encoding defined using
+ * setContentType.
+ * <p>
+ * Either this method or getOutputStream() may be called to write the
+ * response, not both.
+ *
+ * @return a <code>PrintWriter</code> for writing character text
+ * @throws IOException
+ * if an input or output exception occurred
+ *
+ * @see #getOutputStream()
+ * @see ServletResponse#getWriter()
+ * @see MimeResponse#getWriter()
+ */
+ public PrintWriter getWriter() throws IOException;
+
+ /**
+ * Sets cache time in milliseconds, -1 means no cache at all. All required
+ * headers related to caching in the response are set based on the time.
+ *
+ * @param milliseconds
+ * Cache time in milliseconds
+ */
+ public void setCacheTime(long milliseconds);
+
+ /**
+ * Sends an error response to the client using the specified status code and
+ * clears the buffer. In some configurations, this can cause a predefined
+ * error page to be displayed.
+ *
+ * @param errorCode
+ * the HTTP status code
+ * @param message
+ * a message to accompany the error
+ * @throws IOException
+ * if an input or output exception occurs
+ *
+ * @see HttpServletResponse#sendError(int, String)
+ */
+ public void sendError(int errorCode, String message) throws IOException;
+
+ /**
+ * Gets the deployment configuration for the context of this response.
+ *
+ * @return the deployment configuration
+ *
+ * @see DeploymentConfiguration
+ */
+ public DeploymentConfiguration getDeploymentConfiguration();
+}
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index cebfd8a679..f65b4c51e7 100644
--- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
@@ -1,6 +1,6 @@
<module>
- <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module
- you want to extend when creating an extended widget set, or when creating
+ <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module
+ you want to extend when creating an extended widget set, or when creating
a specialized widget set with a subset of the components. -->
<!-- Hint for WidgetSetBuilder not to automatically update the file -->
@@ -10,26 +10,34 @@
<inherits name="com.google.gwt.http.HTTP" />
+ <inherits name="com.google.gwt.json.JSON" />
+
+ <inherits
+ name="com.vaadin.terminal.gwt.DefaultWidgetSetBrowserSpecificOverrides" />
+
<source path="client" />
- <!-- Use own Scheduler implementation to be able to track if commands are
+ <!-- Use own Scheduler implementation to be able to track if commands are
running -->
<replace-with class="com.vaadin.terminal.gwt.client.VSchedulerImpl">
<when-type-is class="com.google.gwt.core.client.impl.SchedulerImpl" />
</replace-with>
-
+
+
+ <!-- Generators for serializators for classes used in communication between
+ server and client -->
+ <generate-with
+ class="com.vaadin.terminal.gwt.widgetsetutils.SerializerMapGenerator">
+ <when-type-is
+ class="com.vaadin.terminal.gwt.client.communication.SerializerMap" />
+ </generate-with>
+
<replace-with class="com.vaadin.terminal.gwt.client.VDebugConsole">
<when-type-is class="com.vaadin.terminal.gwt.client.Console" />
</replace-with>
- <!-- Use our own history impl for IE to workaround #2931. -->
- <replace-with class="com.vaadin.terminal.gwt.client.HistoryImplIEVaadin">
- <when-type-is class="com.google.gwt.user.client.impl.HistoryImpl" />
- <when-property-is name="user.agent" value="ie6" />
- </replace-with>
-
<generate-with
- class="com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator">
+ class="com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator">
<when-type-is class="com.vaadin.terminal.gwt.client.WidgetMap" />
</generate-with>
@@ -39,47 +47,31 @@
class="com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory" />
</generate-with>
- <!-- Fall through to this rule for everything but IE -->
- <replace-with
- class="com.vaadin.terminal.gwt.client.ui.UploadIFrameOnloadStrategy">
- <when-type-is
- class="com.vaadin.terminal.gwt.client.ui.UploadIFrameOnloadStrategy" />
- </replace-with>
-
- <replace-with
- class="com.vaadin.terminal.gwt.client.ui.UploadIFrameOnloadStrategyIE">
- <when-type-is
- class="com.vaadin.terminal.gwt.client.ui.UploadIFrameOnloadStrategy" />
- <any>
- <when-property-is name="user.agent" value="ie6" />
- <when-property-is name="user.agent" value="ie8" />
- </any>
- </replace-with>
+ <!-- Generate client side proxies for client to server RPC interfaces -->
+ <generate-with
+ class="com.vaadin.terminal.gwt.widgetsetutils.RpcProxyGenerator">
+ <when-type-assignable
+ class="com.vaadin.terminal.gwt.client.communication.ServerRpc" />
+ </generate-with>
- <!-- Fall through to this rule for everything but IE -->
- <replace-with
- class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper">
- <when-type-is
- class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper" />
- </replace-with>
+ <!-- Generate client side proxies for client to server RPC interfaces -->
+ <generate-with
+ class="com.vaadin.terminal.gwt.widgetsetutils.RpcProxyCreatorGenerator">
+ <when-type-assignable
+ class="com.vaadin.terminal.gwt.client.communication.RpcProxy.RpcProxyCreator" />
+ </generate-with>
- <replace-with
- class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapperIE">
- <when-type-is
- class="com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper" />
- <any>
- <when-property-is name="user.agent" value="ie6" />
- <when-property-is name="user.agent" value="ie8" />
- </any>
- </replace-with>
-
- <!-- Workaround for #6682. Remove when fixed in GWT. -->
- <replace-with class="com.google.gwt.dom.client.VaadinDOMImplSafari">
- <when-type-is class="com.google.gwt.dom.client.DOMImpl" />
- <when-property-is name="user.agent" value="safari" />
- </replace-with>
+ <!-- Generate client side RPC manager for server to client RPC -->
+ <generate-with
+ class="com.vaadin.terminal.gwt.widgetsetutils.RpcManagerGenerator">
+ <when-type-assignable
+ class="com.vaadin.terminal.gwt.client.communication.RpcManager" />
+ </generate-with>
<entry-point class="com.vaadin.terminal.gwt.client.ApplicationConfiguration" />
+ <!-- Use the new cross site linker to get a nocache.js without document.write -->
+ <add-linker name="xsiframe" />
+
</module>
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSetBrowserSpecificOverrides.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSetBrowserSpecificOverrides.gwt.xml
new file mode 100644
index 0000000000..b5ab61df64
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSetBrowserSpecificOverrides.gwt.xml
@@ -0,0 +1,53 @@
+<module>
+ <!-- This GWT module defines the browser specific overrides used by Vaadin -->
+
+ <!-- Hint for WidgetSetBuilder not to automatically update the file -->
+ <!-- WS Compiler: manually edited -->
+
+ <!-- Fall through to this rule for everything but IE -->
+ <replace-with
+ class="com.vaadin.terminal.gwt.client.ui.upload.UploadIFrameOnloadStrategy">
+ <when-type-is
+ class="com.vaadin.terminal.gwt.client.ui.upload.UploadIFrameOnloadStrategy" />
+ </replace-with>
+
+ <replace-with
+ class="com.vaadin.terminal.gwt.client.ui.upload.UploadIFrameOnloadStrategyIE">
+ <when-type-is
+ class="com.vaadin.terminal.gwt.client.ui.upload.UploadIFrameOnloadStrategy" />
+ <any>
+ <when-property-is name="user.agent" value="ie8" />
+ </any>
+ </replace-with>
+
+ <!-- Fall through to this rule for everything but IE -->
+ <replace-with class="com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper">
+ <when-type-is class="com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper" />
+ </replace-with>
+
+ <replace-with class="com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapperIE">
+ <when-type-is class="com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper" />
+ <any>
+ <when-property-is name="user.agent" value="ie8" />
+ </any>
+ </replace-with>
+
+ <!-- Fall through to this rule for everything but IE -->
+ <replace-with class="com.vaadin.terminal.gwt.client.LayoutManager">
+ <when-type-is class="com.vaadin.terminal.gwt.client.LayoutManager" />
+ </replace-with>
+
+ <replace-with class="com.vaadin.terminal.gwt.client.LayoutManagerIE8">
+ <when-type-is class="com.vaadin.terminal.gwt.client.LayoutManager" />
+ <any>
+ <when-property-is name="user.agent" value="ie8" />
+ </any>
+ </replace-with>
+
+ <!-- Workaround for #6682. Remove when fixed in GWT. -->
+ <replace-with class="com.google.gwt.dom.client.VaadinDOMImplSafari">
+ <when-type-is class="com.google.gwt.dom.client.DOMImpl" />
+ <when-property-is name="user.agent" value="safari" />
+ </replace-with>
+
+</module>
diff --git a/src/com/vaadin/terminal/gwt/client/AbstractFieldState.java b/src/com/vaadin/terminal/gwt/client/AbstractFieldState.java
new file mode 100644
index 0000000000..3a66a01f23
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/AbstractFieldState.java
@@ -0,0 +1,137 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.vaadin.terminal.gwt.client.ui.TabIndexState;
+import com.vaadin.ui.AbstractField;
+
+/**
+ * Shared state for {@link AbstractField}.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public class AbstractFieldState extends ComponentState implements TabIndexState {
+ private boolean propertyReadOnly = false;
+ private boolean hideErrors = false;
+ private boolean required = false;
+ private boolean modified = false;
+
+ /**
+ * The tab order number of this field.
+ */
+ private int tabIndex = 0;
+
+ /**
+ * Checks if the property data source for the Field is in read only mode.
+ * This affects the read only state of the field itself.
+ *
+ * @return true if there is a property data source and it is set to read
+ * only, false otherwise
+ */
+ public boolean isPropertyReadOnly() {
+ return propertyReadOnly;
+ }
+
+ /**
+ * Sets the read only state of the property data source.
+ *
+ * @param propertyReadOnly
+ * true if the property data source if read only, false otherwise
+ */
+ public void setPropertyReadOnly(boolean propertyReadOnly) {
+ this.propertyReadOnly = propertyReadOnly;
+ }
+
+ /**
+ * Returns true if the component will hide any errors even if the error
+ * message is set.
+ *
+ * @return true if error messages are disabled
+ */
+ public boolean isHideErrors() {
+ return hideErrors;
+ }
+
+ /**
+ * Sets whether the component should hide any errors even if the error
+ * message is set.
+ *
+ * This is used e.g. on forms to hide error messages for invalid fields
+ * before the first user actions.
+ *
+ * @param hideErrors
+ * true if error messages should be hidden
+ */
+ public void setHideErrors(boolean hideErrors) {
+ this.hideErrors = hideErrors;
+ }
+
+ /**
+ * Is the field required. Required fields must filled by the user.
+ *
+ * See AbstractField#isRequired() for more information.
+ *
+ * @return <code>true</code> if the field is required, otherwise
+ * <code>false</code>.
+ */
+ public boolean isRequired() {
+ return required;
+ }
+
+ /**
+ * Sets the field required. Required fields must filled by the user.
+ *
+ * See AbstractField#setRequired(boolean) for more information.
+ *
+ * @param required
+ * Is the field required.
+ */
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
+ /**
+ * Has the contents of the field been modified, i.e. has the value been
+ * updated after it was read from the data source.
+ *
+ * @return true if the field has been modified, false otherwise
+ */
+ public boolean isModified() {
+ return modified;
+ }
+
+ /**
+ * Setter for the modified flag, toggled when the contents of the field is
+ * modified by the user.
+ *
+ * @param modified
+ * the new modified state
+ *
+ */
+ public void setModified(boolean modified) {
+ this.modified = modified;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ComponentState#getTabIndex()
+ */
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ui.TabIndexState#setTabIndex(int)
+ */
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
index a60fb808a1..170e949116 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
@@ -5,22 +5,180 @@ package com.vaadin.terminal.gwt.client;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.core.client.JavaScriptObject;
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.user.client.Command;
import com.google.gwt.user.client.Timer;
-import com.vaadin.terminal.gwt.client.ui.VUnknownComponent;
+import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class ApplicationConfiguration implements EntryPoint {
/**
+ * Helper class for reading configuration options from the bootstap
+ * javascript
+ *
+ * @since 7.0
+ */
+ private static class JsoConfiguration extends JavaScriptObject {
+ protected JsoConfiguration() {
+ // JSO Constructor
+ }
+
+ /**
+ * Reads a configuration parameter as a string. Please note that the
+ * javascript value of the parameter should also be a string, or else an
+ * undefined exception may be thrown.
+ *
+ * @param name
+ * name of the configuration parameter
+ * @return value of the configuration parameter, or <code>null</code> if
+ * not defined
+ */
+ private native String getConfigString(String name)
+ /*-{
+ var value = this.getConfig(name);
+ if (value === null || value === undefined) {
+ return null;
+ } else {
+ return value +"";
+ }
+ }-*/;
+
+ /**
+ * Reads a configuration parameter as a boolean object. Please note that
+ * the javascript value of the parameter should also be a boolean, or
+ * else an undefined exception may be thrown.
+ *
+ * @param name
+ * name of the configuration parameter
+ * @return boolean value of the configuration paramter, or
+ * <code>null</code> if no value is defined
+ */
+ private native Boolean getConfigBoolean(String name)
+ /*-{
+ var value = this.getConfig(name);
+ if (value === null || value === undefined) {
+ return null;
+ } else {
+ return @java.lang.Boolean::valueOf(Z)(value);
+ }
+ }-*/;
+
+ /**
+ * Reads a configuration parameter as an integer object. Please note
+ * that the javascript value of the parameter should also be an integer,
+ * or else an undefined exception may be thrown.
+ *
+ * @param name
+ * name of the configuration parameter
+ * @return integer value of the configuration paramter, or
+ * <code>null</code> if no value is defined
+ */
+ private native Integer getConfigInteger(String name)
+ /*-{
+ var value = this.getConfig(name);
+ if (value === null || value === undefined) {
+ return null;
+ } else {
+ return @java.lang.Integer::valueOf(I)(value);
+ }
+ }-*/;
+
+ /**
+ * Reads a configuration parameter as an {@link ErrorMessage} object.
+ * Please note that the javascript value of the parameter should also be
+ * an object with appropriate fields, or else an undefined exception may
+ * be thrown when calling this method or when calling methods on the
+ * returned object.
+ *
+ * @param name
+ * name of the configuration parameter
+ * @return error message with the given name, or <code>null</code> if no
+ * value is defined
+ */
+ private native ErrorMessage getConfigError(String name)
+ /*-{
+ return this.getConfig(name);
+ }-*/;
+
+ /**
+ * Returns a native javascript object containing version information
+ * from the server.
+ *
+ * @return a javascript object with the version information
+ */
+ private native JavaScriptObject getVersionInfoJSObject()
+ /*-{
+ return this.getConfig("versionInfo");
+ }-*/;
+
+ /**
+ * Gets the version of the Vaadin framework used on the server.
+ *
+ * @return a string with the version
+ *
+ * @see com.vaadin.terminal.gwt.server.AbstractApplicationServlet#VERSION
+ */
+ private native String getVaadinVersion()
+ /*-{
+ return this.getConfig("versionInfo").vaadinVersion;
+ }-*/;
+
+ /**
+ * Gets the version of the application running on the server.
+ *
+ * @return a string with the application version
+ *
+ * @see com.vaadin.Application#getVersion()
+ */
+ private native String getApplicationVersion()
+ /*-{
+ return this.getConfig("versionInfo").applicationVersion;
+ }-*/;
+
+ private native String getUIDL()
+ /*-{
+ return this.getConfig("uidl");
+ }-*/;
+ }
+
+ /**
+ * Wraps a native javascript object containing fields for an error message
+ *
+ * @since 7.0
+ */
+ public static final class ErrorMessage extends JavaScriptObject {
+
+ protected ErrorMessage() {
+ // JSO constructor
+ }
+
+ public final native String getCaption()
+ /*-{
+ return this.caption;
+ }-*/;
+
+ public final native String getMessage()
+ /*-{
+ return this.message;
+ }-*/;
+
+ public final native String getUrl()
+ /*-{
+ return this.url;
+ }-*/;
+ }
+
+ /**
* Builds number. For example 0-custom_tag in 5.0.0-custom_tag.
*/
public static final String VERSION;
@@ -39,34 +197,30 @@ public class ApplicationConfiguration implements EntryPoint {
private String id;
private String themeUri;
private String appUri;
- private JavaScriptObject versionInfo;
- private String windowName;
+ private int rootId;
private boolean standalone;
- private String communicationErrorCaption;
- private String communicationErrorMessage;
- private String communicationErrorUrl;
- private String authorizationErrorCaption;
- private String authorizationErrorMessage;
- private String authorizationErrorUrl;
- private String requiredWidgetset;
+ private ErrorMessage communicationError;
+ private ErrorMessage authorizationError;
private boolean useDebugIdInDom = true;
private boolean usePortletURLs = false;
private String portletUidlURLBase;
- private HashMap<String, String> unknownComponents;
+ private HashMap<Integer, String> unknownComponents;
- private Class<? extends Paintable>[] classes = new Class[1024];
+ private Class<? extends ComponentConnector>[] classes = new Class[1024];
- private String windowId;
+ private boolean browserDetailsSent = false;
static// TODO consider to make this hashmap per application
LinkedList<Command> callbacks = new LinkedList<Command>();
private static int widgetsLoading;
- private static ArrayList<ApplicationConnection> unstartedApplications = new ArrayList<ApplicationConnection>();
private static ArrayList<ApplicationConnection> runningApplications = new ArrayList<ApplicationConnection>();
+ private Map<Integer, Integer> componentInheritanceMap = new HashMap<Integer, Integer>();
+ private Map<Integer, String> tagToServerSideClassName = new HashMap<Integer, String>();
+
public boolean usePortletURLs() {
return usePortletURLs;
}
@@ -89,6 +243,13 @@ public class ApplicationConfiguration implements EntryPoint {
return appUri;
}
+ public String getThemeName() {
+ String uri = getThemeUri();
+ String themeName = uri.substring(uri.lastIndexOf('/'));
+ themeName = themeName.replaceAll("[^a-zA-Z0-9]", "");
+ return themeName;
+ }
+
public String getThemeUri() {
return themeUri;
}
@@ -98,6 +259,16 @@ public class ApplicationConfiguration implements EntryPoint {
}
/**
+ * Gets the initial UIDL from the DOM, if it was provided during the init
+ * process.
+ *
+ * @return
+ */
+ public String getUIDL() {
+ return getJsoConfiguration(id).getUIDL();
+ }
+
+ /**
* @return true if the application is served by std. Vaadin servlet and is
* considered to be the only or main content of the host page.
*/
@@ -105,174 +276,99 @@ public class ApplicationConfiguration implements EntryPoint {
return standalone;
}
- public void setInitialWindowName(String name) {
- windowName = name;
- }
-
- public String getInitialWindowName() {
- return windowName;
+ /**
+ * Gets the root if of this application instance. The root id should be
+ * included in every request originating from this instance in order to
+ * associate it with the right Root instance on the server.
+ *
+ * @return the root id
+ */
+ public int getRootId() {
+ return rootId;
}
public JavaScriptObject getVersionInfoJSObject() {
- return versionInfo;
+ return getJsoConfiguration(id).getVersionInfoJSObject();
}
- public String getCommunicationErrorCaption() {
- return communicationErrorCaption;
+ public ErrorMessage getCommunicationError() {
+ return communicationError;
}
- public String getCommunicationErrorMessage() {
- return communicationErrorMessage;
+ public ErrorMessage getAuthorizationError() {
+ return authorizationError;
}
- public String getCommunicationErrorUrl() {
- return communicationErrorUrl;
- }
+ /**
+ * Reads the configuration values defined by the bootstrap javascript.
+ */
+ private void loadFromDOM() {
+ JsoConfiguration jsoConfiguration = getJsoConfiguration(id);
+ appUri = jsoConfiguration.getConfigString("appUri");
+ if (appUri != null && !appUri.endsWith("/")) {
+ appUri += '/';
+ }
+ themeUri = jsoConfiguration.getConfigString("themeUri");
+ rootId = jsoConfiguration.getConfigInteger("rootId").intValue();
- public String getAuthorizationErrorCaption() {
- return authorizationErrorCaption;
- }
+ // null -> true
+ useDebugIdInDom = jsoConfiguration.getConfigBoolean("useDebugIdInDom") != Boolean.FALSE;
- public String getAuthorizationErrorMessage() {
- return authorizationErrorMessage;
- }
+ // null -> false
+ usePortletURLs = jsoConfiguration.getConfigBoolean("usePortletURLs") == Boolean.TRUE;
- public String getAuthorizationErrorUrl() {
- return authorizationErrorUrl;
- }
+ portletUidlURLBase = jsoConfiguration
+ .getConfigString("portletUidlURLBase");
- public String getRequiredWidgetset() {
- return requiredWidgetset;
- }
+ // null -> false
+ standalone = jsoConfiguration.getConfigBoolean("standalone") == Boolean.TRUE;
- private native void loadFromDOM()
- /*-{
+ communicationError = jsoConfiguration.getConfigError("comErrMsg");
+ authorizationError = jsoConfiguration.getConfigError("authErrMsg");
- var id = this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::id;
- if($wnd.vaadin.vaadinConfigurations && $wnd.vaadin.vaadinConfigurations[id]) {
- var jsobj = $wnd.vaadin.vaadinConfigurations[id];
- var uri = jsobj.appUri;
- if(uri != null && uri[uri.length -1] != "/") {
- uri = uri + "/";
- }
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::appUri = uri;
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::themeUri = jsobj.themeUri;
- if(jsobj.windowName) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::windowName = jsobj.windowName;
- }
- if('useDebugIdInDom' in jsobj && typeof(jsobj.useDebugIdInDom) == "boolean") {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::useDebugIdInDom = jsobj.useDebugIdInDom;
- }
- if(jsobj.versionInfo) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::versionInfo = jsobj.versionInfo;
- }
- if(jsobj.comErrMsg) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::communicationErrorCaption = jsobj.comErrMsg.caption;
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::communicationErrorMessage = jsobj.comErrMsg.message;
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::communicationErrorUrl = jsobj.comErrMsg.url;
- }
- if(jsobj.authErrMsg) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::authorizationErrorCaption = jsobj.authErrMsg.caption;
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::authorizationErrorMessage = jsobj.authErrMsg.message;
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::authorizationErrorUrl = jsobj.authErrMsg.url;
- }
- if (jsobj.usePortletURLs) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::usePortletURLs = jsobj.usePortletURLs;
- }
- if (jsobj.portletUidlURLBase) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::portletUidlURLBase = jsobj.portletUidlURLBase;
- }
- if (jsobj.standalone) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::standalone = true;
- }
- if (jsobj.widgetset) {
- this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::requiredWidgetset = jsobj.widgetset;
- }
- } else {
- $wnd.alert("Vaadin app failed to initialize: " + this.id);
+ // boostrap sets initPending to false if it has sent the browser details
+ if (jsoConfiguration.getConfigBoolean("initPending") == Boolean.FALSE) {
+ setBrowserDetailsSent();
}
- }-*/;
+ }
/**
- * Inits the ApplicationConfiguration by reading the DOM and instantiating
- * ApplicationConnections accordingly. Call {@link #startNextApplication()}
- * to actually start the applications.
+ * Starts the application with a given id by reading the configuration
+ * options stored by the bootstrap javascript.
*
- * @param widgetset
- * the widgetset that is running the apps
+ * @param applicationId
+ * id of the application to load, this is also the id of the html
+ * element into which the application should be rendered.
*/
- public static void initConfigurations() {
-
- ArrayList<String> appIds = new ArrayList<String>();
- loadAppIdListFromDOM(appIds);
-
- for (Iterator<String> it = appIds.iterator(); it.hasNext();) {
- String appId = it.next();
- ApplicationConfiguration appConf = getConfigFromDOM(appId);
- if (canStartApplication(appConf)) {
+ public static void startApplication(final String applicationId) {
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ public void execute() {
+ ApplicationConfiguration appConf = getConfigFromDOM(applicationId);
ApplicationConnection a = GWT
.create(ApplicationConnection.class);
a.init(widgetSet, appConf);
- unstartedApplications.add(a);
- consumeApplication(appId);
- } else {
- VConsole.log("Application "
- + appId
- + " was not started. Provided widgetset did not match with this module.");
+ a.start();
+ runningApplications.add(a);
}
- }
-
+ });
}
- /**
- * Marks an applicatin with given id to be initialized. Suggesting other
- * modules should not try to start this application anymore.
- *
- * @param appId
- */
- private native static void consumeApplication(String appId)
- /*-{
- $wnd.vaadin.vaadinConfigurations[appId].initialized = true;
- }-*/;
-
- private static boolean canStartApplication(ApplicationConfiguration appConf) {
- return appConf.getRequiredWidgetset() == null
- || appConf.getRequiredWidgetset().equals(GWT.getModuleName());
+ public static List<ApplicationConnection> getRunningApplications() {
+ return runningApplications;
}
/**
- * Starts the next unstarted application. The WidgetSet should call this
- * once to start the first application; after that, each application should
- * call this once it has started. This ensures that the applications are
- * started synchronously, which is neccessary to avoid session-id problems.
+ * Gets the configuration object for a specific application from the
+ * bootstrap javascript.
*
- * @return true if an unstarted application was found
+ * @param appId
+ * the id of the application to get configuration data for
+ * @return a native javascript object containing the configuration data
*/
- public static boolean startNextApplication() {
- if (unstartedApplications.size() > 0) {
- ApplicationConnection a = unstartedApplications.remove(0);
- a.start();
- runningApplications.add(a);
- return true;
- } else {
- deferredWidgetLoader = new DeferredWidgetLoader();
- return false;
- }
- }
-
- public static List<ApplicationConnection> getRunningApplications() {
- return runningApplications;
- }
-
- private native static void loadAppIdListFromDOM(ArrayList<String> list)
+ private native static JsoConfiguration getJsoConfiguration(String appId)
/*-{
- var j;
- for(j in $wnd.vaadin.vaadinConfigurations) {
- if(!$wnd.vaadin.vaadinConfigurations[j].initialized) {
- list.@java.util.Collection::add(Ljava/lang/Object;)(j);
- }
- }
+ return $wnd.vaadin.getApp(appId);
}-*/;
public static ApplicationConfiguration getConfigFromDOM(String appId) {
@@ -282,27 +378,34 @@ public class ApplicationConfiguration implements EntryPoint {
return conf;
}
- public native String getServletVersion()
- /*-{
- return this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::versionInfo.vaadinVersion;
- }-*/;
+ public String getServletVersion() {
+ return getJsoConfiguration(id).getVaadinVersion();
+ }
- public native String getApplicationVersion()
- /*-{
- return this.@com.vaadin.terminal.gwt.client.ApplicationConfiguration::versionInfo.applicationVersion;
- }-*/;
+ public String getApplicationVersion() {
+ return getJsoConfiguration(id).getApplicationVersion();
+ }
public boolean useDebugIdInDOM() {
return useDebugIdInDom;
}
- public Class<? extends Paintable> getWidgetClassByEncodedTag(String tag) {
+ public Class<? extends ComponentConnector> getWidgetClassByEncodedTag(
+ int tag) {
try {
- int parseInt = Integer.parseInt(tag);
- return classes[parseInt];
+ return classes[tag];
} catch (Exception e) {
// component was not present in mappings
- return VUnknownComponent.class;
+ return UnknownComponentConnector.class;
+ }
+ }
+
+ public void addComponentInheritanceInfo(ValueMap valueMap) {
+ JsArrayString keyArray = valueMap.getKeyArray();
+ for (int i = 0; i < keyArray.length(); i++) {
+ String key = keyArray.get(i);
+ int value = valueMap.getInt(key);
+ componentInheritanceMap.put(Integer.parseInt(key), value);
}
}
@@ -311,27 +414,31 @@ public class ApplicationConfiguration implements EntryPoint {
for (int i = 0; i < keyArray.length(); i++) {
String key = keyArray.get(i).intern();
int value = valueMap.getInt(key);
- classes[value] = widgetSet.getImplementationByClassName(key);
- if (classes[value] == VUnknownComponent.class) {
+ tagToServerSideClassName.put(value, key);
+ }
+
+ for (int i = 0; i < keyArray.length(); i++) {
+ String key = keyArray.get(i).intern();
+ int value = valueMap.getInt(key);
+ classes[value] = widgetSet.getConnectorClassByTag(value, this);
+ if (classes[value] == UnknownComponentConnector.class) {
if (unknownComponents == null) {
- unknownComponents = new HashMap<String, String>();
+ unknownComponents = new HashMap<Integer, String>();
}
- unknownComponents.put("" + value, key);
- } else if (key == "com.vaadin.ui.Window") {
- windowId = "" + value;
+ unknownComponents.put(value, key);
}
}
}
- /**
- * @return the integer value that is used to code top level windows
- * "com.vaadin.ui.Window"
- */
- String getEncodedWindowTag() {
- return windowId;
+ public Integer getParentTag(int tag) {
+ return componentInheritanceMap.get(tag);
}
- String getUnknownServerClassNameByEncodedTagName(String tag) {
+ public String getServerSideClassNameForTag(Integer tag) {
+ return tagToServerSideClassName.get(tag);
+ }
+
+ String getUnknownServerClassNameByTag(int tag) {
if (unknownComponents != null) {
return unknownComponents.get(tag);
}
@@ -398,7 +505,7 @@ public class ApplicationConfiguration implements EntryPoint {
public void run() {
pending = false;
if (!isBusy()) {
- Class<? extends Paintable> nextType = getNextType();
+ Class<? extends ComponentConnector> nextType = getNextType();
if (nextType == null) {
// ensured that all widgets are loaded
deferredWidgetLoader = null;
@@ -411,8 +518,8 @@ public class ApplicationConfiguration implements EntryPoint {
}
}
- private Class<? extends Paintable> getNextType() {
- Class<? extends Paintable>[] deferredLoadedWidgets = widgetSet
+ private Class<? extends ComponentConnector> getNextType() {
+ Class<? extends ComponentConnector>[] deferredLoadedWidgets = widgetSet
.getDeferredLoadedWidgets();
if (deferredLoadedWidgets.length <= nextWidgetIndex) {
return null;
@@ -443,10 +550,6 @@ public class ApplicationConfiguration implements EntryPoint {
public void onModuleLoad() {
- // Enable IE6 Background image caching
- if (BrowserInfo.get().isIE6()) {
- enableIE6BackgroundImageCache();
- }
// Prepare VConsole for debugging
if (isDebugMode()) {
Console console = GWT.create(Console.class);
@@ -472,21 +575,22 @@ public class ApplicationConfiguration implements EntryPoint {
}
});
- initConfigurations();
- startNextApplication();
+ registerCallback(GWT.getModuleName());
+ deferredWidgetLoader = new DeferredWidgetLoader();
}
- // From ImageSrcIE6
- private static native void enableIE6BackgroundImageCache()
+ /**
+ * Registers that callback that the bootstrap javascript uses to start
+ * applications once the widgetset is loaded and all required information is
+ * available
+ *
+ * @param widgetsetName
+ * the name of this widgetset
+ */
+ public native static void registerCallback(String widgetsetName)
/*-{
- // Fix IE background image refresh bug, present through IE6
- // see http://www.mister-pixel.com/#Content__state=is_that_simple
- // this only works with IE6 SP1+
- try {
- $doc.execCommand("BackgroundImageCache", false, true);
- } catch (e) {
- // ignore error on other browsers
- }
+ var callbackHandler = @com.vaadin.terminal.gwt.client.ApplicationConfiguration::startApplication(Ljava/lang/String;);
+ $wnd.vaadin.registerWidgetset(widgetsetName, callbackHandler);
}-*/;
/**
@@ -518,4 +622,25 @@ public class ApplicationConfiguration implements EntryPoint {
return re.test(uri);
}-*/;
+ /**
+ * Checks whether information from the web browser (e.g. uri fragment and
+ * screen size) has been sent to the server.
+ *
+ * @return <code>true</code> if browser information has already been sent
+ *
+ * @see ApplicationConnection#getNativeBrowserDetailsParameters(String)
+ */
+ public boolean isBrowserDetailsSent() {
+ return browserDetailsSent;
+ }
+
+ /**
+ * Registers that the browser details have been sent.
+ * {@link #isBrowserDetailsSent()} will return
+ * <code> after this method has been invoked.
+ */
+ public void setBrowserDetailsSent() {
+ browserDetailsSent = true;
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index de7ad83b54..be6d578112 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -5,44 +5,53 @@
package com.vaadin.terminal.gwt.client;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
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.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.json.client.JSONArray;
+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.Event;
-import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.FocusWidget;
-import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
+import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.RpcManager;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
import com.vaadin.terminal.gwt.client.ui.VContextMenu;
-import com.vaadin.terminal.gwt.client.ui.VNotification;
-import com.vaadin.terminal.gwt.client.ui.VNotification.HideEvent;
-import com.vaadin.terminal.gwt.client.ui.VView;
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification.HideEvent;
+import com.vaadin.terminal.gwt.client.ui.root.RootConnector;
+import com.vaadin.terminal.gwt.client.ui.window.WindowConnector;
import com.vaadin.terminal.gwt.server.AbstractCommunicationManager;
/**
@@ -50,12 +59,10 @@ import com.vaadin.terminal.gwt.server.AbstractCommunicationManager;
* communication with its server side counterpart
* {@link AbstractCommunicationManager}.
*
- * Client-side widgets receive updates from the corresponding server-side
- * components as calls to
- * {@link Paintable#updateFromUIDL(UIDL, ApplicationConnection)} (not to be
- * confused with the server side interface {@link com.vaadin.terminal.Paintable}
- * ). Any client-side changes (typically resulting from user actions) are sent
- * back to the server as variable changes (see {@link #updateVariable()}).
+ * Client-side connectors receive updates from the corresponding server-side
+ * connector (typically component) as state updates or RPC calls. The connector
+ * has the possibility to communicate back with its server side counter part
+ * through RPC calls.
*
* TODO document better
*
@@ -65,27 +72,29 @@ public class ApplicationConnection {
// This indicates the whole page is generated by us (not embedded)
public static final String GENERATED_BODY_CLASSNAME = "v-generated-body";
- private static final String MODIFIED_CLASSNAME = "v-modified";
+ public static final String MODIFIED_CLASSNAME = "v-modified";
public static final String DISABLED_CLASSNAME = "v-disabled";
- private static final String REQUIRED_CLASSNAME_EXT = "-required";
+ public static final String REQUIRED_CLASSNAME_EXT = "-required";
- private static final String ERROR_CLASSNAME_EXT = "-error";
+ public static final String ERROR_CLASSNAME_EXT = "-error";
- public static final char VAR_RECORD_SEPARATOR = '\u001e';
-
- public static final char VAR_FIELD_SEPARATOR = '\u001f';
+ public static final String UPDATE_VARIABLE_INTERFACE = "v";
+ public static final String UPDATE_VARIABLE_METHOD = "v";
public static final char VAR_BURST_SEPARATOR = '\u001d';
- public static final char VAR_ARRAYITEM_SEPARATOR = '\u001c';
-
public static final char VAR_ESCAPE_CHARACTER = '\u001b';
public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key";
/**
+ * Name of the parameter used to transmit root ids back and forth
+ */
+ public static final String ROOT_ID_PARAMETER = "rootId";
+
+ /**
* @deprecated use UIDL_SECURITY_TOKEN_ID instead
*/
@Deprecated
@@ -93,9 +102,6 @@ public class ApplicationConnection {
public static final String PARAM_UNLOADBURST = "onunloadburst";
- public static final String ATTRIBUTE_DESCRIPTION = "description";
- public static final String ATTRIBUTE_ERROR = "error";
-
/**
* 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
@@ -117,15 +123,14 @@ public class ApplicationConnection {
*/
public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh";
+ private final boolean debugLogging = false;
+
// will hold the UIDL security key (for XSS protection) once received
private String uidlSecurityKey = "init";
private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
- private final ArrayList<String> pendingVariables = new ArrayList<String>();
-
- private final ComponentDetailMap idToPaintableDetail = ComponentDetailMap
- .create();
+ private ArrayList<MethodInvocation> pendingInvocations = new ArrayList<MethodInvocation>();
private WidgetSet widgetSet;
@@ -136,17 +141,19 @@ public class ApplicationConnection {
private Timer loadTimer3;
private Element loadElement;
- private final VView view;
+ private final RootConnector rootConnector;
protected boolean applicationRunning = false;
private boolean hasActiveRequest = false;
+ protected boolean cssLoaded = false;
+
/** Parameters for this application connection loaded from the web-page */
private ApplicationConfiguration configuration;
/** List of pending variable change bursts that must be submitted in order */
- private final ArrayList<ArrayList<String>> pendingVariableBursts = new ArrayList<ArrayList<String>>();
+ private final ArrayList<ArrayList<MethodInvocation>> pendingBursts = new ArrayList<ArrayList<MethodInvocation>>();
/** Timer for automatic refirect to SessionExpiredURL */
private Timer redirectTimer;
@@ -154,21 +161,42 @@ public class ApplicationConnection {
/** redirectTimer scheduling interval in seconds */
private int sessionExpirationInterval;
- private ArrayList<Paintable> relativeSizeChanges = new ArrayList<Paintable>();;
- private ArrayList<Paintable> componentCaptionSizeChanges = new ArrayList<Paintable>();;
+ private ArrayList<Widget> componentCaptionSizeChanges = new ArrayList<Widget>();
private Date requestStartTime;
private boolean validatingLayouts = false;
- private Set<Paintable> zeroWidthComponents = null;
+ private Set<ComponentConnector> zeroWidthComponents = null;
- private Set<Paintable> zeroHeightComponents = null;
+ private Set<ComponentConnector> zeroHeightComponents = null;
- private Set<String> unregistryBag = new HashSet<String>();
+ private final LayoutManager layoutManager;
+
+ private final RpcManager rpcManager;
+
+ public static class MultiStepDuration extends Duration {
+ private int previousStep = elapsedMillis();
+
+ public void logDuration(String message) {
+ logDuration(message, 0);
+ }
+
+ public void logDuration(String message, int minDuration) {
+ int currentTime = elapsedMillis();
+ int stepDuration = currentTime - previousStep;
+ if (stepDuration >= minDuration) {
+ VConsole.log(message + ": " + stepDuration + " ms");
+ }
+ previousStep = currentTime;
+ }
+ }
public ApplicationConnection() {
- view = GWT.create(VView.class);
+ rootConnector = GWT.create(RootConnector.class);
+ rpcManager = GWT.create(RpcManager.class);
+ layoutManager = GWT.create(LayoutManager.class);
+ layoutManager.setConnection(this);
}
public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) {
@@ -186,7 +214,6 @@ public class ApplicationConnection {
this.widgetSet = widgetSet;
configuration = cnf;
- windowName = configuration.getInitialWindowName();
ComponentLocator componentLocator = new ComponentLocator(this);
@@ -198,7 +225,7 @@ public class ApplicationConnection {
initializeClientHooks();
- view.init(cnf.getRootPanelId(), this);
+ rootConnector.init(cnf.getRootPanelId(), this);
showLoadingIndicator();
}
@@ -209,10 +236,19 @@ public class ApplicationConnection {
* failed to start. This ensures that the applications are started in order,
* to avoid session-id problems.
*
- * @return
*/
public void start() {
- repaintAll();
+ String jsonText = configuration.getUIDL();
+ if (jsonText == null) {
+ // inital UIDL not in DOM, request later
+ repaintAll();
+ } else {
+ // Update counter so TestBench knows something is still going on
+ hasActiveRequest = true;
+
+ // initial UIDL provided in DOM, continue as if returned by request
+ handleJSONText(jsonText, -1);
+ }
}
private native void initializeTestbenchHooks(
@@ -224,14 +260,13 @@ public class ApplicationConnection {
return ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::hasActiveRequest()()
|| ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::isExecutingDeferredCommands()();
});
-
var vi = ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::getVersionInfo()();
if (vi) {
client.getVersionInfo = function() {
return vi;
}
}
-
+
client.getProfilingData = $entry(function() {
var pd = [
ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::lastProcessingTime,
@@ -248,10 +283,6 @@ public class ApplicationConnection {
return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
});
- if (!$wnd.vaadin.clients) {
- $wnd.vaadin.clients = {};
- }
-
$wnd.vaadin.clients[TTAppId] = client;
}-*/;
@@ -379,36 +410,31 @@ public class ApplicationConnection {
private String getRepaintAllParameters() {
// collect some client side data that will be sent to server on
// initial uidl request
- int clientHeight = Window.getClientHeight();
- int clientWidth = Window.getClientWidth();
- com.google.gwt.dom.client.Element pe = view.getElement()
- .getParentElement();
- int offsetHeight = pe.getOffsetHeight();
- int offsetWidth = pe.getOffsetWidth();
- int screenWidth = BrowserInfo.get().getScreenWidth();
- int screenHeight = BrowserInfo.get().getScreenHeight();
- int tzOffset = BrowserInfo.get().getTimezoneOffset();
- int rtzOffset = BrowserInfo.get().getRawTimezoneOffset();
- int dstDiff = BrowserInfo.get().getDSTSavings();
- boolean dstInEffect = BrowserInfo.get().isDSTInEffect();
- long curDate = BrowserInfo.get().getCurrentDate().getTime();
+ String nativeBootstrapParameters = getNativeBrowserDetailsParameters(getConfiguration()
+ .getRootPanelId());
String widgetsetVersion = ApplicationConfiguration.VERSION;
- String token = History.getToken();
-
// TODO figure out how client and view size could be used better on
// server. screen size can be accessed via Browser object, but other
// values currently only via transaction listener.
- String parameters = "repaintAll=1&" + "sh=" + screenHeight + "&sw="
- + screenWidth + "&cw=" + clientWidth + "&ch=" + clientHeight
- + "&vw=" + offsetWidth + "&vh=" + offsetHeight + "&fr=" + token
- + "&tzo=" + tzOffset + "&rtzo=" + rtzOffset + "&dstd="
- + dstDiff + "&dston=" + dstInEffect + "&curdate=" + curDate
- + "&wsver=" + widgetsetVersion
- + (BrowserInfo.get().isTouchDevice() ? "&td=1" : "");
+ String parameters = "repaintAll=1&" + nativeBootstrapParameters
+ + "&wsver=" + widgetsetVersion;
return parameters;
}
+ /**
+ * Gets the browser detail parameters that are sent by the bootstrap
+ * javascript for two-request initialization.
+ *
+ * @param parentElementId
+ * @return
+ */
+ private static native String getNativeBrowserDetailsParameters(
+ String parentElementId)
+ /*-{
+ return $wnd.vaadin.getBrowserDetailsParameters(parentElementId);
+ }-*/;
+
protected void repaintAll() {
String repainAllParameters = getRepaintAllParameters();
makeUidlRequest("", repainAllParameters, false);
@@ -427,11 +453,11 @@ public class ApplicationConnection {
* Sends a request to the server to print details to console that will help
* developer to locate component in the source code.
*
- * @param paintable
+ * @param componentConnector
*/
- void highlightComponent(Paintable paintable) {
+ void highlightComponent(ComponentConnector componentConnector) {
String params = getRepaintAllParameters() + "&highlightComponent="
- + getPid(paintable);
+ + componentConnector.getConnectorId();
makeUidlRequest("", params, false);
}
@@ -465,9 +491,8 @@ public class ApplicationConnection {
if (extraParams != null && extraParams.length() > 0) {
uri = addGetParameters(uri, extraParams);
}
- if (windowName != null && windowName.length() > 0) {
- uri = addGetParameters(uri, "windowName=" + windowName);
- }
+ uri = addGetParameters(uri,
+ ROOT_ID_PARAMETER + "=" + configuration.getRootId());
doUidlRequest(uri, payload, forceSync);
@@ -491,10 +516,6 @@ public class ApplicationConnection {
public void onError(Request request, Throwable exception) {
showCommunicationError(exception.getMessage(), -1);
endRequest();
- if (!applicationRunning) {
- // start failed, let's try to start the next app
- ApplicationConfiguration.startNextApplication();
- }
}
public void onResponseReceived(Request request,
@@ -578,30 +599,10 @@ public class ApplicationConnection {
}
}
- final Date start = new Date();
// for(;;);[realjson]
final String jsonText = response.getText().substring(9,
response.getText().length() - 1);
- final ValueMap json;
- try {
- json = parseJSONResponse(jsonText);
- } catch (final Exception e) {
- endRequest();
- showCommunicationError(e.getMessage()
- + " - Original JSON-text:" + jsonText,
- statusCode);
- return;
- }
-
- VConsole.log("JSON parsing took "
- + (new Date().getTime() - start.getTime()) + "ms");
- if (applicationRunning) {
- handleReceivedJSONMessage(start, jsonText, json);
- } else {
- applicationRunning = true;
- handleWhenCSSLoaded(jsonText, json);
- ApplicationConfiguration.startNextApplication();
- }
+ handleJSONText(jsonText, statusCode);
}
};
@@ -626,6 +627,35 @@ public class ApplicationConnection {
}
/**
+ * Handles received UIDL JSON text, parsing it, and passing it on to the
+ * appropriate handlers, while logging timiing information.
+ *
+ * @param jsonText
+ * @param statusCode
+ */
+ private void handleJSONText(String jsonText, int statusCode) {
+ final Date start = new Date();
+ final ValueMap json;
+ try {
+ json = parseJSONResponse(jsonText);
+ } catch (final Exception e) {
+ endRequest();
+ showCommunicationError(e.getMessage() + " - Original JSON-text:"
+ + jsonText, statusCode);
+ return;
+ }
+
+ VConsole.log("JSON parsing took "
+ + (new Date().getTime() - start.getTime()) + "ms");
+ if (applicationRunning) {
+ handleReceivedJSONMessage(start, jsonText, json);
+ } else {
+ applicationRunning = true;
+ handleWhenCSSLoaded(jsonText, json);
+ }
+ }
+
+ /**
* Sends an asynchronous UIDL request to the server using the given URI.
*
* @param uri
@@ -674,9 +704,7 @@ public class ApplicationConnection {
protected void handleWhenCSSLoaded(final String jsonText,
final ValueMap json) {
- int heightOfLoadElement = DOM.getElementPropertyInt(loadElement,
- "offsetHeight");
- if (heightOfLoadElement == 0 && cssWaits < MAX_CSS_WAITS) {
+ if (!isCSSLoaded() && cssWaits < MAX_CSS_WAITS) {
(new Timer() {
@Override
public void run() {
@@ -688,6 +716,7 @@ public class ApplicationConnection {
+ "(.v-loading-indicator height == 0)");
cssWaits++;
} else {
+ cssLoaded = true;
handleReceivedJSONMessage(new Date(), jsonText, json);
if (cssWaits >= MAX_CSS_WAITS) {
VConsole.error("CSS files may have not loaded properly.");
@@ -696,19 +725,14 @@ public class ApplicationConnection {
}
/**
- * Shows the communication error notification.
+ * Checks whether or not the CSS is loaded. By default checks the size of
+ * the loading indicator element.
*
- * @param details
- * Optional details for debugging.
- *
- * @deprecated Use the method with errorCode instead
+ * @return
*/
- @Deprecated
- protected void showCommunicationError(String details) {
- VConsole.error("Communication error: " + details);
- showError(details, configuration.getCommunicationErrorCaption(),
- configuration.getCommunicationErrorMessage(),
- configuration.getCommunicationErrorUrl());
+ protected boolean isCSSLoaded() {
+ return cssLoaded
+ || DOM.getElementPropertyInt(loadElement, "offsetHeight") != 0;
}
/**
@@ -717,11 +741,14 @@ public class ApplicationConnection {
* @param details
* Optional details for debugging.
* @param statusCode
- * The http error code during the problematic request or -1 if
- * error happened before response was received.
+ * The status code returned for the request
+ *
*/
protected void showCommunicationError(String details, int statusCode) {
- showCommunicationError(details);
+ VConsole.error("Communication error: " + details);
+ ErrorMessage communicationError = configuration.getCommunicationError();
+ showError(details, communicationError.getCaption(),
+ communicationError.getMessage(), communicationError.getUrl());
}
/**
@@ -732,9 +759,9 @@ public class ApplicationConnection {
*/
protected void showAuthenticationError(String details) {
VConsole.error("Authentication error: " + details);
- showError(details, configuration.getAuthorizationErrorCaption(),
- configuration.getAuthorizationErrorMessage(),
- configuration.getAuthorizationErrorUrl());
+ ErrorMessage authorizationError = configuration.getAuthorizationError();
+ showError(details, authorizationError.getCaption(),
+ authorizationError.getMessage(), authorizationError.getUrl());
}
/**
@@ -831,14 +858,14 @@ public class ApplicationConnection {
* change set if it exists.
*/
private void checkForPendingVariableBursts() {
- cleanVariableBurst(pendingVariables);
- if (pendingVariableBursts.size() > 0) {
- for (Iterator<ArrayList<String>> iterator = pendingVariableBursts
+ cleanVariableBurst(pendingInvocations);
+ if (pendingBursts.size() > 0) {
+ for (Iterator<ArrayList<MethodInvocation>> iterator = pendingBursts
.iterator(); iterator.hasNext();) {
cleanVariableBurst(iterator.next());
}
- ArrayList<String> nextBurst = pendingVariableBursts.get(0);
- pendingVariableBursts.remove(0);
+ ArrayList<MethodInvocation> nextBurst = pendingBursts.get(0);
+ pendingBursts.remove(0);
buildAndSendVariableBurst(nextBurst, false);
}
}
@@ -849,15 +876,13 @@ public class ApplicationConnection {
*
* @param variableBurst
*/
- private void cleanVariableBurst(ArrayList<String> variableBurst) {
- for (int i = 1; i < variableBurst.size(); i += 2) {
- String id = variableBurst.get(i);
- id = id.substring(0, id.indexOf(VAR_FIELD_SEPARATOR));
- if (!idToPaintableDetail.containsKey(id) && !id.startsWith("DD")) {
+ private void cleanVariableBurst(ArrayList<MethodInvocation> variableBurst) {
+ for (int i = 1; i < variableBurst.size(); i++) {
+ String id = variableBurst.get(i).getConnectorId();
+ if (!getConnectorMap().hasConnector(id)
+ && !getConnectorMap().isDragAndDropPaintable(id)) {
// variable owner does not exist anymore
- variableBurst.remove(i - 1);
- variableBurst.remove(i - 1);
- i -= 2;
+ variableBurst.remove(i);
VConsole.log("Removed variable from removed component: " + id);
}
}
@@ -868,7 +893,7 @@ public class ApplicationConnection {
if (loadElement == null) {
loadElement = DOM.createDiv();
DOM.setStyleAttribute(loadElement, "position", "absolute");
- DOM.appendChild(view.getElement(), loadElement);
+ DOM.appendChild(rootConnector.getWidget().getElement(), loadElement);
VConsole.log("inserting load indicator");
}
DOM.setElementProperty(loadElement, "className", "v-loading-indicator");
@@ -898,12 +923,14 @@ public class ApplicationConnection {
private void hideLoadingIndicator() {
if (loadTimer != null) {
loadTimer.cancel();
- if (loadTimer2 != null) {
- loadTimer2.cancel();
- loadTimer3.cancel();
- }
loadTimer = null;
}
+ if (loadTimer2 != null) {
+ loadTimer2.cancel();
+ loadTimer3.cancel();
+ loadTimer2 = null;
+ loadTimer3 = null;
+ }
if (loadElement != null) {
DOM.setStyleAttribute(loadElement, "display", "none");
}
@@ -962,6 +989,7 @@ public class ApplicationConnection {
protected void handleUIDLMessage(final Date start, final String jsonText,
final ValueMap json) {
+ VConsole.log("Handling message from server");
// Handle redirect
if (json.containsKey("redirect")) {
String url = json.getValueMap("redirect").getString("url");
@@ -970,10 +998,13 @@ public class ApplicationConnection {
return;
}
+ final MultiStepDuration handleUIDLDuration = new MultiStepDuration();
+
// Get security key
if (json.containsKey(UIDL_SECURITY_TOKEN_ID)) {
uidlSecurityKey = json.getString(UIDL_SECURITY_TOKEN_ID);
}
+ VConsole.log(" * Handling resources from server");
if (json.containsKey("resources")) {
ValueMap resources = json.getValueMap("resources");
@@ -984,12 +1015,27 @@ public class ApplicationConnection {
resourcesMap.put(key, resources.getAsString(key));
}
}
+ handleUIDLDuration.logDuration(
+ " * Handling resources from server completed", 10);
+
+ VConsole.log(" * Handling type inheritance map from server");
+
+ if (json.containsKey("typeInheritanceMap")) {
+ configuration.addComponentInheritanceInfo(json
+ .getValueMap("typeInheritanceMap"));
+ }
+ handleUIDLDuration.logDuration(
+ " * Handling type inheritance map from server completed", 10);
+
+ VConsole.log("Handling type mappings from server");
if (json.containsKey("typeMappings")) {
configuration.addComponentMappings(
json.getValueMap("typeMappings"), widgetSet);
}
+ handleUIDLDuration.logDuration(
+ " * Handling type mappings from server completed", 10);
/*
* Hook for TestBench to get details about server status
*/
@@ -999,27 +1045,42 @@ public class ApplicationConnection {
Command c = new Command() {
public void execute() {
- VConsole.dirUIDL(json, configuration);
+ handleUIDLDuration.logDuration(" * Loading widgets completed",
+ 10);
+
+ MultiStepDuration updateDuration = new MultiStepDuration();
+
+ if (debugLogging) {
+ VConsole.log(" * Dumping UIDL to the console");
+ VConsole.dirUIDL(json, configuration);
+
+ updateDuration.logDuration(
+ " * Dumping UIDL to the console completed", 10);
+ }
if (json.containsKey("locales")) {
+ VConsole.log(" * Handling locales");
// Store locale data
JsArray<ValueMap> valueMapArray = json
.getJSValueMapArray("locales");
LocaleService.addLocales(valueMapArray);
}
+ updateDuration.logDuration(" * Handling locales completed", 10);
+
boolean repaintAll = false;
ValueMap meta = null;
if (json.containsKey("meta")) {
+ VConsole.log(" * Handling meta information");
meta = json.getValueMap("meta");
if (meta.containsKey("repaintAll")) {
repaintAll = true;
- view.clear();
- idToPaintableDetail.clear();
+ rootConnector.getWidget().clear();
+ getConnectorMap().clear();
if (meta.containsKey("invalidLayouts")) {
validatingLayouts = true;
- zeroWidthComponents = new HashSet<Paintable>();
- zeroHeightComponents = new HashSet<Paintable>();
+ zeroWidthComponents = new HashSet<ComponentConnector>();
+ zeroHeightComponents = new HashSet<ComponentConnector>();
}
}
if (meta.containsKey("timedRedirect")) {
@@ -1036,60 +1097,61 @@ public class ApplicationConnection {
}
}
+ updateDuration.logDuration(
+ " * Handling meta information completed", 10);
+
if (redirectTimer != null) {
redirectTimer.schedule(1000 * sessionExpirationInterval);
}
- // Process changes
- JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
-
- ArrayList<Paintable> updatedWidgets = new ArrayList<Paintable>();
- relativeSizeChanges.clear();
componentCaptionSizeChanges.clear();
- int length = changes.length();
- for (int i = 0; i < length; i++) {
- try {
- final UIDL change = changes.get(i).cast();
- final UIDL uidl = change.getChildUIDL(0);
- // TODO optimize
- final Paintable paintable = getPaintable(uidl.getId());
- if (paintable != null) {
- paintable.updateFromUIDL(uidl,
- ApplicationConnection.this);
- // paintable may have changed during render to
- // another
- // implementation, use the new one for updated
- // widgets map
- updatedWidgets.add(idToPaintableDetail.get(
- uidl.getId()).getComponent());
- } else {
- if (!uidl.getTag().equals(
- configuration.getEncodedWindowTag())) {
- VConsole.error("Received update for "
- + uidl.getTag()
- + ", but there is no such paintable ("
- + uidl.getId() + ") rendered.");
- } else {
- String pid = uidl.getId();
- if (!idToPaintableDetail.containsKey(pid)) {
- registerPaintable(pid, view);
- }
- // VView does not call updateComponent so we
- // register any event listeners here
- ComponentDetail cd = idToPaintableDetail
- .get(pid);
- cd.registerEventListenersFromUIDL(uidl);
-
- // Finally allow VView to update itself
- view.updateFromUIDL(uidl,
- ApplicationConnection.this);
- }
- }
- } catch (final Throwable e) {
- VConsole.error(e);
- }
- }
+ int startProcessing = updateDuration.elapsedMillis();
+
+ // Ensure that all connectors that we are about to update exist
+ createConnectorsIfNeeded(json);
+
+ updateDuration.logDuration(" * Creating connectors completed",
+ 10);
+
+ // Update states, do not fire events
+ Collection<StateChangeEvent> pendingStateChangeEvents = updateConnectorState(json);
+
+ updateDuration.logDuration(
+ " * Update of connector states completed", 10);
+
+ // Update hierarchy, do not fire events
+ Collection<ConnectorHierarchyChangeEvent> pendingHierarchyChangeEvents = updateConnectorHierarchy(json);
+
+ updateDuration.logDuration(
+ " * Update of connector hierarchy completed", 10);
+
+ // Fire hierarchy change events
+ sendHierarchyChangeEvents(pendingHierarchyChangeEvents);
+
+ updateDuration.logDuration(
+ " * Hierarchy state change event processing completed",
+ 10);
+
+ // Fire state change events.
+ sendStateChangeEvents(pendingStateChangeEvents);
+
+ updateDuration.logDuration(
+ " * State change event processing completed", 10);
+
+ // Update of legacy (UIDL) style connectors
+ updateVaadin6StyleConnectors(json);
+
+ updateDuration
+ .logDuration(
+ " * Vaadin 6 style connector updates (updateFromUidl) completed",
+ 10);
+
+ // Handle any RPC invocations done on the server side
+ handleRpcInvocations(json);
+
+ updateDuration.logDuration(
+ " * Processing of RPC invocations completed", 10);
if (json.containsKey("dd")) {
// response contains data for drag and drop service
@@ -1097,28 +1159,26 @@ public class ApplicationConnection {
json.getValueMap("dd"));
}
- // Check which widgets' size has been updated
- Set<Paintable> sizeUpdatedWidgets = new HashSet<Paintable>();
+ updateDuration
+ .logDuration(
+ " * Processing of drag and drop server response completed",
+ 10);
- updatedWidgets.addAll(relativeSizeChanges);
- sizeUpdatedWidgets.addAll(componentCaptionSizeChanges);
+ unregisterRemovedConnectors();
- for (Paintable paintable : updatedWidgets) {
- ComponentDetail detail = idToPaintableDetail
- .get(getPid(paintable));
- Widget widget = (Widget) paintable;
- Size oldSize = detail.getOffsetSize();
- Size newSize = new Size(widget.getOffsetWidth(),
- widget.getOffsetHeight());
+ updateDuration.logDuration(
+ " * Unregistering of removed components completed", 10);
- if (oldSize == null || !oldSize.equals(newSize)) {
- sizeUpdatedWidgets.add(paintable);
- detail.setOffsetSize(newSize);
- }
+ VConsole.log("handleUIDLMessage: "
+ + (updateDuration.elapsedMillis() - startProcessing)
+ + " ms");
- }
+ LayoutManager layoutManager = getLayoutManager();
+ layoutManager.setEverythingNeedsMeasure();
+ layoutManager.layoutNow();
- Util.componentSizeUpdated(sizeUpdatedWidgets);
+ updateDuration
+ .logDuration(" * Layout processing completed", 10);
if (meta != null) {
if (meta.containsKey("appError")) {
@@ -1162,15 +1222,7 @@ public class ApplicationConnection {
}
}
- if (repaintAll) {
- /*
- * idToPaintableDetail is already cleanded at the start of
- * the changeset handling, bypass cleanup.
- */
- unregistryBag.clear();
- } else {
- purgeUnregistryBag();
- }
+ updateDuration.logDuration(" * Error handling completed", 10);
// TODO build profiling for widget impl loading time
@@ -1181,213 +1233,383 @@ public class ApplicationConnection {
VConsole.log(" Processing time was "
+ String.valueOf(lastProcessingTime) + "ms for "
+ jsonText.length() + " characters of JSON");
- VConsole.log("Referenced paintables: "
- + idToPaintableDetail.size());
+ VConsole.log("Referenced paintables: " + connectorMap.size());
endRequest();
}
- };
- ApplicationConfiguration.runWhenWidgetsLoaded(c);
- }
- /**
- * This method assures that all pending variable changes are sent to server.
- * Method uses synchronized xmlhttprequest and does not return before the
- * changes are sent. No UIDL updates are processed and thus UI is left in
- * inconsistent state. This method should be called only when closing
- * windows - normally sendPendingVariableChanges() should be used.
- */
- public void sendPendingVariableChangesSync() {
- if (applicationRunning) {
- pendingVariableBursts.add(pendingVariables);
- ArrayList<String> nextBurst = pendingVariableBursts.get(0);
- pendingVariableBursts.remove(0);
- buildAndSendVariableBurst(nextBurst, true);
- }
- }
+ /**
+ * Sends the state change events created while updating the state
+ * information.
+ *
+ * This must be called after hierarchy change listeners have been
+ * called. At least caption updates for the parent are strange if
+ * fired from state change listeners and thus calls the parent
+ * BEFORE the parent is aware of the child (through a
+ * ConnectorHierarchyChangedEvent)
+ *
+ * @param pendingStateChangeEvents
+ * The events to send
+ */
+ private void sendStateChangeEvents(
+ Collection<StateChangeEvent> pendingStateChangeEvents) {
+ VConsole.log(" * Sending state change events");
- // Redirect browser, null reloads current page
- private static native void redirect(String url)
- /*-{
- if (url) {
- $wnd.location = url;
- } else {
- $wnd.location.reload(false);
- }
- }-*/;
+ for (StateChangeEvent sce : pendingStateChangeEvents) {
+ try {
+ sce.getConnector().fireEvent(sce);
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
- public void registerPaintable(String pid, Paintable paintable) {
- ComponentDetail componentDetail = new ComponentDetail(this, pid,
- paintable);
- idToPaintableDetail.put(pid, componentDetail);
- setPid(((Widget) paintable).getElement(), pid);
- }
+ }
- private native void setPid(Element el, String pid)
- /*-{
- el.tkPid = pid;
- }-*/;
+ private void unregisterRemovedConnectors() {
+ int unregistered = 0;
+ List<ServerConnector> currentConnectors = new ArrayList<ServerConnector>(
+ connectorMap.getConnectors());
+ for (ServerConnector c : currentConnectors) {
+ if (c instanceof ComponentConnector) {
+ ComponentConnector cc = (ComponentConnector) c;
+ if (cc.getParent() != null) {
+ if (!cc.getParent().getChildren().contains(cc)) {
+ VConsole.error("ERROR: Connector is connected to a parent but the parent does not contain the connector");
+ }
+ } else if ((cc instanceof RootConnector && cc == getRootConnector())) {
+ // RootConnector for this connection, leave as-is
+ } else if (cc instanceof WindowConnector
+ && getRootConnector().hasSubWindow(
+ (WindowConnector) cc)) {
+ // Sub window attached to this RootConnector, leave
+ // as-is
+ } else {
+ // The connector has been detached from the
+ // hierarchy, unregister it and any possible
+ // children. The RootConnector should never be
+ // unregistered even though it has no parent.
+ connectorMap.unregisterConnector(cc);
+ unregistered++;
+ }
+ }
- /**
- * Gets the paintableId for a specific paintable (a.k.a Vaadin Widget).
- * <p>
- * The paintableId is used in the UIDL to identify a specific widget
- * instance, effectively linking the widget with it's server side Component.
- * </p>
- *
- * @param paintable
- * the paintable who's id is needed
- * @return the id for the given paintable
- */
- public String getPid(Paintable paintable) {
- return getPid(((Widget) paintable).getElement());
- }
+ }
- /**
- * Gets the paintableId using a DOM element - the element should be the main
- * element for a paintable otherwise no id will be found. Use
- * {@link #getPid(Paintable)} instead whenever possible.
- *
- * @see #getPid(Paintable)
- * @param el
- * element of the paintable whose pid is desired
- * @return the pid of the element's paintable, if it's a paintable
- */
- public native String getPid(Element el)
- /*-{
- return el.tkPid;
- }-*/;
+ VConsole.log("* Unregistered " + unregistered + " connectors");
+ }
- /**
- * Gets the main element for the paintable with the given id. The revers of
- * {@link #getPid(Element)}.
- *
- * @param pid
- * the pid of the widget whose element is desired
- * @return the element for the paintable corresponding to the pid
- */
- public Element getElementByPid(String pid) {
- return ((Widget) getPaintable(pid)).getElement();
- }
+ private void createConnectorsIfNeeded(ValueMap json) {
+ VConsole.log(" * Creating connectors (if needed)");
- /**
- * Unregisters the given paintable; always use after removing a paintable.
- * This method does not remove the paintable from the DOM, but marks the
- * paintable so that ApplicationConnection may clean up its references to
- * it. Removing the widget from DOM is component containers responsibility.
- *
- * @param p
- * the paintable to remove
- */
- public void unregisterPaintable(Paintable p) {
+ if (!json.containsKey("types")) {
+ return;
+ }
- // add to unregistry que
+ ValueMap types = json.getValueMap("types");
+ JsArrayString keyArray = types.getKeyArray();
+ for (int i = 0; i < keyArray.length(); i++) {
+ try {
+ String connectorId = keyArray.get(i);
+ int connectorType = Integer.parseInt(types
+ .getString((connectorId)));
+ ServerConnector connector = connectorMap
+ .getConnector(connectorId);
+ if (connector != null) {
+ continue;
+ }
- if (p == null) {
- VConsole.error("WARN: Trying to unregister null paintable");
- return;
- }
- String id = getPid(p);
- if (id == null) {
- /*
- * Uncomment the following to debug unregistring components. No
- * paintables with null id should end here. At least one exception
- * is our VScrollTableRow, that is hacked to fake it self as a
- * Paintable to build support for sizing easier.
- */
- // if (!(p instanceof VScrollTableRow)) {
- // VConsole.log("Trying to unregister Paintable not created by Application Connection.");
- // }
- if (p instanceof HasWidgets) {
- unregisterChildPaintables((HasWidgets) p);
+ Class<? extends ComponentConnector> connectorClass = configuration
+ .getWidgetClassByEncodedTag(connectorType);
+
+ // Connector does not exist so we must create it
+ if (connectorClass != RootConnector.class) {
+ // create, initialize and register the paintable
+ getConnector(connectorId, connectorType);
+ } else {
+ // First RootConnector update. Before this the
+ // RootConnector has been created but not
+ // initialized as the connector id has not been
+ // known
+ connectorMap.registerConnector(connectorId,
+ rootConnector);
+ rootConnector.doInit(connectorId,
+ ApplicationConnection.this);
+ }
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
}
- } else {
- unregistryBag.add(id);
- if (p instanceof HasWidgets) {
- unregisterChildPaintables((HasWidgets) p);
+
+ private void updateVaadin6StyleConnectors(ValueMap json) {
+ JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
+ int length = changes.length();
+
+ VConsole.log(" * Passing UIDL to Vaadin 6 style connectors");
+ // update paintables
+ for (int i = 0; i < length; i++) {
+ try {
+ final UIDL change = changes.get(i).cast();
+ final UIDL uidl = change.getChildUIDL(0);
+ String connectorId = uidl.getId();
+
+ final ComponentConnector legacyConnector = (ComponentConnector) connectorMap
+ .getConnector(connectorId);
+ if (legacyConnector instanceof Paintable) {
+ ((Paintable) legacyConnector).updateFromUIDL(uidl,
+ ApplicationConnection.this);
+ } else if (legacyConnector == null) {
+ VConsole.error("Received update for "
+ + uidl.getTag()
+ + ", but there is no such paintable ("
+ + connectorId + ") rendered.");
+ } else {
+ VConsole.error("Server sent Vaadin 6 style updates for "
+ + Util.getConnectorString(legacyConnector)
+ + " but this is not a Vaadin 6 Paintable");
+ }
+
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
}
- }
- }
- private void purgeUnregistryBag() {
- for (String id : unregistryBag) {
- ComponentDetail componentDetail = idToPaintableDetail.get(id);
- if (componentDetail == null) {
- /*
- * this should never happen, but it does :-( See e.g.
- * com.vaadin.tests.components.accordion.RemoveTabs (with test
- * script)
- */
- VConsole.error("ApplicationConnetion tried to unregister component (id="
- + id
- + ") that is never registered (or already unregistered)");
- continue;
+ private void sendHierarchyChangeEvents(
+ Collection<ConnectorHierarchyChangeEvent> pendingHierarchyChangeEvents) {
+ if (pendingHierarchyChangeEvents.isEmpty()) {
+ return;
+ }
+
+ VConsole.log(" * Sending hierarchy change events");
+ for (ConnectorHierarchyChangeEvent event : pendingHierarchyChangeEvents) {
+ try {
+ event.getConnector().fireEvent(event);
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
+
}
- // check if can be cleaned
- Widget component = (Widget) componentDetail.getComponent();
- if (!component.isAttached()) {
- // clean reference from ac to paintable
- idToPaintableDetail.remove(id);
+
+ private Collection<StateChangeEvent> updateConnectorState(
+ ValueMap json) {
+ ArrayList<StateChangeEvent> events = new ArrayList<StateChangeEvent>();
+ VConsole.log(" * Updating connector states");
+ if (!json.containsKey("state")) {
+ return events;
+ }
+ // set states for all paintables mentioned in "state"
+ ValueMap states = json.getValueMap("state");
+ JsArrayString keyArray = states.getKeyArray();
+ for (int i = 0; i < keyArray.length(); i++) {
+ try {
+ String connectorId = keyArray.get(i);
+ ServerConnector connector = connectorMap
+ .getConnector(connectorId);
+ if (null != connector) {
+
+ JSONArray stateDataAndType = new JSONArray(
+ states.getJavaScriptObject(connectorId));
+
+ Object state = JsonDecoder.decodeValue(
+ stateDataAndType, connectorMap,
+ ApplicationConnection.this);
+
+ connector.setState((SharedState) state);
+ StateChangeEvent event = GWT
+ .create(StateChangeEvent.class);
+ event.setConnector(connector);
+ events.add(event);
+ }
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
+
+ return events;
}
- /*
- * else NOP : same component has been reattached to another parent
- * or replaced by another component implementation.
+
+ /**
+ * Updates the connector hierarchy and returns a list of events that
+ * should be fired after update of the hierarchy and the state is
+ * done.
+ *
+ * @param json
+ * The JSON containing the hierarchy information
+ * @return A collection of events that should be fired when update
+ * of hierarchy and state is complete
*/
- }
+ private Collection<ConnectorHierarchyChangeEvent> updateConnectorHierarchy(
+ ValueMap json) {
+ List<ConnectorHierarchyChangeEvent> events = new LinkedList<ConnectorHierarchyChangeEvent>();
- unregistryBag.clear();
- }
+ VConsole.log(" * Updating connector hierarchy");
+ if (!json.containsKey("hierarchy")) {
+ return events;
+ }
+
+ ValueMap hierarchies = json.getValueMap("hierarchy");
+ JsArrayString hierarchyKeys = hierarchies.getKeyArray();
+ for (int i = 0; i < hierarchyKeys.length(); i++) {
+ try {
+ String connectorId = hierarchyKeys.get(i);
+ ServerConnector connector = connectorMap
+ .getConnector(connectorId);
+ if (!(connector instanceof ComponentContainerConnector)) {
+ VConsole.error("Retrieved a hierarchy update for a connector ("
+ + connectorId
+ + ") that is not a ComponentContainerConnector");
+ continue;
+ }
+ ComponentContainerConnector ccc = (ComponentContainerConnector) connector;
+
+ JsArrayString childConnectorIds = hierarchies
+ .getJSStringArray(connectorId);
+ int childConnectorSize = childConnectorIds.length();
+
+ List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
+ for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
+ String childConnectorId = childConnectorIds
+ .get(connectorIndex);
+ ComponentConnector childConnector = (ComponentConnector) connectorMap
+ .getConnector(childConnectorId);
+ if (childConnector == null) {
+ VConsole.error("Hierarchy claims that "
+ + childConnectorId + " is a child for "
+ + connectorId + " ("
+ + connector.getClass().getName()
+ + ") but no connector with id "
+ + childConnectorId
+ + " has been registered");
+ continue;
+ }
+ newChildren.add(childConnector);
+ if (childConnector.getParent() != ccc) {
+ // Avoid extra calls to setParent
+ childConnector.setParent(ccc);
+ }
+ }
+
+ // TODO This check should be done on the server side in
+ // the future so the hierarchy update is only sent when
+ // something actually has changed
+ List<ComponentConnector> oldChildren = ccc
+ .getChildren();
+ boolean actuallyChanged = !Util.collectionsEquals(
+ oldChildren, newChildren);
+
+ if (!actuallyChanged) {
+ continue;
+ }
+
+ // Fire change event if the hierarchy has changed
+ ConnectorHierarchyChangeEvent event = GWT
+ .create(ConnectorHierarchyChangeEvent.class);
+ event.setOldChildren(oldChildren);
+ event.setConnector(ccc);
+ ccc.setChildren((List) newChildren);
+ events.add(event);
+
+ // Remove parent for children that are no longer
+ // attached to this (avoid updating children if they
+ // have already been assigned to a new parent)
+ for (ComponentConnector oldChild : oldChildren) {
+ if (oldChild.getParent() != ccc) {
+ continue;
+ }
+
+ // TODO This could probably be optimized
+ if (!newChildren.contains(oldChild)) {
+ oldChild.setParent(null);
+ }
+ }
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
+ return events;
- /**
- * Unregisters a paintable and all it's child paintables recursively. Use
- * when after removing a paintable that contains other paintables. Does not
- * unregister the given container itself. Does not actually remove the
- * paintable from the DOM.
- *
- * @see #unregisterPaintable(Paintable)
- * @param container
- */
- public void unregisterChildPaintables(HasWidgets container) {
- final Iterator<Widget> it = container.iterator();
- while (it.hasNext()) {
- final Widget w = it.next();
- if (w instanceof Paintable) {
- unregisterPaintable((Paintable) w);
- } else if (w instanceof HasWidgets) {
- unregisterChildPaintables((HasWidgets) w);
}
- }
+
+ private void handleRpcInvocations(ValueMap json) {
+ if (json.containsKey("rpc")) {
+ VConsole.log(" * Performing server to client RPC calls");
+
+ JSONArray rpcCalls = new JSONArray(
+ json.getJavaScriptObject("rpc"));
+
+ int rpcLength = rpcCalls.size();
+ for (int i = 0; i < rpcLength; i++) {
+ try {
+ JSONArray rpcCall = (JSONArray) rpcCalls.get(i);
+ MethodInvocation invocation = parseMethodInvocation(rpcCall);
+ VConsole.log("Server to client RPC call: "
+ + invocation);
+ rpcManager.applyInvocation(invocation,
+ getConnectorMap());
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
+ }
+
+ }
+
+ };
+ ApplicationConfiguration.runWhenWidgetsLoaded(c);
}
- /**
- * Returns Paintable element by its id
- *
- * @param id
- * Paintable ID
- */
- public Paintable getPaintable(String id) {
- ComponentDetail componentDetail = idToPaintableDetail.get(id);
- if (componentDetail == null) {
- return null;
- } else {
- return componentDetail.getComponent();
+ private MethodInvocation parseMethodInvocation(JSONArray rpcCall) {
+ String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
+ String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
+ String methodName = ((JSONString) rpcCall.get(2)).stringValue();
+ JSONArray parametersJson = (JSONArray) rpcCall.get(3);
+ Object[] parameters = new Object[parametersJson.size()];
+ for (int j = 0; j < parametersJson.size(); ++j) {
+ parameters[j] = JsonDecoder.decodeValue(
+ (JSONArray) parametersJson.get(j), getConnectorMap(), this);
}
+ return new MethodInvocation(connectorId, interfaceName, methodName,
+ parameters);
}
- private void addVariableToQueue(String paintableId, String variableName,
- String encodedValue, boolean immediate, char type) {
- final String id = paintableId + VAR_FIELD_SEPARATOR + variableName
- + VAR_FIELD_SEPARATOR + type;
- for (int i = 1; i < pendingVariables.size(); i += 2) {
- if ((pendingVariables.get(i)).equals(id)) {
- pendingVariables.remove(i - 1);
- pendingVariables.remove(i - 1);
- break;
- }
- }
- pendingVariables.add(encodedValue);
- pendingVariables.add(id);
+ // Redirect browser, null reloads current page
+ private static native void redirect(String url)
+ /*-{
+ if (url) {
+ $wnd.location = url;
+ } else {
+ $wnd.location.reload(false);
+ }
+ }-*/;
+
+ private void addVariableToQueue(String connectorId, String variableName,
+ Object value, boolean immediate) {
+ // note that type is now deduced from value
+ // TODO could eliminate invocations of same shared variable setter
+ addMethodInvocationToQueue(new MethodInvocation(connectorId,
+ UPDATE_VARIABLE_INTERFACE, UPDATE_VARIABLE_METHOD,
+ new Object[] { variableName, value }), immediate);
+ }
+
+ /**
+ * Adds an explicit RPC method invocation to the send queue.
+ *
+ * @since 7.0
+ *
+ * @param invocation
+ * RPC method invocation
+ * @param immediate
+ * true to trigger sending within a short time window (possibly
+ * combining subsequent calls to a single request), false to let
+ * the framework delay sending of RPC calls and variable changes
+ * until the next immediate change
+ */
+ public void addMethodInvocationToQueue(MethodInvocation invocation,
+ boolean immediate) {
+ pendingInvocations.add(invocation);
if (immediate) {
sendPendingVariableChanges();
}
@@ -1403,20 +1625,32 @@ public class ApplicationConnection {
* "burst" to queue that will be purged after current request is handled.
*
*/
- @SuppressWarnings("unchecked")
public void sendPendingVariableChanges() {
+ if (!deferedSendPending) {
+ deferedSendPending = true;
+ Scheduler.get().scheduleDeferred(sendPendingCommand);
+ }
+ }
+
+ private final ScheduledCommand sendPendingCommand = new ScheduledCommand() {
+ public void execute() {
+ deferedSendPending = false;
+ doSendPendingVariableChanges();
+ }
+ };
+ private boolean deferedSendPending = false;
+
+ @SuppressWarnings("unchecked")
+ private void doSendPendingVariableChanges() {
if (applicationRunning) {
if (hasActiveRequest()) {
// skip empty queues if there are pending bursts to be sent
- if (pendingVariables.size() > 0
- || pendingVariableBursts.size() == 0) {
- ArrayList<String> burst = (ArrayList<String>) pendingVariables
- .clone();
- pendingVariableBursts.add(burst);
- pendingVariables.clear();
+ if (pendingInvocations.size() > 0 || pendingBursts.size() == 0) {
+ pendingBursts.add(pendingInvocations);
+ pendingInvocations = new ArrayList<MethodInvocation>();
}
} else {
- buildAndSendVariableBurst(pendingVariables, false);
+ buildAndSendVariableBurst(pendingInvocations, false);
}
}
}
@@ -1428,39 +1662,64 @@ public class ApplicationConnection {
* at the same time. This is ok as we can assume that DOM will never be
* updated after this.
*
- * @param pendingVariables
- * Vector of variable changes to send
+ * @param pendingInvocations
+ * List of RPC method invocations to send
* @param forceSync
* Should we use synchronous request?
*/
- private void buildAndSendVariableBurst(ArrayList<String> pendingVariables,
- boolean forceSync) {
+ private void buildAndSendVariableBurst(
+ ArrayList<MethodInvocation> pendingInvocations, boolean forceSync) {
final StringBuffer req = new StringBuffer();
- while (!pendingVariables.isEmpty()) {
+ while (!pendingInvocations.isEmpty()) {
if (ApplicationConfiguration.isDebugMode()) {
- Util.logVariableBurst(this, pendingVariables);
+ Util.logVariableBurst(this, pendingInvocations);
}
- for (int i = 0; i < pendingVariables.size(); i++) {
- if (i > 0) {
- if (i % 2 == 0) {
- req.append(VAR_RECORD_SEPARATOR);
- } else {
- req.append(VAR_FIELD_SEPARATOR);
- }
+
+ JSONArray reqJson = new JSONArray();
+
+ for (MethodInvocation invocation : pendingInvocations) {
+ JSONArray invocationJson = new JSONArray();
+ invocationJson.set(0,
+ new JSONString(invocation.getConnectorId()));
+ invocationJson.set(1,
+ new JSONString(invocation.getInterfaceName()));
+ invocationJson.set(2,
+ new JSONString(invocation.getMethodName()));
+ JSONArray paramJson = new JSONArray();
+ for (int i = 0; i < invocation.getParameters().length; ++i) {
+ // TODO non-static encoder? type registration?
+ paramJson.set(i, JsonEncoder.encode(
+ invocation.getParameters()[i], getConnectorMap(),
+ this));
}
- req.append(pendingVariables.get(i));
+ invocationJson.set(3, paramJson);
+ reqJson.set(reqJson.size(), invocationJson);
}
- pendingVariables.clear();
- // Append all the busts to this synchronous request
- if (forceSync && !pendingVariableBursts.isEmpty()) {
- pendingVariables = pendingVariableBursts.get(0);
- pendingVariableBursts.remove(0);
+ // escape burst separators (if any)
+ req.append(escapeBurstContents(reqJson.toString()));
+
+ pendingInvocations.clear();
+ // Append all the bursts to this synchronous request
+ if (forceSync && !pendingBursts.isEmpty()) {
+ pendingInvocations = pendingBursts.get(0);
+ pendingBursts.remove(0);
req.append(VAR_BURST_SEPARATOR);
}
}
- makeUidlRequest(req.toString(), "", forceSync);
+
+ // Include the browser detail parameters if they aren't already sent
+ String extraParams;
+ if (!getConfiguration().isBrowserDetailsSent()) {
+ extraParams = getNativeBrowserDetailsParameters(getConfiguration()
+ .getRootPanelId());
+ getConfiguration().setBrowserDetailsSent();
+ } else {
+ extraParams = "";
+ }
+
+ makeUidlRequest(req.toString(), extraParams, forceSync);
}
/**
@@ -1481,9 +1740,8 @@ public class ApplicationConnection {
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
- Paintable newValue, boolean immediate) {
- String pid = (newValue != null) ? getPid(newValue) : null;
- addVariableToQueue(paintableId, variableName, pid, immediate, 'p');
+ ServerConnector newValue, boolean immediate) {
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1506,8 +1764,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
String newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName,
- escapeVariableValue(newValue), immediate, 's');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1530,8 +1787,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
int newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
- 'i');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1554,8 +1810,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
long newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
- 'l');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1578,8 +1833,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
float newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
- 'f');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1602,8 +1856,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
double newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
- 'd');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1626,8 +1879,7 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
boolean newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, newValue ? "true"
- : "false", immediate, 'b');
+ addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
@@ -1642,56 +1894,14 @@ public class ApplicationConnection {
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
- * @param newValue
- * the new value to be sent
+ * @param map
+ * the new values to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
Map<String, Object> map, boolean immediate) {
- final StringBuffer buf = new StringBuffer();
- Iterator<String> iterator = map.keySet().iterator();
- while (iterator.hasNext()) {
- String key = iterator.next();
- Object value = map.get(key);
- char transportType = getTransportType(value);
- buf.append(transportType);
- buf.append(escapeVariableValue(key));
- buf.append(VAR_ARRAYITEM_SEPARATOR);
- if (transportType == 'p') {
- buf.append(getPid((Paintable) value));
- } else {
- buf.append(escapeVariableValue(String.valueOf(value)));
- }
-
- if (iterator.hasNext()) {
- buf.append(VAR_ARRAYITEM_SEPARATOR);
- }
- }
-
- addVariableToQueue(paintableId, variableName, buf.toString(),
- immediate, 'm');
- }
-
- private char getTransportType(Object value) {
- if (value instanceof String) {
- return 's';
- } else if (value instanceof Paintable) {
- return 'p';
- } else if (value instanceof Boolean) {
- return 'b';
- } else if (value instanceof Integer) {
- return 'i';
- } else if (value instanceof Float) {
- return 'f';
- } else if (value instanceof Double) {
- return 'd';
- } else if (value instanceof Long) {
- return 'l';
- } else if (value instanceof Enum) {
- return 's'; // transported as string representation
- }
- return 'u';
+ addVariableToQueue(paintableId, variableName, map, immediate);
}
/**
@@ -1707,25 +1917,14 @@ public class ApplicationConnection {
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
- * @param newValue
+ * @param values
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
String[] values, boolean immediate) {
- final StringBuffer buf = new StringBuffer();
- if (values != null) {
- for (int i = 0; i < values.length; i++) {
- buf.append(escapeVariableValue(values[i]));
- // there will be an extra separator at the end to differentiate
- // between an empty array and one containing an empty string
- // only
- buf.append(VAR_ARRAYITEM_SEPARATOR);
- }
- }
- addVariableToQueue(paintableId, variableName, buf.toString(),
- immediate, 'c');
+ addVariableToQueue(paintableId, variableName, values, immediate);
}
/**
@@ -1742,44 +1941,25 @@ public class ApplicationConnection {
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
- * @param newValue
+ * @param values
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
Object[] values, boolean immediate) {
- final StringBuffer buf = new StringBuffer();
- if (values != null) {
- for (int i = 0; i < values.length; i++) {
- if (i > 0) {
- buf.append(VAR_ARRAYITEM_SEPARATOR);
- }
- Object value = values[i];
- char transportType = getTransportType(value);
- // first char tells the type in array
- buf.append(transportType);
- if (transportType == 'p') {
- buf.append(getPid((Paintable) value));
- } else {
- buf.append(escapeVariableValue(String.valueOf(value)));
- }
- }
- }
- addVariableToQueue(paintableId, variableName, buf.toString(),
- immediate, 'a');
+ addVariableToQueue(paintableId, variableName, values, immediate);
}
/**
- * Encode burst, record, field and array item separator characters in a
- * String for transport over the network. This protects from separator
- * injection attacks.
+ * 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 escapeVariableValue(String value) {
+ protected String escapeBurstContents(String value) {
final StringBuilder result = new StringBuilder();
for (int i = 0; i < value.length(); ++i) {
char character = value.charAt(i);
@@ -1787,9 +1967,6 @@ public class ApplicationConnection {
case VAR_ESCAPE_CHARACTER:
// fall-through - escape character is duplicated
case VAR_BURST_SEPARATOR:
- case VAR_RECORD_SEPARATOR:
- case VAR_FIELD_SEPARATOR:
- case VAR_ARRAYITEM_SEPARATOR:
result.append(VAR_ESCAPE_CHARACTER);
// encode as letters for easier reading
result.append(((char) (character + 0x30)));
@@ -1803,288 +1980,6 @@ public class ApplicationConnection {
return result.toString();
}
- /**
- * Update generic component features.
- *
- * <h2>Selecting correct implementation</h2>
- *
- * <p>
- * The implementation of a component depends on many properties, including
- * styles, component features, etc. Sometimes the user changes those
- * properties after the component has been created. Calling this method in
- * the beginning of your updateFromUIDL -method automatically replaces your
- * component with more appropriate if the requested implementation changes.
- * </p>
- *
- * <h2>Caption, icon, error messages and description</h2>
- *
- * <p>
- * Component can delegate management of caption, icon, error messages and
- * description to parent layout. This is optional an should be decided by
- * component author
- * </p>
- *
- * <h2>Component visibility and disabling</h2>
- *
- * This method will manage component visibility automatically and if
- * component is an instanceof FocusWidget, also handle component disabling
- * when needed.
- *
- * @param component
- * Widget to be updated, expected to implement an instance of
- * Paintable
- * @param uidl
- * UIDL to be painted
- * @param manageCaption
- * True if you want to delegate caption, icon, description and
- * error message management to parent.
- *
- * @return Returns true iff no further painting is needed by caller
- */
- public boolean updateComponent(Widget component, UIDL uidl,
- boolean manageCaption) {
- String pid = getPid(component.getElement());
- if (pid == null) {
- VConsole.error("Trying to update an unregistered component: "
- + Util.getSimpleName(component));
- return true;
- }
-
- ComponentDetail componentDetail = idToPaintableDetail.get(pid);
-
- if (componentDetail == null) {
- VConsole.error("ComponentDetail not found for "
- + Util.getSimpleName(component) + " with PID " + pid
- + ". This should not happen.");
- return true;
- }
-
- // If the server request that a cached instance should be used, do
- // nothing
- if (uidl.getBooleanAttribute("cached")) {
- return true;
- }
-
- // register the listened events by the server-side to the event-handler
- // of the component
- componentDetail.registerEventListenersFromUIDL(uidl);
-
- // Visibility
- boolean visible = !uidl.getBooleanAttribute("invisible");
- boolean wasVisible = component.isVisible();
- component.setVisible(visible);
- if (wasVisible != visible) {
- // Changed invisibile <-> visible
- if (wasVisible && manageCaption) {
- // Must hide caption when component is hidden
- final Container parent = Util.getLayout(component);
- if (parent != null) {
- parent.updateCaption((Paintable) component, uidl);
- }
-
- }
- }
-
- String id = uidl.getId();
- if (configuration.useDebugIdInDOM() && id.startsWith("PID_")) {
- // PIDs with a debug id have form "PID_[seq]S[debugid]" where [seq]
- // is either empty or a sequential integer used to uniquify
- // different Paintables having the same debug id. See #5109.
- DOM.setElementProperty(component.getElement(), "id",
- id.substring(id.indexOf('S') + 1));
- }
-
- if (!visible) {
- // component is invisible, delete old size to notify parent, if
- // later make visible
- componentDetail.setOffsetSize(null);
- return true;
- }
-
- // Switch to correct implementation if needed
- if (!widgetSet.isCorrectImplementation(component, uidl, configuration)) {
- final Widget w = (Widget) widgetSet.createWidget(uidl,
- configuration);
- // deferred binding check TODO change isCorrectImplementation to use
- // stored detected class, making this innecessary
- if (w.getClass() != component.getClass()) {
- final Container parent = Util.getLayout(component);
- if (parent != null) {
- parent.replaceChildComponent(component, w);
- unregisterPaintable((Paintable) component);
- registerPaintable(uidl.getId(), (Paintable) w);
- ((Paintable) w).updateFromUIDL(uidl, this);
- return true;
- }
- }
- }
-
- boolean enabled = !uidl.getBooleanAttribute("disabled");
- if (uidl.hasAttribute("tabindex") && component instanceof Focusable) {
- ((Focusable) component).setTabIndex(uidl
- .getIntAttribute("tabindex"));
- }
- /*
- * Disabled state may affect (override) tabindex so the order must be
- * first setting tabindex, then enabled state.
- */
- if (component instanceof FocusWidget) {
- FocusWidget fw = (FocusWidget) component;
- fw.setEnabled(enabled);
- }
-
- TooltipInfo tooltipInfo = componentDetail.getTooltipInfo(null);
- // Update tooltip
- if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) {
- tooltipInfo
- .setTitle(uidl.getStringAttribute(ATTRIBUTE_DESCRIPTION));
- } else {
- tooltipInfo.setTitle(null);
- }
-
- // add error classname to components w/ error
- if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
- tooltipInfo.setErrorUidl(uidl.getErrors());
- } else {
- tooltipInfo.setErrorUidl(null);
- }
-
- // Style names
- component.setStyleName(getStyleName(component.getStylePrimaryName(),
- uidl, component instanceof Field));
-
- // Set captions
- if (manageCaption) {
- final Container parent = Util.getLayout(component);
- if (parent != null) {
- parent.updateCaption((Paintable) component, uidl);
- }
- }
- /*
- * updateComponentSize need to be after caption update so caption can be
- * taken into account
- */
-
- updateComponentSize(componentDetail, uidl);
-
- return false;
- }
-
- /**
- * Generates the style name for the widget based on the given primary style
- * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL.
- * An additional "modified" style name can be added if the field parameter
- * is set to true.
- *
- * @param primaryStyleName
- * @param uidl
- * @param isField
- * @return
- */
- public static String getStyleName(String primaryStyleName, UIDL uidl,
- boolean field) {
- boolean enabled = !uidl.getBooleanAttribute("disabled");
-
- StringBuffer styleBuf = new StringBuffer();
- styleBuf.append(primaryStyleName);
-
- // first disabling and read-only status
- if (!enabled) {
- styleBuf.append(" ");
- styleBuf.append(DISABLED_CLASSNAME);
- }
- if (uidl.getBooleanAttribute("readonly")) {
- styleBuf.append(" ");
- styleBuf.append("v-readonly");
- }
-
- // add additional styles as css classes, prefixed with component default
- // stylename
- if (uidl.hasAttribute("style")) {
- final String[] styles = uidl.getStringAttribute("style").split(" ");
- for (int i = 0; i < styles.length; i++) {
- styleBuf.append(" ");
- styleBuf.append(primaryStyleName);
- styleBuf.append("-");
- styleBuf.append(styles[i]);
- styleBuf.append(" ");
- styleBuf.append(styles[i]);
- }
- }
-
- // add modified classname to Fields
- if (field && uidl.hasAttribute("modified")) {
- styleBuf.append(" ");
- styleBuf.append(MODIFIED_CLASSNAME);
- }
-
- if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
- styleBuf.append(" ");
- styleBuf.append(primaryStyleName);
- styleBuf.append(ERROR_CLASSNAME_EXT);
- }
- // add required style to required components
- if (uidl.hasAttribute("required")) {
- styleBuf.append(" ");
- styleBuf.append(primaryStyleName);
- styleBuf.append(REQUIRED_CLASSNAME_EXT);
- }
-
- return styleBuf.toString();
-
- }
-
- private void updateComponentSize(ComponentDetail cd, UIDL uidl) {
- String w = uidl.hasAttribute("width") ? uidl
- .getStringAttribute("width") : "";
-
- String h = uidl.hasAttribute("height") ? uidl
- .getStringAttribute("height") : "";
-
- float relativeWidth = Util.parseRelativeSize(w);
- float relativeHeight = Util.parseRelativeSize(h);
-
- // First update maps so they are correct in the setHeight/setWidth calls
- if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
- // One or both is relative
- FloatSize relativeSize = new FloatSize(relativeWidth,
- relativeHeight);
- if (cd.getRelativeSize() == null && cd.getOffsetSize() != null) {
- // The component has changed from absolute size to relative size
- relativeSizeChanges.add(cd.getComponent());
- }
- cd.setRelativeSize(relativeSize);
- } else if (relativeHeight < 0.0 && relativeWidth < 0.0) {
- if (cd.getRelativeSize() != null) {
- // The component has changed from relative size to absolute size
- relativeSizeChanges.add(cd.getComponent());
- }
- cd.setRelativeSize(null);
- }
-
- Widget component = (Widget) cd.getComponent();
- // Set absolute sizes
- if (relativeHeight < 0.0) {
- component.setHeight(h);
- }
- if (relativeWidth < 0.0) {
- component.setWidth(w);
- }
-
- // Set relative sizes
- if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
- // One or both is relative
- handleComponentRelativeSize(cd);
- }
-
- }
-
- /**
- * Traverses recursively child widgets until ContainerResizedListener child
- * widget is found. They will delegate it further if needed.
- *
- * @param container
- */
private boolean runningLayout = false;
/**
@@ -2106,11 +2001,11 @@ public class ApplicationConnection {
* development. Published to JavaScript.
*/
public void forceLayout() {
- Set<Paintable> set = new HashSet<Paintable>();
- for (ComponentDetail cd : idToPaintableDetail.values()) {
- set.add(cd.getComponent());
- }
- Util.componentSizeUpdated(set);
+ Duration duration = new Duration();
+
+ layoutManager.forceLayout();
+
+ VConsole.log("forceLayout in " + duration.elapsedMillis() + " ms");
}
private void internalRunDescendentsLayout(HasWidgets container) {
@@ -2120,7 +2015,7 @@ public class ApplicationConnection {
while (childWidgets.hasNext()) {
final Widget child = childWidgets.next();
- if (child instanceof Paintable) {
+ if (getConnectorMap().isConnector(child)) {
if (handleComponentRelativeSize(child)) {
/*
@@ -2151,131 +2046,8 @@ public class ApplicationConnection {
* @param child
* @return true if the child has a relative size
*/
- private boolean handleComponentRelativeSize(ComponentDetail cd) {
- if (cd == null) {
- return false;
- }
- boolean debugSizes = false;
-
- FloatSize relativeSize = cd.getRelativeSize();
- if (relativeSize == null) {
- return false;
- }
- Widget widget = (Widget) cd.getComponent();
-
- boolean horizontalScrollBar = false;
- boolean verticalScrollBar = false;
-
- Container parent = Util.getLayout(widget);
- RenderSpace renderSpace;
-
- // Parent-less components (like sub-windows) are relative to browser
- // window.
- if (parent == null) {
- renderSpace = new RenderSpace(Window.getClientWidth(),
- Window.getClientHeight());
- } else {
- renderSpace = parent.getAllocatedSpace(widget);
- }
-
- if (relativeSize.getHeight() >= 0) {
- if (renderSpace != null) {
-
- if (renderSpace.getScrollbarSize() > 0) {
- if (relativeSize.getWidth() > 100) {
- horizontalScrollBar = true;
- } else if (relativeSize.getWidth() < 0
- && renderSpace.getWidth() > 0) {
- int offsetWidth = widget.getOffsetWidth();
- int width = renderSpace.getWidth();
- if (offsetWidth > width) {
- horizontalScrollBar = true;
- }
- }
- }
-
- int height = renderSpace.getHeight();
- if (horizontalScrollBar) {
- height -= renderSpace.getScrollbarSize();
- }
- if (validatingLayouts && height <= 0) {
- zeroHeightComponents.add(cd.getComponent());
- }
-
- height = (int) (height * relativeSize.getHeight() / 100.0);
-
- if (height < 0) {
- height = 0;
- }
-
- if (debugSizes) {
- VConsole.log("Widget " + Util.getSimpleName(widget) + "/"
- + getPid(widget.getElement()) + " relative height "
- + relativeSize.getHeight() + "% of "
- + renderSpace.getHeight() + "px (reported by "
-
- + Util.getSimpleName(parent) + "/"
- + (parent == null ? "?" : parent.hashCode())
- + ") : " + height + "px");
- }
- widget.setHeight(height + "px");
- } else {
- widget.setHeight(relativeSize.getHeight() + "%");
- VConsole.error(Util.getLayout(widget).getClass().getName()
- + " did not produce allocatedSpace for "
- + widget.getClass().getName());
- }
- }
-
- if (relativeSize.getWidth() >= 0) {
-
- if (renderSpace != null) {
-
- int width = renderSpace.getWidth();
-
- if (renderSpace.getScrollbarSize() > 0) {
- if (relativeSize.getHeight() > 100) {
- verticalScrollBar = true;
- } else if (relativeSize.getHeight() < 0
- && renderSpace.getHeight() > 0
- && widget.getOffsetHeight() > renderSpace
- .getHeight()) {
- verticalScrollBar = true;
- }
- }
-
- if (verticalScrollBar) {
- width -= renderSpace.getScrollbarSize();
- }
- if (validatingLayouts && width <= 0) {
- zeroWidthComponents.add(cd.getComponent());
- }
-
- width = (int) (width * relativeSize.getWidth() / 100.0);
-
- if (width < 0) {
- width = 0;
- }
-
- if (debugSizes) {
- VConsole.log("Widget " + Util.getSimpleName(widget) + "/"
- + getPid(widget.getElement()) + " relative width "
- + relativeSize.getWidth() + "% of "
- + renderSpace.getWidth() + "px (reported by "
- + Util.getSimpleName(parent) + "/"
- + (parent == null ? "?" : getPid(parent)) + ") : "
- + width + "px");
- }
- widget.setWidth(width + "px");
- } else {
- widget.setWidth(relativeSize.getWidth() + "%");
- VConsole.error(Util.getLayout(widget).getClass().getName()
- + " did not produce allocatedSpace for "
- + widget.getClass().getName());
- }
- }
-
- return true;
+ private boolean handleComponentRelativeSize(ComponentConnector paintable) {
+ return false;
}
/**
@@ -2284,57 +2056,61 @@ public class ApplicationConnection {
* @param child
* @return true if the child has a relative size
*/
- public boolean handleComponentRelativeSize(Widget child) {
- return handleComponentRelativeSize(idToPaintableDetail.get(getPid(child
- .getElement())));
+ public boolean handleComponentRelativeSize(Widget widget) {
+ return handleComponentRelativeSize(connectorMap.getConnector(widget));
}
- /**
- * Gets the specified Paintables relative size (percent).
- *
- * @param widget
- * the paintable whose size is needed
- * @return the the size if the paintable is relatively sized, -1 otherwise
- */
- public FloatSize getRelativeSize(Widget widget) {
- return idToPaintableDetail.get(getPid(widget.getElement()))
- .getRelativeSize();
+ @Deprecated
+ public ComponentConnector getPaintable(UIDL uidl) {
+ return getConnector(uidl.getId(), Integer.parseInt(uidl.getTag()));
}
/**
- * Get either existing or new Paintable for given UIDL.
+ * Get either an existing ComponentConnector or create a new
+ * ComponentConnector with the given type and id.
*
- * If corresponding Paintable has been previously painted, return it.
- * Otherwise create and register a new Paintable from UIDL. Caller must
- * update the returned Paintable from UIDL after it has been connected to
- * parent.
+ * If a ComponentConnector with the given id already exists, returns it.
+ * Otherwise creates and registers a new ComponentConnector of the given
+ * type.
*
- * @param uidl
- * UIDL to create Paintable from.
- * @return Either existing or new Paintable corresponding to UIDL.
- */
- public Paintable getPaintable(UIDL uidl) {
- final String id = uidl.getId();
- Paintable w = getPaintable(id);
- if (w != null) {
- return w;
- } else {
- w = widgetSet.createWidget(uidl, configuration);
- registerPaintable(id, w);
- return w;
-
+ * @param connectorId
+ * Id of the paintable
+ * @param connectorType
+ * Type of the connector, as passed from the server side
+ *
+ * @return Either an existing ComponentConnector or a new ComponentConnector
+ * of the given type
+ */
+ public ComponentConnector getConnector(String connectorId, int connectorType) {
+ if (!connectorMap.hasConnector(connectorId)) {
+ return createAndRegisterConnector(connectorId, connectorType);
}
+ return (ComponentConnector) connectorMap.getConnector(connectorId);
}
/**
- * Returns a Paintable element by its root element
+ * Creates a new ComponentConnector with the given type and id.
+ *
+ * Creates and registers a new ComponentConnector of the given type. Should
+ * never be called with the connector id of an existing connector.
+ *
+ * @param connectorId
+ * Id of the new connector
+ * @param connectorType
+ * Type of the connector, as passed from the server side
*
- * @param element
- * Root element of the paintable
+ * @return A new ComponentConnector of the given type
*/
- public Paintable getPaintable(Element element) {
- return getPaintable(getPid(element));
+ private ComponentConnector createAndRegisterConnector(String connectorId,
+ int connectorType) {
+ // Create and register a new connector with the given type
+ ComponentConnector p = widgetSet.createWidget(connectorType,
+ configuration);
+ connectorMap.registerConnector(connectorId, p);
+ p.doInit(connectorId, this);
+
+ return p;
}
/**
@@ -2427,16 +2203,12 @@ public class ApplicationConnection {
* Updating TooltipInfo is done in updateComponent method.
*
*/
- public TooltipInfo getTooltipTitleInfo(Paintable titleOwner, Object key) {
+ public TooltipInfo getTooltipTitleInfo(ComponentConnector titleOwner,
+ Object key) {
if (null == titleOwner) {
return null;
}
- ComponentDetail cd = idToPaintableDetail.get(getPid(titleOwner));
- if (null != cd) {
- return cd.getTooltipInfo(key);
- } else {
- return null;
- }
+ return connectorMap.getTooltipInfo(titleOwner, key);
}
private final VTooltip tooltip = new VTooltip(this);
@@ -2451,7 +2223,7 @@ public class ApplicationConnection {
* @param event
* @param owner
*/
- public void handleTooltipEvent(Event event, Paintable owner) {
+ public void handleTooltipEvent(Event event, ComponentConnector owner) {
tooltip.handleTooltipEvent(event, owner, null);
}
@@ -2469,73 +2241,13 @@ public class ApplicationConnection {
* the key for tooltip if this is "additional" tooltip, null for
* components "main tooltip"
*/
- public void handleTooltipEvent(Event event, Paintable owner, Object key) {
+ public void handleTooltipEvent(Event event, ComponentConnector owner,
+ Object key) {
tooltip.handleTooltipEvent(event, owner, key);
}
- /**
- * Adds PNG-fix conditionally (only for IE6) to the specified IMG -element.
- *
- * @param el
- * the IMG element to fix
- */
- public void addPngFix(Element el) {
- BrowserInfo b = BrowserInfo.get();
- if (b.isIE6()) {
- Util.addPngFix(el);
- }
- }
-
- /*
- * Helper to run layout functions triggered by child components with a
- * decent interval.
- */
- private final Timer layoutTimer = new Timer() {
-
- private boolean isPending = false;
-
- @Override
- public void schedule(int delayMillis) {
- if (!isPending) {
- super.schedule(delayMillis);
- isPending = true;
- }
- }
-
- @Override
- public void run() {
- VConsole.log("Running re-layout of " + view.getClass().getName());
- runDescendentsLayout(view);
- isPending = false;
- }
- };
-
- /**
- * Components can call this function to run all layout functions. This is
- * usually done, when component knows that its size has changed.
- */
- public void requestLayoutPhase() {
- layoutTimer.schedule(500);
- }
-
- private String windowName = null;
-
- /**
- * Reset the name of the current browser-window. This should reflect the
- * window-name used in the server, but might be different from the
- * window-object target-name on client.
- *
- * @param stringAttribute
- * New name for the window.
- */
- public void setWindowName(String newName) {
- windowName = newName;
- }
-
- protected String getWindowName() {
- return windowName;
- }
+ private ConnectorMap connectorMap = GWT.create(ConnectorMap.class);
protected String getUidlSecurityKey() {
return uidlSecurityKey;
@@ -2548,17 +2260,17 @@ public class ApplicationConnection {
* @param component
* the Paintable whose caption has changed
*/
- public void captionSizeUpdated(Paintable component) {
- componentCaptionSizeChanges.add(component);
+ public void captionSizeUpdated(Widget widget) {
+ componentCaptionSizeChanges.add(widget);
}
/**
- * Gets the main view, a.k.a top-level window.
+ * Gets the main view
*
* @return the main view
*/
- public VView getView() {
- return view;
+ public RootConnector getRootConnector() {
+ return rootConnector;
}
/**
@@ -2567,7 +2279,7 @@ public class ApplicationConnection {
* this method.
* <p>
* Component must also pipe events to
- * {@link #handleTooltipEvent(Event, Paintable, Object)} method.
+ * {@link #handleTooltipEvent(Event, ComponentConnector, Object)} method.
* <p>
* This method can also be used to deregister tooltips by using null as
* tooltip
@@ -2577,17 +2289,16 @@ public class ApplicationConnection {
* @param key
* key assosiated with given tooltip. Can be any object. For
* example a related dom element. Same key must be given for
- * {@link #handleTooltipEvent(Event, Paintable, Object)} method.
+ * {@link #handleTooltipEvent(Event, ComponentConnector, Object)}
+ * method.
*
* @param tooltip
* the TooltipInfo object containing details shown in tooltip,
* null if deregistering tooltip
*/
- public void registerTooltip(Paintable paintable, Object key,
+ public void registerTooltip(ComponentConnector paintable, Object key,
TooltipInfo tooltip) {
- ComponentDetail componentDetail = idToPaintableDetail
- .get(getPid(paintable));
- componentDetail.putAdditionalTooltip(key, tooltip);
+ connectorMap.registerTooltip(paintable, key, tooltip);
}
/**
@@ -2606,14 +2317,18 @@ public class ApplicationConnection {
* before the component is updated so the value is correct if called from
* updatedFromUIDL.
*
+ * @param paintable
+ * The connector to register event listeners for
* @param eventIdentifier
* The identifier for the event
* @return true if at least one listener has been registered on server side
* for the event identified by eventIdentifier.
+ * @deprecated Use {@link ComponentState#hasEventListener(String)} instead
*/
- public boolean hasEventListeners(Paintable paintable, String eventIdentifier) {
- return idToPaintableDetail.get(getPid(paintable)).hasEventListeners(
- eventIdentifier);
+ @Deprecated
+ public boolean hasEventListeners(ComponentConnector paintable,
+ String eventIdentifier) {
+ return paintable.hasEventListener(eventIdentifier);
}
/**
@@ -2657,4 +2372,71 @@ public class ApplicationConnection {
return uri;
}
+ ConnectorMap getConnectorMap() {
+ return connectorMap;
+ }
+
+ @Deprecated
+ public void unregisterPaintable(ServerConnector p) {
+ System.out.println("unregisterPaintable (unnecessarily) called for "
+ + Util.getConnectorString(p));
+ // connectorMap.unregisterConnector(p);
+ }
+
+ public VTooltip getVTooltip() {
+ return tooltip;
+ }
+
+ @Deprecated
+ public void handleTooltipEvent(Event event, Widget owner, Object key) {
+ handleTooltipEvent(event, getConnectorMap().getConnector(owner), key);
+ }
+
+ /**
+ * Method provided for backwards compatibility. Duties previously done by
+ * this method is now handled by the state change event handler in
+ * AbstractComponentConnector. The only function this method has is to
+ * return true if the UIDL is a "cached" update.
+ *
+ * @param component
+ * @param uidl
+ * @param manageCaption
+ * @return
+ */
+ @Deprecated
+ public boolean updateComponent(Widget component, UIDL uidl,
+ boolean manageCaption) {
+ ComponentConnector connector = getConnectorMap()
+ .getConnector(component);
+ if (!AbstractComponentConnector.isRealUpdate(uidl)) {
+ return true;
+ }
+
+ if (!manageCaption) {
+ VConsole.error(Util.getConnectorString(connector)
+ + " called updateComponent with manageCaption=false. The parameter was ignored - override delegateCaption() to return false instead. It is however not recommended to use caption this way at all.");
+ }
+ return false;
+ }
+
+ @Deprecated
+ public void handleTooltipEvent(Event event, Widget owner) {
+ handleTooltipEvent(event, getConnectorMap().getConnector(owner));
+
+ }
+
+ @Deprecated
+ public void registerTooltip(Widget owner, Object key, TooltipInfo info) {
+ registerTooltip(getConnectorMap().getConnector(owner), key, info);
+ }
+
+ @Deprecated
+ public boolean hasEventListeners(Widget widget, String eventIdentifier) {
+ return hasEventListeners(getConnectorMap().getConnector(widget),
+ eventIdentifier);
+ }
+
+ LayoutManager getLayoutManager() {
+ return layoutManager;
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
index 844b4f2e96..ef1dc481b1 100644
--- a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
+++ b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java
@@ -4,8 +4,6 @@
package com.vaadin.terminal.gwt.client;
-import java.util.Date;
-
import com.google.gwt.user.client.ui.RootPanel;
/**
@@ -66,11 +64,11 @@ public class BrowserInfo {
browserDetails.setIEMode(documentMode);
}
}
-
+
if (browserDetails.isChrome()) {
- touchDevice = detectChromeTouchDevice();
+ touchDevice = detectChromeTouchDevice();
} else {
- touchDevice = detectTouchDevice();
+ touchDevice = detectTouchDevice();
}
}
@@ -78,7 +76,7 @@ public class BrowserInfo {
/*-{
try { document.createEvent("TouchEvent");return true;} catch(e){return false;};
}-*/;
-
+
private native boolean detectChromeTouchDevice()
/*-{
return ("ontouchstart" in window);
@@ -202,14 +200,6 @@ public class BrowserInfo {
return isSafari() && browserDetails.getBrowserMajorVersion() == 4;
}
- public boolean isIE6() {
- return isIE() && browserDetails.getBrowserMajorVersion() == 6;
- }
-
- public boolean isIE7() {
- return isIE() && browserDetails.getBrowserMajorVersion() == 7;
- }
-
public boolean isIE8() {
return isIE() && browserDetails.getBrowserMajorVersion() == 8;
}
@@ -230,23 +220,6 @@ public class BrowserInfo {
return browserDetails.isWebKit();
}
- public boolean isFF2() {
- // FIXME: Should use browserVersion
- return browserDetails.isFirefox()
- && browserDetails.getBrowserEngineVersion() == 1.8;
- }
-
- public boolean isFF3() {
- // FIXME: Should use browserVersion
- return browserDetails.isFirefox()
- && browserDetails.getBrowserEngineVersion() == 1.9;
- }
-
- public boolean isFF4() {
- return browserDetails.isFirefox()
- && browserDetails.getBrowserMajorVersion() == 4;
- }
-
/**
* Returns the Gecko version if the browser is Gecko based. The Gecko
* version for Firefox 2 is 1.8 and 1.9 for Firefox 3.
@@ -321,92 +294,22 @@ public class BrowserInfo {
}-*/;
/**
- * Get's the timezone offset from GMT in minutes, as reported by the
- * browser. DST affects this value.
- *
- * @return offset to GMT in minutes
- */
- public native int getTimezoneOffset()
- /*-{
- return new Date().getTimezoneOffset();
- }-*/;
-
- /**
- * Gets the timezone offset from GMT in minutes, as reported by the browser
- * AND adjusted to ignore daylight savings time. DST does not affect this
- * value.
- *
- * @return offset to GMT in minutes
- */
- public native int getRawTimezoneOffset()
- /*-{
- var d = new Date();
- var tzo1 = d.getTimezoneOffset(); // current offset
-
- for (var m=12;m>0;m--) {
- d.setUTCMonth(m);
- var tzo2 = d.getTimezoneOffset();
- if (tzo1 != tzo2) {
- // NOTE js indicates this 'backwards' (e.g -180)
- return (tzo1 > tzo2 ? tzo1 : tzo2); // offset w/o DST
- }
- }
-
- return tzo1; // no DST
-
- }-*/;
-
- /**
- * Gets the difference in minutes between the browser's GMT timezone and
- * DST.
- *
- * @return the amount of minutes that the timezone shifts when DST is in
- * effect
- */
- public native int getDSTSavings()
- /*-{
- var d = new Date();
- var tzo1 = d.getTimezoneOffset(); // current offset
-
- for (var m=12;m>0;m--) {
- d.setUTCMonth(m);
- var tzo2 = d.getTimezoneOffset();
- if (tzo1 != tzo2) {
- // NOTE js indicates this 'backwards' (e.g -180)
- return (tzo1 > tzo2 ? tzo1-tzo2 : tzo2-tzo1); // offset w/o DST
- }
- }
-
- return 0; // no DST
- }-*/;
-
- /**
- * Determines whether daylight savings time (DST) is currently in effect in
- * the region of the browser or not.
- *
- * @return true if the browser resides at a location that currently is in
- * DST
+ * @return true if the browser runs on a touch based device.
*/
- public boolean isDSTInEffect() {
- return getTimezoneOffset() != getRawTimezoneOffset();
+ public boolean isTouchDevice() {
+ return touchDevice;
}
/**
- * Returns the current date and time of the browser. This will not be
- * entirely accurate due to varying network latencies, but should provide a
- * close-enough value for most cases.
+ * Indicates whether the browser might require juggling to properly update
+ * sizes inside elements with overflow: auto.
*
- * @return the current date and time of the browser.
- */
- public Date getCurrentDate() {
- return new Date();
- }
-
- /**
- * @return true if the browser runs on a touch based device.
+ * @return <code>true</code> if the browser requires the workaround,
+ * otherwise <code>false</code>
*/
- public boolean isTouchDevice() {
- return touchDevice;
+ public boolean requiresOverflowAutoFix() {
+ return (getWebkitVersion() > 0 || getOperaVersion() >= 11)
+ && Util.getNativeScrollbarSize() > 0;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java
new file mode 100644
index 0000000000..5f9171084e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java
@@ -0,0 +1,130 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * An interface used by client-side widgets or paintable parts to receive
+ * updates from the corresponding server-side components in the form of
+ * {@link UIDL}.
+ *
+ * Updates can be sent back to the server using the
+ * {@link ApplicationConnection#updateVariable()} methods.
+ */
+public interface ComponentConnector extends ServerConnector {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.VPaintable#getState()
+ */
+ public ComponentState getState();
+
+ /**
+ * Returns the widget for this {@link ComponentConnector}
+ */
+ public Widget getWidget();
+
+ public LayoutManager getLayoutManager();
+
+ /**
+ * Returns <code>true</code> if the width of this paintable is currently
+ * undefined. If the width is undefined, the actual width of the paintable
+ * is defined by its contents.
+ *
+ * @return <code>true</code> if the width is undefined, else
+ * <code>false</code>
+ */
+ public boolean isUndefinedWidth();
+
+ /**
+ * Returns <code>true</code> if the height of this paintable is currently
+ * undefined. If the height is undefined, the actual height of the paintable
+ * is defined by its contents.
+ *
+ * @return <code>true</code> if the height is undefined, else
+ * <code>false</code>
+ */
+ public boolean isUndefinedHeight();
+
+ /**
+ * Returns <code>true</code> if the width of this paintable is currently
+ * relative. If the width is relative, the actual width of the paintable is
+ * a percentage of the size allocated to it by its parent.
+ *
+ * @return <code>true</code> if the width is undefined, else
+ * <code>false</code>
+ */
+ public boolean isRelativeWidth();
+
+ /**
+ * Returns <code>true</code> if the height of this paintable is currently
+ * relative. If the height is relative, the actual height of the paintable
+ * is a percentage of the size allocated to it by its parent.
+ *
+ * @return <code>true</code> if the width is undefined, else
+ * <code>false</code>
+ */
+ public boolean isRelativeHeight();
+
+ /**
+ * Returns the parent of this connector. Can be null for only the root
+ * connector.
+ *
+ * @return The parent of this connector, as set by
+ * {@link #setParent(ComponentContainerConnector)}.
+ */
+ public ComponentContainerConnector getParent();
+
+ /**
+ * Sets the parent for this connector. This method should only be called by
+ * the framework to ensure that the connector hierarchy on the client side
+ * and the server side are in sync.
+ * <p>
+ * Note that calling this method does not fire a
+ * {@link ConnectorHierarchyChangeEvent}. The event is fired only when the
+ * whole hierarchy has been updated.
+ *
+ * @param parent
+ * The new parent of the connector
+ */
+ public void setParent(ComponentContainerConnector parent);
+
+ /**
+ * Checks if the connector is read only.
+ *
+ * @deprecated This belongs in AbstractFieldConnector, see #8514
+ * @return true
+ */
+ @Deprecated
+ public boolean isReadOnly();
+
+ public boolean hasEventListener(String eventIdentifier);
+
+ /**
+ * Return true if parent handles caption, false if the paintable handles the
+ * caption itself.
+ *
+ * <p>
+ * This should always return true and all components should let the parent
+ * handle the caption and use other attributes for internal texts in the
+ * component
+ * </p>
+ *
+ * @return true if caption handling is delegated to the parent, false if
+ * parent should not be allowed to render caption
+ */
+ public boolean delegateCaptionHandling();
+
+ /**
+ * Sets the enabled state of the widget associated to this connector.
+ *
+ * @param widgetEnabled
+ * true if the widget should be enabled, false otherwise
+ */
+ public void setWidgetEnabled(boolean widgetEnabled);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java b/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java
new file mode 100644
index 0000000000..05334e8049
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ComponentContainerConnector.java
@@ -0,0 +1,74 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.List;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
+
+/**
+ * An interface used by client-side connectors whose widget is a component
+ * container (implements {@link HasWidgets}).
+ */
+public interface ComponentContainerConnector extends ComponentConnector {
+
+ /**
+ * Update child components caption, description and error message.
+ *
+ * <p>
+ * Each component is responsible for maintaining its caption, description
+ * and error message. In most cases components doesn't want to do that and
+ * those elements reside outside of the component. Because of this layouts
+ * must provide service for it's childen to show those elements for them.
+ * </p>
+ *
+ * @param connector
+ * Child component for which service is requested.
+ */
+ void updateCaption(ComponentConnector connector);
+
+ /**
+ * Returns the children for this connector.
+ * <p>
+ * The children for this connector are defined as all
+ * {@link ComponentConnector}s whose parent is this
+ * {@link ComponentContainerConnector}.
+ * </p>
+ *
+ * @return A collection of children for this connector. An empty collection
+ * if there are no children. Never returns null.
+ */
+ public List<ComponentConnector> getChildren();
+
+ /**
+ * Sets the children for this connector. This method should only be called
+ * by the framework to ensure that the connector hierarchy on the client
+ * side and the server side are in sync.
+ * <p>
+ * Note that calling this method does not call
+ * {@link #connectorHierarchyChanged(ConnectorHierarchyChangeEvent)}. The
+ * event method is called only when the hierarchy has been updated for all
+ * connectors.
+ *
+ * @param children
+ * The new child connectors
+ */
+ public void setChildren(List<ComponentConnector> children);
+
+ /**
+ * Adds a handler that is called whenever the child hierarchy of this
+ * connector has been updated by the server.
+ *
+ * @param handler
+ * The handler that should be added.
+ * @return A handler registration reference that can be used to unregister
+ * the handler
+ */
+ public HandlerRegistration addConnectorHierarchyChangeHandler(
+ ConnectorHierarchyChangeHandler handler);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java
index 7fc93b2a3e..686cb640a4 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java
@@ -5,20 +5,12 @@ package com.vaadin.terminal.gwt.client;
import java.util.HashMap;
-import com.google.gwt.core.client.JsArrayString;
-import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-
class ComponentDetail {
- private Paintable component;
private TooltipInfo tooltipInfo = new TooltipInfo();
- private String pid;
- public ComponentDetail(ApplicationConnection client, String pid,
- Paintable component) {
- this.component = component;
- this.pid = pid;
+ public ComponentDetail() {
+
}
/**
@@ -48,54 +40,8 @@ class ComponentDetail {
this.tooltipInfo = tooltipInfo;
}
- private FloatSize relativeSize;
- private Size offsetSize;
private HashMap<Object, TooltipInfo> additionalTooltips;
- /**
- * @return the pid
- */
- String getPid() {
- return pid;
- }
-
- /**
- * @return the component
- */
- Paintable getComponent() {
- return component;
- }
-
- /**
- * @return the relativeSize
- */
- FloatSize getRelativeSize() {
- return relativeSize;
- }
-
- /**
- * @param relativeSize
- * the relativeSize to set
- */
- void setRelativeSize(FloatSize relativeSize) {
- this.relativeSize = relativeSize;
- }
-
- /**
- * @return the offsetSize
- */
- Size getOffsetSize() {
- return offsetSize;
- }
-
- /**
- * @param offsetSize
- * the offsetSize to set
- */
- void setOffsetSize(Size offsetSize) {
- this.offsetSize = offsetSize;
- }
-
public void putAdditionalTooltip(Object key, TooltipInfo tooltip) {
if (tooltip == null && additionalTooltips != null) {
additionalTooltips.remove(key);
@@ -107,38 +53,4 @@ class ComponentDetail {
}
}
- private JsArrayString eventListeners;
-
- /**
- * Stores the event listeners registered on server-side and passed along in
- * the UIDL.
- *
- * @param componentUIDL
- * The UIDL for the component
- * @since 6.2
- */
- native void registerEventListenersFromUIDL(UIDL uidl)
- /*-{
- this.@com.vaadin.terminal.gwt.client.ComponentDetail::eventListeners = uidl[1].eventListeners;
- }-*/;
-
- /**
- * Checks if there is a registered server side listener for the event.
- *
- * @param eventIdentifier
- * The identifier for the event
- * @return true if at least one listener has been registered on server side
- * for the event identified by eventIdentifier.
- */
- public boolean hasEventListeners(String eventIdentifier) {
- if (eventListeners != null) {
- int l = eventListeners.length();
- for (int i = 0; i < l; i++) {
- if (eventListeners.get(i).equals(eventIdentifier)) {
- return true;
- }
- }
- }
- return false;
- }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
index b71b5fdeb5..d847d49e6f 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
@@ -5,6 +5,7 @@ package com.vaadin.terminal.gwt.client;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
@@ -12,8 +13,12 @@ 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.terminal.gwt.client.ui.SubPartAware;
-import com.vaadin.terminal.gwt.client.ui.VView;
-import com.vaadin.terminal.gwt.client.ui.VWindow;
+import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.VMeasuringOrderedLayout;
+import com.vaadin.terminal.gwt.client.ui.root.VRoot;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheetPanel;
+import com.vaadin.terminal.gwt.client.ui.window.VWindow;
+import com.vaadin.terminal.gwt.client.ui.window.WindowConnector;
/**
* ComponentLocator provides methods for generating a String locator for a given
@@ -78,7 +83,7 @@ public class ComponentLocator {
Element e = targetElement;
while (true) {
- pid = client.getPid(e);
+ pid = ConnectorMap.get(client).getConnectorId(e);
if (pid != null) {
break;
}
@@ -94,7 +99,8 @@ public class ComponentLocator {
// 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 = (Widget) client.getPaintable(pid);
+ w = ((ComponentConnector) ConnectorMap.get(client)
+ .getConnector(pid)).getWidget();
/*
* Still if the Paintable contains a widget that implements
@@ -364,18 +370,14 @@ public class ComponentLocator {
return null;
}
- String pid = client.getPid(w.getElement());
- if (isStaticPid(pid)) {
- return pid;
- }
-
- if (w instanceof VView) {
+ if (w instanceof VRoot) {
return "";
} else if (w instanceof VWindow) {
- VWindow win = (VWindow) w;
- ArrayList<VWindow> subWindowList = client.getView()
- .getSubWindowList();
- int indexOfSubWindow = subWindowList.indexOf(win);
+ Connector windowConnector = ConnectorMap.get(client)
+ .getConnector(w);
+ List<WindowConnector> subWindowList = client.getRootConnector()
+ .getSubWindows();
+ int indexOfSubWindow = subWindowList.indexOf(windowConnector);
return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]";
} else if (w instanceof RootPanel) {
return ROOT_ID;
@@ -434,10 +436,25 @@ public class ComponentLocator {
if (part.equals(ROOT_ID)) {
w = RootPanel.get();
} else if (part.equals("")) {
- w = client.getView();
+ w = client.getRootConnector().getWidget();
} else if (w == null) {
- // Must be static pid (PID_*)
- w = (Widget) client.getPaintable(part);
+ String id = part;
+ // Must be old static pid (PID_S*)
+ ComponentConnector connector = (ComponentConnector) ConnectorMap
+ .get(client).getConnector(id);
+ if (connector == null) {
+ // Lookup by debugId
+ // TODO Optimize this
+ connector = findConnectorById(client.getRootConnector(),
+ id.substring(5));
+ }
+
+ if (connector != null) {
+ w = connector.getWidget();
+ } else {
+ // Not found
+ return null;
+ }
} else if (part.startsWith("domChild[")) {
// The target widget has been found and the rest identifies the
// element
@@ -455,6 +472,67 @@ public class ComponentLocator {
int widgetPosition = Integer.parseInt(indexString.substring(0,
indexString.length() - 1));
+ // AbsolutePanel in GridLayout has been removed -> skip it
+ if (w instanceof VGridLayout
+ && "AbsolutePanel".equals(widgetClassName)) {
+ continue;
+ }
+
+ 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;
+ }
+
+ /*
+ * 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 VMeasuringOrderedLayout || 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;
@@ -463,7 +541,14 @@ public class ComponentLocator {
* compatibility
*/
if (widgetClassName.equals("VWindow")) {
- iterator = client.getView().getSubWindowList().iterator();
+ List<WindowConnector> windows = client.getRootConnector()
+ .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 {
@@ -503,19 +588,23 @@ public class ComponentLocator {
return w;
}
- /**
- * Checks if the given pid is a static pid.
- *
- * @param pid
- * The pid to check
- * @return true if the pid is a static pid, false otherwise
- */
- private boolean isStaticPid(String pid) {
- if (pid == null) {
- return false;
+ private ComponentConnector findConnectorById(ComponentConnector root,
+ String id) {
+ if (root instanceof ComponentConnector
+ && id.equals(root.getState().getDebugId())) {
+ return root;
+ }
+ if (root instanceof ComponentContainerConnector) {
+ ComponentContainerConnector ccc = (ComponentContainerConnector) root;
+ for (ComponentConnector child : ccc.getChildren()) {
+ ComponentConnector found = findConnectorById(child, id);
+ if (found != null) {
+ return found;
+ }
+ }
}
- return pid.startsWith("PID_");
+ return null;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentState.java b/src/com/vaadin/terminal/gwt/client/ComponentState.java
new file mode 100644
index 0000000000..10cd14251b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ComponentState.java
@@ -0,0 +1,406 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.communication.URLReference;
+import com.vaadin.ui.Component;
+
+/**
+ * Default shared state implementation for UI components.
+ *
+ * State classes of concrete components should extend this class.
+ *
+ * @since 7.0
+ */
+public class ComponentState extends SharedState {
+ private String height = "";
+ private String width = "";
+ private boolean readOnly = false;
+ private boolean immediate = false;
+ private boolean enabled = true;
+ private String description = "";
+ // Note: for the caption, there is a difference between null and an empty
+ // string!
+ private String caption = null;
+ private boolean visible = true;
+ private URLReference icon = null;
+ private List<String> styles = null;
+ private String debugId = null;
+ /**
+ * A set of event identifiers with registered listeners.
+ */
+ private Set<String> registeredEventListeners = null;
+
+ // HTML formatted error message for the component
+ // TODO this could be an object with more information, but currently the UI
+ // only uses the message
+ private String errorMessage = null;
+
+ /**
+ * Returns the component height as set by the server.
+ *
+ * Can be relative (containing the percent sign) or absolute, or empty
+ * string for undefined height.
+ *
+ * @return component height as defined by the server, not null
+ */
+ public String getHeight() {
+ if (height == null) {
+ return "";
+ }
+ return height;
+ }
+
+ /**
+ * Sets the height of the component in the server format.
+ *
+ * Can be relative (containing the percent sign) or absolute, or null or
+ * empty string for undefined height.
+ *
+ * @param height
+ * component height
+ */
+ public void setHeight(String height) {
+ this.height = height;
+ }
+
+ /**
+ * Returns true if the component height is undefined, false if defined
+ * (absolute or relative).
+ *
+ * @return true if component height is undefined
+ */
+ public boolean isUndefinedHeight() {
+ return "".equals(getHeight());
+ }
+
+ /**
+ * Returns the component width as set by the server.
+ *
+ * Can be relative (containing the percent sign) or absolute, or empty
+ * string for undefined height.
+ *
+ * @return component width as defined by the server, not null
+ */
+ public String getWidth() {
+ if (width == null) {
+ return "";
+ }
+ return width;
+ }
+
+ /**
+ * Sets the width of the component in the server format.
+ *
+ * Can be relative (containing the percent sign) or absolute, or null or
+ * empty string for undefined width.
+ *
+ * @param width
+ * component width
+ */
+ public void setWidth(String width) {
+ this.width = width;
+ }
+
+ /**
+ * Returns true if the component width is undefined, false if defined
+ * (absolute or relative).
+ *
+ * @return true if component width is undefined
+ */
+ public boolean isUndefinedWidth() {
+ return "".equals(getWidth());
+ }
+
+ /**
+ * Returns true if the component is in read-only mode.
+ *
+ * @see com.vaadin.ui.Component#isReadOnly()
+ *
+ * @return true if the component is in read-only mode
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ /**
+ * Sets or resets the read-only mode for a component.
+ *
+ * @see com.vaadin.ui.Component#setReadOnly()
+ *
+ * @param readOnly
+ * new mode for the component
+ */
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ /**
+ * Returns true if the component is in immediate mode.
+ *
+ * @see com.vaadin.terminal.VariableOwner#isImmediate()
+ *
+ * @return true if the component is in immediate mode
+ */
+ public boolean isImmediate() {
+ return immediate;
+ }
+
+ /**
+ * Sets or resets the immediate mode for a component.
+ *
+ * @see com.vaadin.terminal.VariableOwner#setImmediate()
+ *
+ * @param immediate
+ * new mode for the component
+ */
+ public void setImmediate(boolean immediate) {
+ this.immediate = immediate;
+ }
+
+ /**
+ * Returns true if the component has user-defined styles.
+ *
+ * @return true if the component has user-defined styles
+ */
+ public boolean hasStyles() {
+ return styles != null && !styles.isEmpty();
+ }
+
+ /**
+ * Returns true if the component is enabled.
+ *
+ * @see com.vaadin.ui.Component#isEnabled()
+ *
+ * @return true if the component is enabled
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Enables or disables the component.
+ *
+ * @see com.vaadin.ui.Component#setEnabled(boolean)
+ *
+ * @param enabled
+ * new mode for the component
+ */
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Gets the description of the component (typically shown as tooltip).
+ *
+ * @see com.vaadin.ui.AbstractComponent#getDescription()
+ *
+ * @return component description (not null, can be empty string)
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the description of the component (typically shown as tooltip).
+ *
+ * @see com.vaadin.ui.AbstractComponent#setDescription(String)
+ *
+ * @param description
+ * new component description (can be null)
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns true if the component has a description.
+ *
+ * @return true if the component has a description
+ */
+ public boolean hasDescription() {
+ return getDescription() != null && !"".equals(getDescription());
+ }
+
+ /**
+ * Gets the caption of the component (typically shown by the containing
+ * layout).
+ *
+ * @see com.vaadin.ui.Component#getCaption()
+ *
+ * @return component caption - can be null (no caption) or empty string
+ * (reserve space for an empty caption)
+ */
+ public String getCaption() {
+ return caption;
+ }
+
+ /**
+ * Sets the caption of the component (typically shown by the containing
+ * layout).
+ *
+ * @see com.vaadin.ui.Component#setCaption(String)
+ *
+ * @param caption
+ * new component caption - can be null (no caption) or empty
+ * string (reserve space for an empty caption)
+ */
+ public void setCaption(String caption) {
+ this.caption = caption;
+ }
+
+ /**
+ * Returns the visibility state of the component. Note that this state is
+ * related to the component only, not its parent. This might differ from
+ * what {@link Component#isVisible()} returns as this takes the hierarchy
+ * into account.
+ *
+ * @return The visibility state.
+ */
+ public boolean isVisible() {
+ return visible;
+ }
+
+ /**
+ * Sets the visibility state of the component.
+ *
+ * @param visible
+ * The new visibility state.
+ */
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ public URLReference getIcon() {
+ return icon;
+ }
+
+ public void setIcon(URLReference icon) {
+ this.icon = icon;
+ }
+
+ /**
+ * Gets the style names for the component.
+ *
+ * @return A List of style names or null if no styles have been set.
+ */
+ public List<String> getStyles() {
+ return styles;
+ }
+
+ /**
+ * Sets the style names for the component.
+ *
+ * @param styles
+ * A list containing style names
+ */
+ public void setStyles(List<String> styles) {
+ this.styles = styles;
+ }
+
+ /**
+ * Gets the debug id for the component. The debugId is added as DOM id for
+ * the component.
+ *
+ * @return The debug id for the component or null if not set
+ */
+ public String getDebugId() {
+ return debugId;
+ }
+
+ /**
+ * Sets the debug id for the component. The debugId is added as DOM id for
+ * the component.
+ *
+ * @param debugId
+ * The new debugId for the component or null for no debug id
+ *
+ */
+ public void setDebugId(String debugId) {
+ this.debugId = debugId;
+ }
+
+ /**
+ * Gets the identifiers for the event listeners that have been registered
+ * for the component (using an event id)
+ *
+ * @return A set of event identifiers or null if no identifiers have been
+ * registered
+ */
+ public Set<String> getRegisteredEventListeners() {
+ return registeredEventListeners;
+ }
+
+ /**
+ * Sets the identifiers for the event listeners that have been registered
+ * for the component (using an event id)
+ *
+ * @param registeredEventListeners
+ * The new set of identifiers or null if no identifiers have been
+ * registered
+ */
+ public void setRegisteredEventListeners(Set<String> registeredEventListeners) {
+ this.registeredEventListeners = registeredEventListeners;
+ }
+
+ /**
+ * Adds an event listener id.
+ *
+ * @param eventListenerId
+ * The event identifier to add
+ */
+ public void addRegisteredEventListener(String eventListenerId) {
+ if (registeredEventListeners == null) {
+ registeredEventListeners = new HashSet<String>();
+ }
+ registeredEventListeners.add(eventListenerId);
+
+ }
+
+ /**
+ * Removes an event listener id.
+ *
+ * @param eventListenerId
+ * The event identifier to remove
+ */
+ public void removeRegisteredEventListener(String eventIdentifier) {
+ if (registeredEventListeners == null) {
+ return;
+ }
+ registeredEventListeners.remove(eventIdentifier);
+ if (registeredEventListeners.size() == 0) {
+ registeredEventListeners = null;
+ }
+ }
+
+ /**
+ * Returns the current error message for the component.
+ *
+ * @return HTML formatted error message to show for the component or null if
+ * none
+ */
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ /**
+ * Sets the current error message for the component.
+ *
+ * TODO this could use an object with more details about the error
+ *
+ * @param errorMessage
+ * HTML formatted error message to show for the component or null
+ * for none
+ */
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ComputedStyle.java b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java
index e994e47d63..bedb217c63 100644
--- a/src/com/vaadin/terminal/gwt/client/ComputedStyle.java
+++ b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java
@@ -4,7 +4,7 @@
package com.vaadin.terminal.gwt.client;
import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.dom.client.Element;
public class ComputedStyle {
diff --git a/src/com/vaadin/terminal/gwt/client/Connector.java b/src/com/vaadin/terminal/gwt/client/Connector.java
new file mode 100644
index 0000000000..1c61052735
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/Connector.java
@@ -0,0 +1,49 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+
+/**
+ * Interface implemented by all classes that are capable of communicating with
+ * the server or the client side.
+ * <p>
+ * A connector consists of a shared state (server sets the state and
+ * automatically communicates changes to the client) and the possibility to do
+ * RPC calls either from the server to the client or from the client to the
+ * server.
+ * </p>
+ * <p>
+ * No classes should implement this interface directly, client side classes
+ * wanting to communicate with server side should implement
+ * {@link ServerConnector} and server side classes should implement
+ * {@link ClientConnector}.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public interface Connector extends Serializable {
+ /**
+ * Gets the current shared state of the connector.
+ *
+ * @since 7.0.
+ * @return state The shared state object. Can be any sub type of
+ * {@link SharedState}. Never null.
+ */
+ public SharedState getState();
+
+ /**
+ * Returns the id for this connector. This is set by the framework and does
+ * not change during the lifetime of a connector.
+ *
+ * @return The id for the connector.
+ */
+ public String getConnectorId();
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ConnectorHierarchyChangeEvent.java b/src/com/vaadin/terminal/gwt/client/ConnectorHierarchyChangeEvent.java
new file mode 100644
index 0000000000..aa41caf75d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ConnectorHierarchyChangeEvent.java
@@ -0,0 +1,97 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
+import com.vaadin.terminal.gwt.client.communication.AbstractServerConnectorEvent;
+
+/**
+ * Event for containing data related to a change in the {@link ServerConnector}
+ * hierarchy. A {@link ConnectorHierarchyChangedEvent} is fired when an update
+ * from the server has been fully processed and all hierarchy updates have been
+ * completed.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public class ConnectorHierarchyChangeEvent extends
+ AbstractServerConnectorEvent<ConnectorHierarchyChangeHandler> {
+ /**
+ * Type of this event, used by the event bus.
+ */
+ public static final Type<ConnectorHierarchyChangeHandler> TYPE = new Type<ConnectorHierarchyChangeHandler>();
+
+ List<ComponentConnector> oldChildren;
+ private ComponentContainerConnector parent;
+
+ public ConnectorHierarchyChangeEvent() {
+ }
+
+ /**
+ * Returns a collection of the old children for the connector. This was the
+ * state before the update was received from the server.
+ *
+ * @return A collection of old child connectors. Never returns null.
+ */
+ public List<ComponentConnector> getOldChildren() {
+ return oldChildren;
+ }
+
+ /**
+ * Sets the collection of the old children for the connector.
+ *
+ * @param oldChildren
+ * The old child connectors. Must not be null.
+ */
+ public void setOldChildren(List<ComponentConnector> oldChildren) {
+ this.oldChildren = oldChildren;
+ }
+
+ /**
+ * Returns the {@link ComponentContainerConnector} for which this event
+ * occurred.
+ *
+ * @return The {@link ComponentContainerConnector} whose child collection
+ * has changed. Never returns null.
+ */
+ public ComponentContainerConnector getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the {@link ComponentContainerConnector} for which this event
+ * occurred.
+ *
+ * @param The
+ * {@link ComponentContainerConnector} whose child collection has
+ * changed.
+ */
+ public void setParent(ComponentContainerConnector parent) {
+ this.parent = parent;
+ }
+
+ public interface ConnectorHierarchyChangeHandler extends Serializable,
+ EventHandler {
+ public void onConnectorHierarchyChange(
+ ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent);
+ }
+
+ @Override
+ public void dispatch(ConnectorHierarchyChangeHandler handler) {
+ handler.onConnectorHierarchyChange(this);
+ }
+
+ @Override
+ public GwtEvent.Type<ConnectorHierarchyChangeHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ConnectorMap.java b/src/com/vaadin/terminal/gwt/client/ConnectorMap.java
new file mode 100644
index 0000000000..e57776cf1c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ConnectorMap.java
@@ -0,0 +1,250 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.ui.Widget;
+
+public class ConnectorMap {
+
+ private Map<String, ServerConnector> idToConnector = new HashMap<String, ServerConnector>();
+
+ public static ConnectorMap get(ApplicationConnection applicationConnection) {
+ return applicationConnection.getConnectorMap();
+ }
+
+ @Deprecated
+ private final ComponentDetailMap idToComponentDetail = ComponentDetailMap
+ .create();
+
+ /**
+ * Returns a {@link ServerConnector} by its id
+ *
+ * @param id
+ * The connector id
+ * @return A connector or null if a connector with the given id has not been
+ * registered
+ */
+ public ServerConnector getConnector(String connectorId) {
+ return idToConnector.get(connectorId);
+ }
+
+ /**
+ * Returns a {@link ComponentConnector} element by its root element
+ *
+ * @param element
+ * Root element of the {@link ComponentConnector}
+ * @return A connector or null if a connector with the given id has not been
+ * registered
+ */
+ public ComponentConnector getConnector(Element element) {
+ return (ComponentConnector) getConnector(getConnectorId(element));
+ }
+
+ /**
+ * FIXME: What does this even do and why?
+ *
+ * @param pid
+ * @return
+ */
+ public boolean isDragAndDropPaintable(String pid) {
+ return (pid.startsWith("DD"));
+ }
+
+ /**
+ * Checks if a connector with the given id has been registered.
+ *
+ * @param connectorId
+ * The id to check for
+ * @return true if a connector has been registered with the given id, false
+ * otherwise
+ */
+ public boolean hasConnector(String connectorId) {
+ return idToConnector.containsKey(connectorId);
+ }
+
+ /**
+ * Removes all registered connectors
+ */
+ public void clear() {
+ idToConnector.clear();
+ idToComponentDetail.clear();
+ }
+
+ /**
+ * Retrieves the connector whose widget matches the parameter.
+ *
+ * @param widget
+ * The widget
+ * @return A connector with {@literal widget} as its root widget or null if
+ * no connector was found
+ */
+ public ComponentConnector getConnector(Widget widget) {
+ return getConnector(widget.getElement());
+ }
+
+ public void registerConnector(String id, ServerConnector connector) {
+ ComponentDetail componentDetail = GWT.create(ComponentDetail.class);
+ idToComponentDetail.put(id, componentDetail);
+ idToConnector.put(id, connector);
+ if (connector instanceof ComponentConnector) {
+ ComponentConnector pw = (ComponentConnector) connector;
+ setConnectorId(pw.getWidget().getElement(), id);
+ }
+ }
+
+ private native void setConnectorId(Element el, String id)
+ /*-{
+ el.tkPid = id;
+ }-*/;
+
+ /**
+ * Gets the connector id using a DOM element - the element should be the
+ * root element for a connector, otherwise no id will be found. Use
+ * {@link #getConnectorId(ServerConnector)} instead whenever possible.
+ *
+ * @see #getConnectorId(ServerConnector)
+ * @param el
+ * element of the connector whose id is desired
+ * @return the id of the element's connector, if it's a connector
+ */
+ native String getConnectorId(Element el)
+ /*-{
+ return el.tkPid;
+ }-*/;
+
+ /**
+ * Gets the main element for the connector with the given id. The reverse of
+ * {@link #getConnectorId(Element)}.
+ *
+ * @param connectorId
+ * the id of the widget whose element is desired
+ * @return the element for the connector corresponding to the id
+ */
+ public Element getElement(String connectorId) {
+ ServerConnector p = getConnector(connectorId);
+ if (p instanceof ComponentConnector) {
+ return ((ComponentConnector) p).getWidget().getElement();
+ }
+
+ return null;
+ }
+
+ /**
+ * Unregisters the given connector; always use after removing a connector.
+ * This method does not remove the connector from the DOM, but marks the
+ * connector so that ApplicationConnection may clean up its references to
+ * it. Removing the widget from DOM is component containers responsibility.
+ *
+ * @param connector
+ * the connector to remove
+ */
+ public void unregisterConnector(ServerConnector connector) {
+ if (connector == null) {
+ VConsole.error("Trying to unregister null connector");
+ return;
+ }
+
+ String connectorId = connector.getConnectorId();
+
+ idToComponentDetail.remove(connectorId);
+ idToConnector.remove(connectorId);
+ connector.onUnregister();
+
+ if (connector instanceof ComponentContainerConnector) {
+ for (ComponentConnector child : ((ComponentContainerConnector) connector)
+ .getChildren()) {
+ if (child.getParent() == connector) {
+ // Only unregister children that are actually connected to
+ // this parent. For instance when moving connectors from one
+ // layout to another and removing the first layout it will
+ // still contain references to its old children, which are
+ // now attached to another connector.
+ unregisterConnector(child);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets all registered {@link ComponentConnector} instances
+ *
+ * @return An array of all registered {@link ComponentConnector} instances
+ */
+ public ComponentConnector[] getComponentConnectors() {
+ ArrayList<ComponentConnector> result = new ArrayList<ComponentConnector>();
+
+ for (ServerConnector connector : getConnectors()) {
+ if (connector instanceof ComponentConnector) {
+ result.add((ComponentConnector) connector);
+ }
+ }
+
+ return result.toArray(new ComponentConnector[result.size()]);
+ }
+
+ @Deprecated
+ private ComponentDetail getComponentDetail(
+ ComponentConnector componentConnector) {
+ return idToComponentDetail.get(componentConnector.getConnectorId());
+ }
+
+ public int size() {
+ return idToConnector.size();
+ }
+
+ /**
+ * FIXME: Should be moved to VAbstractPaintableWidget
+ *
+ * @param paintable
+ * @return
+ */
+ @Deprecated
+ public TooltipInfo getTooltipInfo(ComponentConnector paintable, Object key) {
+ return getComponentDetail(paintable).getTooltipInfo(key);
+ }
+
+ @Deprecated
+ public TooltipInfo getWidgetTooltipInfo(Widget widget, Object key) {
+ return getTooltipInfo(getConnector(widget), key);
+ }
+
+ public Collection<? extends ServerConnector> getConnectors() {
+ return Collections.unmodifiableCollection(idToConnector.values());
+ }
+
+ /**
+ * FIXME: Should not be here
+ *
+ * @param componentConnector
+ * @return
+ */
+ @Deprecated
+ public void registerTooltip(ComponentConnector componentConnector,
+ Object key, TooltipInfo tooltip) {
+ getComponentDetail(componentConnector).putAdditionalTooltip(key,
+ tooltip);
+
+ }
+
+ /**
+ * Tests if the widget is the root widget of a {@link ComponentConnector}.
+ *
+ * @param widget
+ * The widget to test
+ * @return true if the widget is the root widget of a
+ * {@link ComponentConnector}, false otherwise
+ */
+ public boolean isConnector(Widget w) {
+ return getConnectorId(w.getElement()) != null;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/Console.java b/src/com/vaadin/terminal/gwt/client/Console.java
index 483ab8e0fd..8db145342a 100644
--- a/src/com/vaadin/terminal/gwt/client/Console.java
+++ b/src/com/vaadin/terminal/gwt/client/Console.java
@@ -22,8 +22,8 @@ public interface Console {
public abstract void printLayoutProblems(ValueMap meta,
ApplicationConnection applicationConnection,
- Set<Paintable> zeroHeightComponents,
- Set<Paintable> zeroWidthComponents);
+ Set<ComponentConnector> zeroHeightComponents,
+ Set<ComponentConnector> zeroWidthComponents);
public abstract void setQuietMode(boolean quietDebugMode);
diff --git a/src/com/vaadin/terminal/gwt/client/Container.java b/src/com/vaadin/terminal/gwt/client/Container.java
deleted file mode 100644
index 83f104886f..0000000000
--- a/src/com/vaadin/terminal/gwt/client/Container.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client;
-
-import java.util.Set;
-
-import com.google.gwt.user.client.ui.Widget;
-
-public interface Container extends Paintable {
-
- /**
- * Replace child of this layout with another component.
- *
- * Each layout must be able to switch children. To to this, one must just
- * give references to a current and new child.
- *
- * @param oldComponent
- * Child to be replaced
- * @param newComponent
- * Child that replaces the oldComponent
- */
- void replaceChildComponent(Widget oldComponent, Widget newComponent);
-
- /**
- * Is a given component child of this layout.
- *
- * @param component
- * Component to test.
- * @return true iff component is a child of this layout.
- */
- boolean hasChildComponent(Widget component);
-
- /**
- * Update child components caption, description and error message.
- *
- * <p>
- * Each component is responsible for maintaining its caption, description
- * and error message. In most cases components doesn't want to do that and
- * those elements reside outside of the component. Because of this layouts
- * must provide service for it's childen to show those elements for them.
- * </p>
- *
- * @param component
- * Child component for which service is requested.
- * @param uidl
- * UIDL of the child component.
- */
- void updateCaption(Paintable component, UIDL uidl);
-
- /**
- * Called when a child components size has been updated in the rendering
- * phase.
- *
- * @param children
- * Set of child widgets whose size have changed
- * @return true if the size of the Container remains the same, false if the
- * event need to be propagated to the Containers parent
- */
- boolean requestLayout(Set<Paintable> children);
-
- /**
- * Returns the size currently allocated for the child component.
- *
- * @param child
- * @return
- */
- RenderSpace getAllocatedSpace(Widget child);
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/DateTimeService.java b/src/com/vaadin/terminal/gwt/client/DateTimeService.java
index c0151d2819..45ba4a7452 100644
--- a/src/com/vaadin/terminal/gwt/client/DateTimeService.java
+++ b/src/com/vaadin/terminal/gwt/client/DateTimeService.java
@@ -8,7 +8,7 @@ import java.util.Date;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.LocaleInfo;
-import com.vaadin.terminal.gwt.client.ui.VDateField;
+import com.vaadin.terminal.gwt.client.ui.datefield.VDateField;
/**
* This class provides date/time parsing services to all components on the
diff --git a/src/com/vaadin/terminal/gwt/client/DirectionalManagedLayout.java b/src/com/vaadin/terminal/gwt/client/DirectionalManagedLayout.java
new file mode 100644
index 0000000000..296fbb22ff
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/DirectionalManagedLayout.java
@@ -0,0 +1,12 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
+
+public interface DirectionalManagedLayout extends ManagedLayout {
+ public void layoutVertically();
+
+ public void layoutHorizontally();
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/EventHelper.java b/src/com/vaadin/terminal/gwt/client/EventHelper.java
index 600baf8c9d..95f5125f1b 100644
--- a/src/com/vaadin/terminal/gwt/client/EventHelper.java
+++ b/src/com/vaadin/terminal/gwt/client/EventHelper.java
@@ -6,10 +6,12 @@ package com.vaadin.terminal.gwt.client;
import static com.vaadin.terminal.gwt.client.EventId.BLUR;
import static com.vaadin.terminal.gwt.client.EventId.FOCUS;
+import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.DomEvent.Type;
+import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.HasBlurHandlers;
-import com.google.gwt.event.dom.client.HasFocusHandlers;
+import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
/**
@@ -40,38 +42,56 @@ import com.google.gwt.event.shared.HandlerRegistration;
*/
public class EventHelper {
- public static HandlerRegistration updateFocusHandler(Paintable paintable,
- ApplicationConnection client,
- HandlerRegistration handlerRegistration) {
- if (client.hasEventListeners(paintable, FOCUS)) {
- if (handlerRegistration == null) {
- handlerRegistration = ((HasFocusHandlers) paintable)
- .addFocusHandler((FocusHandler) paintable);
- }
- return handlerRegistration;
- } else if (handlerRegistration != null) {
- handlerRegistration.removeHandler();
- handlerRegistration = null;
+ /**
+ * Adds or removes a focus handler depending on if the connector has focus
+ * listeners on the server side or not.
+ *
+ * @param connector
+ * The connector to update. Must implement focusHandler.
+ * @param handlerRegistration
+ * The old registration reference or null no handler has been
+ * registered previously
+ * @return a new registration handler that can be used to unregister the
+ * handler later
+ */
+ public static <T extends ComponentConnector & FocusHandler> HandlerRegistration updateFocusHandler(
+ T connector, HandlerRegistration handlerRegistration) {
+ return updateHandler(connector, FOCUS, handlerRegistration,
+ FocusEvent.getType());
+ }
- }
- return null;
+ /**
+ * Adds or removes a blur handler depending on if the connector has blur
+ * listeners on the server side or not.
+ *
+ * @param connector
+ * The connector to update. Must implement BlurHandler.
+ * @param handlerRegistration
+ * The old registration reference or null no handler has been
+ * registered previously
+ * @return a new registration handler that can be used to unregister the
+ * handler later
+ */
+ public static <T extends ComponentConnector & BlurHandler> HandlerRegistration updateBlurHandler(
+ T connector, HandlerRegistration handlerRegistration) {
+ return updateHandler(connector, BLUR, handlerRegistration,
+ BlurEvent.getType());
}
- public static HandlerRegistration updateBlurHandler(Paintable paintable,
- ApplicationConnection client,
- HandlerRegistration handlerRegistration) {
- if (client.hasEventListeners(paintable, BLUR)) {
+ private static <H extends EventHandler> HandlerRegistration updateHandler(
+ ComponentConnector connector, String eventIdentifier,
+ HandlerRegistration handlerRegistration, Type<H> type) {
+ if (connector.hasEventListener(eventIdentifier)) {
if (handlerRegistration == null) {
- handlerRegistration = ((HasBlurHandlers) paintable)
- .addBlurHandler((BlurHandler) paintable);
+ handlerRegistration = connector.getWidget().addDomHandler(
+ (H) connector, type);
}
- return handlerRegistration;
} else if (handlerRegistration != null) {
handlerRegistration.removeHandler();
handlerRegistration = null;
-
}
- return null;
+ return handlerRegistration;
+
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/EventId.java b/src/com/vaadin/terminal/gwt/client/EventId.java
index ae2f5ddeb3..d3ef2e4e7e 100644
--- a/src/com/vaadin/terminal/gwt/client/EventId.java
+++ b/src/com/vaadin/terminal/gwt/client/EventId.java
@@ -6,5 +6,4 @@ package com.vaadin.terminal.gwt.client;
public interface EventId {
public static final String BLUR = "blur";
public static final String FOCUS = "focus";
- public static final String LAYOUT_CLICK = "layout_click";
}
diff --git a/src/com/vaadin/terminal/gwt/client/FastStringSet.java b/src/com/vaadin/terminal/gwt/client/FastStringSet.java
new file mode 100644
index 0000000000..05ed8addc8
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/FastStringSet.java
@@ -0,0 +1,60 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArrayString;
+
+public final class FastStringSet extends JavaScriptObject {
+ protected FastStringSet() {
+ // JSO constructor
+ }
+
+ public native boolean contains(String string)
+ /*-{
+ return this.hasOwnProperty(string);
+ }-*/;
+
+ public native void add(String string)
+ /*-{
+ this[string] = true;
+ }-*/;
+
+ public native void addAll(JsArrayString array)
+ /*-{
+ for(var i = 0; i < array.length; i++) {
+ this[array[i]] = true;
+ }
+ }-*/;
+
+ public native JsArrayString dump()
+ /*-{
+ var array = [];
+ for(var string in this) {
+ if (this.hasOwnProperty(string)) {
+ array.push(string);
+ }
+ }
+ return array;
+ }-*/;
+
+ public native void remove(String string)
+ /*-{
+ delete this[string];
+ }-*/;
+
+ public native boolean isEmpty()
+ /*-{
+ for(var string in this) {
+ if (this.hasOwnProperty(string)) {
+ return false;
+ }
+ }
+ return true;
+ }-*/;
+
+ public static FastStringSet create() {
+ return JavaScriptObject.createObject().cast();
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/HistoryImplIEVaadin.java b/src/com/vaadin/terminal/gwt/client/HistoryImplIEVaadin.java
deleted file mode 100644
index 0571959339..0000000000
--- a/src/com/vaadin/terminal/gwt/client/HistoryImplIEVaadin.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-/*
- * Copyright 2008 Google Inc.
- *
- * 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.terminal.gwt.client;
-
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.impl.HistoryImpl;
-
-/**
- * A slightly modified version of GWT's HistoryImplIE6 to bypass bug #2931. Also
- * combined with HistoryImplFrame.
- *
- * This class should be removed if GWT issue 3890 gets resolved. (Also remember
- * to removed deferred binding rule from .gwt.xml file).
- */
-public class HistoryImplIEVaadin extends HistoryImpl {
-
- private static native Element findHistoryFrame()
- /*-{
- return $doc.getElementById('__gwt_historyFrame');
- }-*/;
-
- private static native Element getTokenElement(Element historyFrame)
- /*-{
- // Initialize the history iframe. If '__gwt_historyToken' already exists, then
- // we're probably backing into the app, so _don't_ set the iframe's location.
- if (historyFrame.contentWindow) {
- var doc = historyFrame.contentWindow.document;
- return doc.getElementById('__gwt_historyToken');
- }
- }-*/;
-
- protected Element historyFrame;
-
- @Override
- protected final void nativeUpdate(String historyToken) {
- /*
- * Must update the location hash since it isn't already correct.
- */
- updateHash(historyToken);
- navigateFrame(historyToken);
- }
-
- @Override
- protected final void nativeUpdateOnEvent(String historyToken) {
- updateHash(historyToken);
- }
-
- /**
- * Sanitizes an untrusted string to be used in an HTML context. NOTE: This
- * method of escaping strings should only be used on Internet Explorer.
- *
- * @param maybeHtml
- * untrusted string that may contain html
- * @return sanitized string
- */
- private static String escapeHtml(String maybeHtml) {
- final Element div = DOM.createDiv();
- DOM.setInnerText(div, maybeHtml);
- return DOM.getInnerHTML(div);
- }
-
- /**
- * For IE6, reading from $wnd.location.hash drops part of the fragment if
- * the fragment contains a '?'. To avoid this bug, we use location.href
- * instead.
- */
- private static native String getLocationHash()
- /*-{
- var href = $wnd.location.href;
- var hashLoc = href.lastIndexOf("#");
- return (hashLoc > 0) ? href.substring(hashLoc) : "";
- }-*/;
-
- @Override
- public boolean init() {
- historyFrame = findHistoryFrame();
- if (historyFrame == null) {
- return false;
- }
-
- initHistoryToken();
-
- // Initialize the history iframe. If a token element already exists,
- // then
- // we're probably backing into the app, so _don't_ create a new item.
- Element tokenElement = getTokenElement(historyFrame);
- if (tokenElement != null) {
- setToken(getTokenElementContent(tokenElement));
- } else {
- navigateFrame(getToken());
- }
-
- injectGlobalHandler();
-
- initUrlCheckTimer();
- return true;
- }
-
- protected native String getTokenElementContent(Element tokenElement)
- /*-{
- return tokenElement.innerText;
- }-*/;
-
- protected native void initHistoryToken()
- /*-{
- // Assume an empty token.
- var token = '';
- // Get the initial token from the url's hash component.
- var hash = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::getLocationHash()();
- if (hash.length > 0) {
- try {
- token = this.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
- } catch (e) {
- // Clear the bad hash (this can't have been a valid token).
- $wnd.location.hash = '';
- }
- }
- @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
- }-*/;
-
- protected native void injectGlobalHandler()
- /*-{
- var historyImplRef = this;
-
- $wnd.__gwt_onHistoryLoad = $entry(function(token) {
- historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
- });
- }-*/;
-
- protected native void navigateFrame(String token)
- /*-{
- var escaped = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::escapeHtml(Ljava/lang/String;)(token);
- var doc = this.@com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::historyFrame.contentWindow.document;
- doc.open();
- doc.write('<html><body onload="if(parent.__gwt_onHistoryLoad)parent.__gwt_onHistoryLoad(__gwt_historyToken.innerText)"><div id="__gwt_historyToken">' + escaped + '</div></body></html>');
- doc.close();
- }-*/;
-
- protected native void updateHash(String token)
- /*-{
- $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(token);
- }-*/;
-
- private native void initUrlCheckTimer()
- /*-{
- // This is the URL check timer. It detects when an unexpected change
- // occurs in the document's URL (e.g. when the user enters one manually
- // or selects a 'favorite', but only the #hash part changes). When this
- // occurs, we _must_ reload the page. This is because IE has a really
- // nasty bug that totally mangles its history stack and causes the location
- // bar in the UI to stop working under these circumstances.
- var historyImplRef = this;
- var urlChecker = function() {
- $wnd.setTimeout(urlChecker, 250);
- var hash = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::getLocationHash()();
- if (hash.length > 0) {
- var token = '';
- try {
- token = historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
- } catch (e) {
- // If there's a bad hash, always reload. This could only happen if
- // if someone entered or linked to a bad url.
- $wnd.location.reload();
- }
-
- var historyToken = @com.google.gwt.user.client.impl.HistoryImpl::getToken()();
- if (token != historyToken) {
- $wnd.location.reload();
- }
- }
- };
- urlChecker();
- }-*/;
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
new file mode 100644
index 0000000000..9390c719fc
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
@@ -0,0 +1,1191 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.JsArrayString;
+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.user.client.Timer;
+import com.vaadin.terminal.gwt.client.MeasuredSize.MeasureResult;
+import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener;
+import com.vaadin.terminal.gwt.client.ui.layout.LayoutDependencyTree;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+
+public class LayoutManager {
+ private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
+
+ private static final boolean debugLogging = false;
+
+ private ApplicationConnection connection;
+ private final Set<Element> measuredNonConnectorElements = new HashSet<Element>();
+ private final MeasuredSize nullSize = new MeasuredSize();
+
+ private LayoutDependencyTree currentDependencyTree;
+
+ private final Collection<ManagedLayout> needsHorizontalLayout = new HashSet<ManagedLayout>();
+ private final Collection<ManagedLayout> needsVerticalLayout = new HashSet<ManagedLayout>();
+
+ private final Collection<ComponentConnector> needsMeasure = new HashSet<ComponentConnector>();
+
+ private Collection<ComponentConnector> pendingOverflowFixes = new HashSet<ComponentConnector>();
+
+ private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<Element, Collection<ElementResizeListener>>();
+ private final Set<Element> listenersToFire = new HashSet<Element>();
+
+ private boolean layoutPending = false;
+ private Timer layoutTimer = new Timer() {
+ @Override
+ public void run() {
+ cancel();
+ layoutNow();
+ }
+ };
+ private boolean everythingNeedsMeasure = false;
+
+ public void setConnection(ApplicationConnection connection) {
+ if (this.connection != null) {
+ throw new RuntimeException(
+ "LayoutManager connection can never be changed");
+ }
+ this.connection = connection;
+ }
+
+ /**
+ * Gets the layout manager associated with the given
+ * {@link ApplicationConnection}.
+ *
+ * @param connection
+ * the application connection to get a layout manager for
+ * @return the layout manager associated with the provided application
+ * connection
+ */
+ public static LayoutManager get(ApplicationConnection connection) {
+ return connection.getLayoutManager();
+ }
+
+ /**
+ * Registers that a ManagedLayout is depending on the size of an Element.
+ * This causes this layout manager to measure the element in the beginning
+ * of every layout phase and call the appropriate layout method of the
+ * managed layout if the size of the element has changed.
+ *
+ * @param owner
+ * the ManagedLayout that depends on an element
+ * @param element
+ * the Element that should be measured
+ */
+ public void registerDependency(ManagedLayout owner, Element element) {
+ MeasuredSize measuredSize = ensureMeasured(element);
+ setNeedsLayout(owner);
+ measuredSize.addDependent(owner.getConnectorId());
+ }
+
+ private MeasuredSize ensureMeasured(Element element) {
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ measuredSize = new MeasuredSize();
+
+ if (ConnectorMap.get(connection).getConnector(element) == null) {
+ measuredNonConnectorElements.add(element);
+ }
+ setMeasuredSize(element, measuredSize);
+ }
+ return measuredSize;
+ }
+
+ private boolean needsMeasure(Element e) {
+ if (connection.getConnectorMap().getConnectorId(e) != null) {
+ return true;
+ } else if (elementResizeListeners.containsKey(e)) {
+ return true;
+ } else if (getMeasuredSize(e, nullSize).hasDependents()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Assigns a measured size to an element. Method defined as protected to
+ * allow separate implementation for IE8 in which delete not always works.
+ *
+ * @param element
+ * the dom element to attach the measured size to
+ * @param measuredSize
+ * the measured size to attach to the element. If
+ * <code>null</code>, any previous measured size is removed.
+ */
+ protected native void setMeasuredSize(Element element,
+ MeasuredSize measuredSize)
+ /*-{
+ if (measuredSize) {
+ element.vMeasuredSize = measuredSize;
+ } else {
+ delete element.vMeasuredSize;
+ }
+ }-*/;
+
+ private static native final MeasuredSize getMeasuredSize(Element element,
+ MeasuredSize defaultSize)
+ /*-{
+ return element.vMeasuredSize || defaultSize;
+ }-*/;
+
+ private final MeasuredSize getMeasuredSize(ComponentConnector connector) {
+ Element element = connector.getWidget().getElement();
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ measuredSize = new MeasuredSize();
+ setMeasuredSize(element, measuredSize);
+ }
+ return measuredSize;
+ }
+
+ /**
+ * Registers that a ManagedLayout is no longer depending on the size of an
+ * Element.
+ *
+ * @see #registerDependency(ManagedLayout, Element)
+ *
+ * @param owner
+ * the ManagedLayout no longer depends on an element
+ * @param element
+ * the Element that that no longer needs to be measured
+ */
+ public void unregisterDependency(ManagedLayout owner, Element element) {
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ return;
+ }
+ measuredSize.removeDependent(owner.getConnectorId());
+ stopMeasuringIfUnecessary(element);
+ }
+
+ public boolean isLayoutRunning() {
+ return currentDependencyTree != null;
+ }
+
+ private void countLayout(Map<ManagedLayout, Integer> layoutCounts,
+ ManagedLayout layout) {
+ Integer count = layoutCounts.get(layout);
+ if (count == null) {
+ count = Integer.valueOf(0);
+ } else {
+ count = Integer.valueOf(count.intValue() + 1);
+ }
+ layoutCounts.put(layout, count);
+ if (count.intValue() > 2) {
+ VConsole.error(Util.getConnectorString(layout)
+ + " has been layouted " + count.intValue() + " times");
+ }
+ }
+
+ private void layoutLater() {
+ if (!layoutPending) {
+ layoutPending = true;
+ layoutTimer.schedule(100);
+ }
+ }
+
+ public void layoutNow() {
+ if (isLayoutRunning()) {
+ throw new IllegalStateException(
+ "Can't start a new layout phase before the previous layout phase ends.");
+ }
+ layoutPending = false;
+ try {
+ currentDependencyTree = new LayoutDependencyTree();
+ doLayout();
+ } finally {
+ currentDependencyTree = null;
+ }
+ }
+
+ private void doLayout() {
+ VConsole.log("Starting layout phase");
+
+ Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>();
+
+ int passes = 0;
+ Duration totalDuration = new Duration();
+
+ for (ManagedLayout layout : needsHorizontalLayout) {
+ currentDependencyTree.setNeedsHorizontalLayout(layout, true);
+ }
+ for (ManagedLayout layout : needsVerticalLayout) {
+ currentDependencyTree.setNeedsVerticalLayout(layout, true);
+ }
+ needsHorizontalLayout.clear();
+ needsVerticalLayout.clear();
+
+ for (ComponentConnector connector : needsMeasure) {
+ currentDependencyTree.setNeedsMeasure(connector, true);
+ }
+ needsMeasure.clear();
+
+ measureNonConnectors();
+
+ VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms");
+
+ while (true) {
+ Duration passDuration = new Duration();
+ passes++;
+
+ int measuredConnectorCount = measureConnectors(
+ currentDependencyTree, everythingNeedsMeasure);
+ everythingNeedsMeasure = false;
+ if (measuredConnectorCount == 0) {
+ VConsole.log("No more changes in pass " + passes);
+ break;
+ }
+
+ int measureTime = passDuration.elapsedMillis();
+ VConsole.log(" Measured " + measuredConnectorCount
+ + " elements in " + measureTime + " ms");
+
+ if (!listenersToFire.isEmpty()) {
+ for (Element element : listenersToFire) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ ElementResizeListener[] array = listeners
+ .toArray(new ElementResizeListener[listeners.size()]);
+ ElementResizeEvent event = new ElementResizeEvent(this,
+ element);
+ for (ElementResizeListener listener : array) {
+ try {
+ listener.onElementResize(event);
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ }
+ }
+ int measureListenerTime = passDuration.elapsedMillis();
+ VConsole.log(" Fired resize listeners for "
+ + listenersToFire.size() + " elements in "
+ + (measureListenerTime - measureTime) + " ms");
+ measureTime = measuredConnectorCount;
+ listenersToFire.clear();
+ }
+
+ FastStringSet updatedSet = FastStringSet.create();
+
+ while (currentDependencyTree.hasHorizontalConnectorToLayout()
+ || currentDependencyTree.hasVerticaConnectorToLayout()) {
+ for (ManagedLayout layout : currentDependencyTree
+ .getHorizontalLayoutTargets()) {
+ if (layout instanceof DirectionalManagedLayout) {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+ try {
+ cl.layoutHorizontally();
+ } catch (RuntimeException e) {
+ VConsole.log(e);
+ }
+ countLayout(layoutCounts, cl);
+ } else {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+ try {
+ rr.layout();
+ } catch (RuntimeException e) {
+ VConsole.log(e);
+ }
+ countLayout(layoutCounts, rr);
+ }
+ if (debugLogging) {
+ updatedSet.add(layout.getConnectorId());
+ }
+ }
+
+ for (ManagedLayout layout : currentDependencyTree
+ .getVerticalLayoutTargets()) {
+ if (layout instanceof DirectionalManagedLayout) {
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+ try {
+ cl.layoutVertically();
+ } catch (RuntimeException e) {
+ VConsole.log(e);
+ }
+ countLayout(layoutCounts, cl);
+ } else {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+ try {
+ rr.layout();
+ } catch (RuntimeException e) {
+ VConsole.log(e);
+ }
+ countLayout(layoutCounts, rr);
+ }
+ if (debugLogging) {
+ updatedSet.add(layout.getConnectorId());
+ }
+ }
+ }
+
+ if (debugLogging) {
+ JsArrayString changedCids = updatedSet.dump();
+
+ StringBuilder b = new StringBuilder(" ");
+ b.append(changedCids.length());
+ b.append(" requestLayout invocations in ");
+ b.append(passDuration.elapsedMillis() - measureTime);
+ b.append(" ms");
+ if (changedCids.length() < 30) {
+ for (int i = 0; i < changedCids.length(); i++) {
+ if (i != 0) {
+ b.append(", ");
+ } else {
+ b.append(": ");
+ }
+ String connectorString = changedCids.get(i);
+ if (changedCids.length() < 10) {
+ ServerConnector connector = ConnectorMap.get(
+ connection).getConnector(connectorString);
+ connectorString = Util
+ .getConnectorString(connector);
+ }
+ b.append(connectorString);
+ }
+ }
+ VConsole.log(b.toString());
+ }
+
+ VConsole.log("Pass " + passes + " completed in "
+ + passDuration.elapsedMillis() + " ms");
+
+ if (passes > 100) {
+ VConsole.log(LOOP_ABORT_MESSAGE);
+ VNotification.createNotification(VNotification.DELAY_FOREVER)
+ .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
+ "error");
+ break;
+ }
+ }
+
+ int postLayoutStart = totalDuration.elapsedMillis();
+ for (ComponentConnector connector : connection.getConnectorMap()
+ .getComponentConnectors()) {
+ if (connector instanceof PostLayoutListener) {
+ ((PostLayoutListener) connector).postLayout();
+ }
+ }
+ VConsole.log("Invoke post layout listeners in "
+ + (totalDuration.elapsedMillis() - postLayoutStart) + " ms");
+
+ VConsole.log("Total layout phase time: "
+ + totalDuration.elapsedMillis() + "ms");
+ }
+
+ private void logConnectorStatus(int connectorId) {
+ currentDependencyTree
+ .logDependencyStatus((ComponentConnector) ConnectorMap.get(
+ connection).getConnector(Integer.toString(connectorId)));
+ }
+
+ private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
+ boolean measureAll) {
+ if (!pendingOverflowFixes.isEmpty()) {
+ Duration duration = new Duration();
+
+ HashMap<Element, String> originalOverflows = new HashMap<Element, String>();
+
+ HashSet<ComponentConnector> delayedOverflowFixes = new HashSet<ComponentConnector>();
+
+ // First set overflow to hidden (and save previous value so it can
+ // be restored later)
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ // Delay the overflow fix if the involved connectors might still
+ // change
+ if (!currentDependencyTree
+ .noMoreChangesExpected(componentConnector)
+ || !currentDependencyTree
+ .noMoreChangesExpected(componentConnector
+ .getParent())) {
+ delayedOverflowFixes.add(componentConnector);
+ continue;
+ }
+
+ if (debugLogging) {
+ VConsole.log("Doing overflow fix for "
+ + Util.getConnectorString(componentConnector)
+ + " in "
+ + Util.getConnectorString(componentConnector
+ .getParent()));
+ }
+
+ Element parentElement = componentConnector.getWidget()
+ .getElement().getParentElement();
+ Style style = parentElement.getStyle();
+ String originalOverflow = style.getOverflow();
+
+ if (originalOverflow != null
+ && !originalOverflows.containsKey(parentElement)) {
+ // Store original value for restore, but only the first time
+ // the value is changed
+ originalOverflows.put(parentElement, originalOverflow);
+ }
+
+ style.setOverflow(Overflow.HIDDEN);
+ }
+
+ pendingOverflowFixes.removeAll(delayedOverflowFixes);
+
+ // Then ensure all scrolling elements are reflowed by measuring
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ componentConnector.getWidget().getElement().getParentElement()
+ .getOffsetHeight();
+ }
+
+ // Finally restore old overflow value and update bookkeeping
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ Element parentElement = componentConnector.getWidget()
+ .getElement().getParentElement();
+ parentElement.getStyle().setProperty("overflow",
+ originalOverflows.get(parentElement));
+
+ layoutDependencyTree.setNeedsMeasure(componentConnector, true);
+ }
+ if (!pendingOverflowFixes.isEmpty()) {
+ VConsole.log("Did overflow fix for "
+ + pendingOverflowFixes.size() + " elements in "
+ + duration.elapsedMillis() + " ms");
+ }
+ pendingOverflowFixes = delayedOverflowFixes;
+ }
+
+ int measureCount = 0;
+ if (measureAll) {
+ ComponentConnector[] connectors = ConnectorMap.get(connection)
+ .getComponentConnectors();
+ for (ComponentConnector connector : connectors) {
+ measueConnector(connector);
+ }
+ for (ComponentConnector connector : connectors) {
+ layoutDependencyTree.setNeedsMeasure(connector, false);
+ }
+ measureCount += connectors.length;
+ }
+
+ while (layoutDependencyTree.hasConnectorsToMeasure()) {
+ Collection<ComponentConnector> measureTargets = layoutDependencyTree
+ .getMeasureTargets();
+ for (ComponentConnector connector : measureTargets) {
+ measueConnector(connector);
+ measureCount++;
+ }
+ for (ComponentConnector connector : measureTargets) {
+ layoutDependencyTree.setNeedsMeasure(connector, false);
+ }
+ }
+ return measureCount;
+ }
+
+ private void measueConnector(ComponentConnector connector) {
+ Element element = connector.getWidget().getElement();
+ MeasuredSize measuredSize = getMeasuredSize(connector);
+ MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
+
+ if (measureResult.isChanged()) {
+ onConnectorChange(connector, measureResult.isWidthChanged(),
+ measureResult.isHeightChanged());
+ }
+ }
+
+ private void onConnectorChange(ComponentConnector connector,
+ boolean widthChanged, boolean heightChanged) {
+ setNeedsOverflowFix(connector);
+ if (heightChanged) {
+ currentDependencyTree.markHeightAsChanged(connector);
+ }
+ if (widthChanged) {
+ currentDependencyTree.markWidthAsChanged(connector);
+ }
+ }
+
+ private void setNeedsOverflowFix(ComponentConnector connector) {
+ // IE9 doesn't need the original fix, but for some reason it needs this
+ if (BrowserInfo.get().requiresOverflowAutoFix()
+ || BrowserInfo.get().isIE9()) {
+ ComponentConnector scrollingBoundary = currentDependencyTree
+ .getScrollingBoundary(connector);
+ if (scrollingBoundary != null) {
+ pendingOverflowFixes.add(scrollingBoundary);
+ }
+ }
+ }
+
+ private void measureNonConnectors() {
+ for (Element element : measuredNonConnectorElements) {
+ measuredAndUpdate(element, getMeasuredSize(element, null));
+ }
+ VConsole.log("Measured " + measuredNonConnectorElements.size()
+ + " non connector elements");
+ }
+
+ private MeasureResult measuredAndUpdate(Element element,
+ MeasuredSize measuredSize) {
+ MeasureResult measureResult = measuredSize.measure(element);
+ if (measureResult.isChanged()) {
+ notifyListenersAndDepdendents(element,
+ measureResult.isWidthChanged(),
+ measureResult.isHeightChanged());
+ }
+ return measureResult;
+ }
+
+ private void notifyListenersAndDepdendents(Element element,
+ boolean widthChanged, boolean heightChanged) {
+ assert widthChanged || heightChanged;
+
+ MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
+ JsArrayString dependents = measuredSize.getDependents();
+ for (int i = 0; i < dependents.length(); i++) {
+ String pid = dependents.get(i);
+ ManagedLayout dependent = (ManagedLayout) connection
+ .getConnectorMap().getConnector(pid);
+ if (dependent != null) {
+ if (heightChanged) {
+ currentDependencyTree.setNeedsVerticalLayout(dependent,
+ true);
+ }
+ if (widthChanged) {
+ currentDependencyTree.setNeedsHorizontalLayout(dependent,
+ true);
+ }
+ }
+ }
+ if (elementResizeListeners.containsKey(element)) {
+ listenersToFire.add(element);
+ }
+ }
+
+ private static boolean isManagedLayout(ComponentConnector connector) {
+ return connector instanceof ManagedLayout;
+ }
+
+ public void forceLayout() {
+ ConnectorMap connectorMap = connection.getConnectorMap();
+ ComponentConnector[] componentConnectors = connectorMap
+ .getComponentConnectors();
+ for (ComponentConnector connector : componentConnectors) {
+ if (connector instanceof ManagedLayout) {
+ setNeedsLayout((ManagedLayout) connector);
+ }
+ }
+ setEverythingNeedsMeasure();
+ layoutNow();
+ }
+
+ /**
+ * 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.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsLayout(ManagedLayout layout) {
+ setNeedsHorizontalLayout(layout);
+ setNeedsVerticalLayout(layout);
+ }
+
+ /**
+ * 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.
+ *
+ * For SimpleManagedLayout which is always layouted in both directions, this
+ * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsHorizontalLayout(ManagedLayout layout) {
+ needsHorizontalLayout.add(layout);
+ }
+
+ /**
+ * 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.
+ *
+ * For SimpleManagedLayout which is always layouted in both directions, this
+ * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsVerticalLayout(ManagedLayout layout) {
+ needsVerticalLayout.add(layout);
+ }
+
+ /**
+ * Gets the outer height (including margins, paddings and borders) 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>
+ *
+ * -1 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 outer height (including margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getOuterHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getOuterHeight();
+ }
+
+ /**
+ * Gets the outer width (including margins, paddings and borders) 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>
+ *
+ * -1 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 outer width (including margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getOuterWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getOuterWidth();
+ }
+
+ /**
+ * Gets the inner height (excluding margins, paddings and borders) 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>
+ *
+ * -1 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 inner height (excluding margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getInnerHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getInnerHeight();
+ }
+
+ /**
+ * Gets the inner width (excluding margins, paddings and borders) 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>
+ *
+ * -1 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 inner width (excluding margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getInnerWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getInnerWidth();
+ }
+
+ /**
+ * Gets the border height (top border + 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 border height (top border + bottom border) of the
+ * element in pixels.
+ */
+ public final int getBorderHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getBorderHeight();
+ }
+
+ /**
+ * Gets the padding height (top padding + bottom padding) 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 padding height (top padding + bottom padding) of the
+ * element in pixels.
+ */
+ public int getPaddingHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingHeight();
+ }
+
+ /**
+ * Gets the border width (left border + 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 border width (left border + right border) of the
+ * element in pixels.
+ */
+ public int getBorderWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getBorderWidth();
+ }
+
+ /**
+ * 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:
+ * <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 padding width (left padding + right padding) of the
+ * element in pixels.
+ */
+ public int getPaddingWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingWidth();
+ }
+
+ /**
+ * Gets the top padding 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 padding of the element in pixels.
+ */
+ public int getPaddingTop(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingTop();
+ }
+
+ /**
+ * Gets the left padding 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 padding of the element in pixels.
+ */
+ public int getPaddingLeft(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingLeft();
+ }
+
+ /**
+ * Gets the bottom padding 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 padding of the element in pixels.
+ */
+ public int getPaddingBottom(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingBottom();
+ }
+
+ /**
+ * Gets the right padding 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 padding of the element in pixels.
+ */
+ public int getPaddingRight(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingRight();
+ }
+
+ /**
+ * Gets the top margin 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 margin of the element in pixels.
+ */
+ public int getMarginTop(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginTop();
+ }
+
+ /**
+ * Gets the right margin 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 margin of the element in pixels.
+ */
+ public int getMarginRight(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginRight();
+ }
+
+ /**
+ * Gets the bottom margin 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 margin of the element in pixels.
+ */
+ public int getMarginBottom(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginBottom();
+ }
+
+ /**
+ * Gets the left margin 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 margin of the element in pixels.
+ */
+ public int getMarginLeft(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginLeft();
+ }
+
+ /**
+ * Registers the outer height (including margins, borders and paddings) of a
+ * component. This can be used as an optimization by ManagedLayouts; by
+ * informing the LayoutManager about what size a component will have, the
+ * layout propagation can continue directly without first measuring the
+ * potentially resized elements.
+ *
+ * @param component
+ * the component for which the size is reported
+ * @param outerHeight
+ * the new outer height (including margins, borders and paddings)
+ * of the component in pixels
+ */
+ public void reportOuterHeight(ComponentConnector component, int outerHeight) {
+ MeasuredSize measuredSize = getMeasuredSize(component);
+ if (isLayoutRunning()) {
+ boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
+
+ if (heightChanged) {
+ onConnectorChange(component, false, true);
+ notifyListenersAndDepdendents(component.getWidget()
+ .getElement(), false, true);
+ }
+ currentDependencyTree.setNeedsVerticalMeasure(component, false);
+ } else if (measuredSize.getOuterHeight() != outerHeight) {
+ setNeedsMeasure(component);
+ }
+ }
+
+ /**
+ * Registers the height reserved for a relatively sized component. This can
+ * be used as an optimization by ManagedLayouts; by informing the
+ * LayoutManager about what size a component will have, the layout
+ * propagation can continue directly without first measuring the potentially
+ * resized elements.
+ *
+ * @param component
+ * the relatively sized component for which the size is reported
+ * @param assignedHeight
+ * the inner height of the relatively sized component's parent
+ * element in pixels
+ */
+ public void reportHeightAssignedToRelative(ComponentConnector component,
+ int assignedHeight) {
+ assert component.isRelativeHeight();
+
+ float percentSize = parsePercent(component.getState().getHeight());
+ int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
+
+ reportOuterHeight(component, effectiveHeight);
+ }
+
+ /**
+ * Registers the width reserved for a relatively sized component. This can
+ * be used as an optimization by ManagedLayouts; by informing the
+ * LayoutManager about what size a component will have, the layout
+ * propagation can continue directly without first measuring the potentially
+ * resized elements.
+ *
+ * @param component
+ * the relatively sized component for which the size is reported
+ * @param assignedWidth
+ * the inner width of the relatively sized component's parent
+ * element in pixels
+ */
+ public void reportWidthAssignedToRelative(ComponentConnector component,
+ int assignedWidth) {
+ assert component.isRelativeWidth();
+
+ float percentSize = parsePercent(component.getState().getWidth());
+ int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
+
+ reportOuterWidth(component, effectiveWidth);
+ }
+
+ private static float parsePercent(String size) {
+ return Float.parseFloat(size.substring(0, size.length() - 1));
+ }
+
+ /**
+ * Registers the outer width (including margins, borders and paddings) of a
+ * component. This can be used as an optimization by ManagedLayouts; by
+ * informing the LayoutManager about what size a component will have, the
+ * layout propagation can continue directly without first measuring the
+ * potentially resized elements.
+ *
+ * @param component
+ * the component for which the size is reported
+ * @param outerWidth
+ * the new outer width (including margins, borders and paddings)
+ * of the component in pixels
+ */
+ public void reportOuterWidth(ComponentConnector component, int outerWidth) {
+ MeasuredSize measuredSize = getMeasuredSize(component);
+ if (isLayoutRunning()) {
+ boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
+
+ if (widthChanged) {
+ onConnectorChange(component, true, false);
+ notifyListenersAndDepdendents(component.getWidget()
+ .getElement(), true, false);
+ }
+ currentDependencyTree.setNeedsHorizontalMeasure(component, false);
+ } else if (measuredSize.getOuterWidth() != outerWidth) {
+ setNeedsMeasure(component);
+ }
+ }
+
+ /**
+ * Adds a listener that will be notified whenever the size of a specific
+ * element changes. Adding a listener to an element also ensures that all
+ * sizes for that element will be available starting from the next layout
+ * phase.
+ *
+ * @param element
+ * the element that should be checked for size changes
+ * @param listener
+ * an ElementResizeListener that will be informed whenever the
+ * size of the target element has changed
+ */
+ public void addElementResizeListener(Element element,
+ ElementResizeListener listener) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ if (listeners == null) {
+ listeners = new HashSet<ElementResizeListener>();
+ elementResizeListeners.put(element, listeners);
+ ensureMeasured(element);
+ }
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes an element resize listener from the provided element. This might
+ * cause this LayoutManager to stop tracking the size of the element if no
+ * other sources are interested in the size.
+ *
+ * @param element
+ * the element to which the element resize listener was
+ * previously added
+ * @param listener
+ * the ElementResizeListener that should no longer get informed
+ * about size changes to the target element.
+ */
+ public void removeElementResizeListener(Element element,
+ ElementResizeListener listener) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ if (listeners != null) {
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ elementResizeListeners.remove(element);
+ stopMeasuringIfUnecessary(element);
+ }
+ }
+ }
+
+ private void stopMeasuringIfUnecessary(Element element) {
+ if (!needsMeasure(element)) {
+ measuredNonConnectorElements.remove(element);
+ setMeasuredSize(element, null);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param component
+ * the component whose size might have changed.
+ */
+ public void setNeedsMeasure(ComponentConnector component) {
+ if (isLayoutRunning()) {
+ currentDependencyTree.setNeedsMeasure(component, true);
+ } else {
+ needsMeasure.add(component);
+ layoutLater();
+ }
+ }
+
+ public void setEverythingNeedsMeasure() {
+ everythingNeedsMeasure = true;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java b/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
new file mode 100644
index 0000000000..2b677985b5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManagerIE8.java
@@ -0,0 +1,23 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.dom.client.Element;
+
+public class LayoutManagerIE8 extends LayoutManager {
+
+ @Override
+ protected native void setMeasuredSize(Element element,
+ MeasuredSize measuredSize)
+ // IE8 cannot do delete element.vMeasuredSize, at least in the case when
+ // element is not attached to the document (e.g. when a caption is removed)
+ /*-{
+ if (measuredSize) {
+ element.vMeasuredSize = measuredSize;
+ } else {
+ element.vMeasuredSize = undefined;
+ }
+ }-*/;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/MeasuredSize.java b/src/com/vaadin/terminal/gwt/client/MeasuredSize.java
new file mode 100644
index 0000000000..97822fa8ec
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/MeasuredSize.java
@@ -0,0 +1,228 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.dom.client.Element;
+
+public class MeasuredSize {
+ public static class MeasureResult {
+ private final boolean widthChanged;
+ private final boolean heightChanged;
+
+ private MeasureResult(boolean widthChanged, boolean heightChanged) {
+ this.widthChanged = widthChanged;
+ this.heightChanged = heightChanged;
+ }
+
+ public boolean isHeightChanged() {
+ return heightChanged;
+ }
+
+ public boolean isWidthChanged() {
+ return widthChanged;
+ }
+
+ public boolean isChanged() {
+ return heightChanged || widthChanged;
+ }
+ }
+
+ private int width = -1;
+ private int height = -1;
+
+ private int[] paddings = new int[4];
+ private int[] borders = new int[4];
+ private int[] margins = new int[4];
+
+ private FastStringSet dependents = FastStringSet.create();
+
+ public int getOuterHeight() {
+ return height;
+ }
+
+ public int getOuterWidth() {
+ return width;
+ }
+
+ public void addDependent(String pid) {
+ dependents.add(pid);
+ }
+
+ public void removeDependent(String pid) {
+ dependents.remove(pid);
+ }
+
+ public boolean hasDependents() {
+ return !dependents.isEmpty();
+ }
+
+ public JsArrayString getDependents() {
+ return dependents.dump();
+ }
+
+ private static int sumWidths(int[] sizes) {
+ return sizes[1] + sizes[3];
+ }
+
+ private static int sumHeights(int[] sizes) {
+ return sizes[0] + sizes[2];
+ }
+
+ public int getInnerHeight() {
+ return height - sumHeights(margins) - sumHeights(borders)
+ - sumHeights(paddings);
+ }
+
+ public int getInnerWidth() {
+ return width - sumWidths(margins) - sumWidths(borders)
+ - sumWidths(paddings);
+ }
+
+ public boolean setOuterHeight(int height) {
+ if (this.height != height) {
+ this.height = height;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean setOuterWidth(int width) {
+ if (this.width != width) {
+ this.width = width;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public int getBorderHeight() {
+ return sumHeights(borders);
+ }
+
+ public int getBorderWidth() {
+ return sumWidths(borders);
+ }
+
+ public int getPaddingHeight() {
+ return sumHeights(paddings);
+ }
+
+ public int getPaddingWidth() {
+ return sumWidths(paddings);
+ }
+
+ public int getMarginHeight() {
+ return sumHeights(margins);
+ }
+
+ public int getMarginWidth() {
+ return sumWidths(margins);
+ }
+
+ public int getMarginTop() {
+ return margins[0];
+ }
+
+ public int getMarginRight() {
+ return margins[1];
+ }
+
+ public int getMarginBottom() {
+ return margins[2];
+ }
+
+ public int getMarginLeft() {
+ return margins[3];
+ }
+
+ public int getBorderTop() {
+ return margins[0];
+ }
+
+ public int getBorderRight() {
+ return margins[1];
+ }
+
+ public int getBorderBottom() {
+ return margins[2];
+ }
+
+ public int getBorderLeft() {
+ return margins[3];
+ }
+
+ public int getPaddingTop() {
+ return paddings[0];
+ }
+
+ public int getPaddingRight() {
+ return paddings[1];
+ }
+
+ public int getPaddingBottom() {
+ return paddings[2];
+ }
+
+ public int getPaddingLeft() {
+ return paddings[3];
+ }
+
+ public MeasureResult measure(Element element) {
+ boolean heightChanged = false;
+ boolean widthChanged = false;
+
+ ComputedStyle computedStyle = new ComputedStyle(element);
+ int[] paddings = computedStyle.getPadding();
+ if (!heightChanged && hasHeightChanged(this.paddings, paddings)) {
+ heightChanged = true;
+ }
+ if (!widthChanged && hasWidthChanged(this.paddings, paddings)) {
+ widthChanged = true;
+ }
+ this.paddings = paddings;
+
+ int[] margins = computedStyle.getMargin();
+ if (!heightChanged && hasHeightChanged(this.margins, margins)) {
+ heightChanged = true;
+ }
+ if (!widthChanged && hasWidthChanged(this.margins, margins)) {
+ widthChanged = true;
+ }
+ this.margins = margins;
+
+ int[] borders = computedStyle.getBorder();
+ if (!heightChanged && hasHeightChanged(this.borders, borders)) {
+ heightChanged = true;
+ }
+ if (!widthChanged && hasWidthChanged(this.borders, borders)) {
+ widthChanged = true;
+ }
+ this.borders = borders;
+
+ int requiredHeight = Util.getRequiredHeight(element);
+ int marginHeight = sumHeights(margins);
+ if (setOuterHeight(requiredHeight + marginHeight)) {
+ heightChanged = true;
+ }
+
+ int requiredWidth = Util.getRequiredWidth(element);
+ int marginWidth = sumWidths(margins);
+ if (setOuterWidth(requiredWidth + marginWidth)) {
+ widthChanged = true;
+ }
+
+ return new MeasureResult(widthChanged, heightChanged);
+ }
+
+ private static boolean hasWidthChanged(int[] sizes1, int[] sizes2) {
+ return sizes1[1] != sizes2[1] || sizes1[3] != sizes2[3];
+ }
+
+ private static boolean hasHeightChanged(int[] sizes1, int[] sizes2) {
+ return sizes1[0] != sizes2[0] || sizes1[2] != sizes2[2];
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/MouseEventDetails.java b/src/com/vaadin/terminal/gwt/client/MouseEventDetails.java
index 260dfa6fff..f5ff707eed 100644
--- a/src/com/vaadin/terminal/gwt/client/MouseEventDetails.java
+++ b/src/com/vaadin/terminal/gwt/client/MouseEventDetails.java
@@ -5,19 +5,18 @@ package com.vaadin.terminal.gwt.client;
import java.io.Serializable;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.Event;
-
/**
* Helper class to store and transfer mouse event details.
*/
public class MouseEventDetails implements Serializable {
- public static final int BUTTON_LEFT = Event.BUTTON_LEFT;
- public static final int BUTTON_MIDDLE = Event.BUTTON_MIDDLE;
- public static final int BUTTON_RIGHT = Event.BUTTON_RIGHT;
+ // From com.google.gwt.dom.client.NativeEvent
+ public static final int BUTTON_LEFT = 1;
+ public static final int BUTTON_MIDDLE = 4;
+ public static final int BUTTON_RIGHT = 2;
private static final char DELIM = ',';
+ // From com.google.gwt.user.client.Event
+ private static final int ONDBLCLICK = 0x00002;
private int button;
private int clientX;
@@ -66,26 +65,47 @@ public class MouseEventDetails implements Serializable {
return relativeY;
}
- public MouseEventDetails(NativeEvent evt) {
- this(evt, null);
+ public void setButton(int button) {
+ this.button = button;
}
- public MouseEventDetails(NativeEvent evt, Element relativeToElement) {
- type = Event.getTypeInt(evt.getType());
- clientX = Util.getTouchOrMouseClientX(evt);
- clientY = Util.getTouchOrMouseClientY(evt);
- button = evt.getButton();
- altKey = evt.getAltKey();
- ctrlKey = evt.getCtrlKey();
- metaKey = evt.getMetaKey();
- shiftKey = evt.getShiftKey();
- if (relativeToElement != null) {
- relativeX = getRelativeX(clientX, relativeToElement);
- relativeY = getRelativeY(clientY, relativeToElement);
- }
+ public void setClientX(int clientX) {
+ this.clientX = clientX;
+ }
+
+ public void setClientY(int clientY) {
+ this.clientY = clientY;
+ }
+
+ public void setAltKey(boolean altKey) {
+ this.altKey = altKey;
+ }
+
+ public void setCtrlKey(boolean ctrlKey) {
+ this.ctrlKey = ctrlKey;
+ }
+
+ public void setMetaKey(boolean metaKey) {
+ this.metaKey = metaKey;
+ }
+
+ public void setShiftKey(boolean shiftKey) {
+ this.shiftKey = shiftKey;
+ }
+
+ public void setType(int type) {
+ this.type = type;
}
- private MouseEventDetails() {
+ public void setRelativeX(int relativeX) {
+ this.relativeX = relativeX;
+ }
+
+ public void setRelativeY(int relativeY) {
+ this.relativeY = relativeY;
+ }
+
+ public MouseEventDetails() {
}
@Override
@@ -128,22 +148,12 @@ public class MouseEventDetails implements Serializable {
return "";
}
- public Class<MouseEventDetails> getType() {
- return MouseEventDetails.class;
+ public int getType() {
+ return type;
}
public boolean isDoubleClick() {
- return type == Event.ONDBLCLICK;
- }
-
- private static int getRelativeX(int clientX, Element target) {
- return clientX - target.getAbsoluteLeft() + target.getScrollLeft()
- + target.getOwnerDocument().getScrollLeft();
- }
-
- private static int getRelativeY(int clientY, Element target) {
- return clientY - target.getAbsoluteTop() + target.getScrollTop()
- + target.getOwnerDocument().getScrollTop();
+ return type == ONDBLCLICK;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/MouseEventDetailsBuilder.java b/src/com/vaadin/terminal/gwt/client/MouseEventDetailsBuilder.java
new file mode 100644
index 0000000000..58dd488351
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/MouseEventDetailsBuilder.java
@@ -0,0 +1,74 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.user.client.Event;
+
+/**
+ * Helper class for constructing a MouseEventDetails object from a
+ * {@link NativeEvent}.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public class MouseEventDetailsBuilder {
+
+ /**
+ * Construct a {@link MouseEventDetails} object from the given event
+ *
+ * @param evt
+ * The event to use as a source for the details
+ * @return a MouseEventDetails containing information from the event
+ */
+ public static MouseEventDetails buildMouseEventDetails(NativeEvent evt) {
+ return buildMouseEventDetails(evt, null);
+ }
+
+ /**
+ * Construct a {@link MouseEventDetails} object from the given event
+ *
+ * @param evt
+ * The event to use as a source for the details
+ * @param relativeToElement
+ * The element whose position
+ * {@link MouseEventDetails#getRelativeX()} and
+ * {@link MouseEventDetails#getRelativeY()} are relative to.
+ * @return a MouseEventDetails containing information from the event
+ */
+ public static MouseEventDetails buildMouseEventDetails(NativeEvent evt,
+ Element relativeToElement) {
+ MouseEventDetails mouseEventDetails = new MouseEventDetails();
+ mouseEventDetails.setType(Event.getTypeInt(evt.getType()));
+ mouseEventDetails.setClientX(Util.getTouchOrMouseClientX(evt));
+ mouseEventDetails.setClientY(Util.getTouchOrMouseClientY(evt));
+ mouseEventDetails.setButton(evt.getButton());
+ mouseEventDetails.setAltKey(evt.getAltKey());
+ mouseEventDetails.setCtrlKey(evt.getCtrlKey());
+ mouseEventDetails.setMetaKey(evt.getMetaKey());
+ mouseEventDetails.setShiftKey(evt.getShiftKey());
+ if (relativeToElement != null) {
+ mouseEventDetails.setRelativeX(getRelativeX(
+ mouseEventDetails.getClientX(), relativeToElement));
+ mouseEventDetails.setRelativeY(getRelativeY(
+ mouseEventDetails.getClientY(), relativeToElement));
+ }
+ return mouseEventDetails;
+
+ }
+
+ private static int getRelativeX(int clientX, Element target) {
+ return clientX - target.getAbsoluteLeft() + target.getScrollLeft()
+ + target.getOwnerDocument().getScrollLeft();
+ }
+
+ private static int getRelativeY(int clientY, Element target) {
+ return clientY - target.getAbsoluteTop() + target.getScrollTop()
+ + target.getOwnerDocument().getScrollTop();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/NullConsole.java b/src/com/vaadin/terminal/gwt/client/NullConsole.java
index 12df4b323b..2d15ffd46c 100644
--- a/src/com/vaadin/terminal/gwt/client/NullConsole.java
+++ b/src/com/vaadin/terminal/gwt/client/NullConsole.java
@@ -32,8 +32,8 @@ public class NullConsole implements Console {
public void printLayoutProblems(ValueMap meta,
ApplicationConnection applicationConnection,
- Set<Paintable> zeroHeightComponents,
- Set<Paintable> zeroWidthComponents) {
+ Set<ComponentConnector> zeroHeightComponents,
+ Set<ComponentConnector> zeroWidthComponents) {
}
public void log(Throwable e) {
@@ -41,7 +41,8 @@ public class NullConsole implements Console {
}
public void error(Throwable e) {
- GWT.log(e.getMessage(), e);
+ // Borrow exception handling from VDebugConsole
+ VDebugConsole.handleError(e, this);
}
public void setQuietMode(boolean quietDebugMode) {
diff --git a/src/com/vaadin/terminal/gwt/client/Paintable.java b/src/com/vaadin/terminal/gwt/client/Paintable.java
index 62abeab5a0..c9e3ef79cc 100644
--- a/src/com/vaadin/terminal/gwt/client/Paintable.java
+++ b/src/com/vaadin/terminal/gwt/client/Paintable.java
@@ -12,6 +12,7 @@ package com.vaadin.terminal.gwt.client;
* Updates can be sent back to the server using the
* {@link ApplicationConnection#updateVariable()} methods.
*/
+@Deprecated
public interface Paintable {
public void updateFromUIDL(UIDL uidl, ApplicationConnection client);
diff --git a/src/com/vaadin/terminal/gwt/client/ServerConnector.java b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
new file mode 100644
index 0000000000..b331f1f07d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
@@ -0,0 +1,104 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client;
+
+import java.util.Collection;
+
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+
+/**
+ * Interface implemented by all client side classes that can be communicate with
+ * the server. Classes implementing this interface are initialized by the
+ * framework when needed and have the ability to communicate with the server.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public interface ServerConnector extends Connector {
+
+ /**
+ * Sets a new state for the connector.
+ *
+ * @param state
+ * The new state
+ * @deprecated This should be removed. Framework should update what is
+ * returned by getState() instead of setting a new state object.
+ * Note that this must be done either so that setState accepts a
+ * state object once (first time received from the server) or
+ * getState() in AbstractConnector uses a generated class to
+ * create the state object (like RpcProy.craete())
+ */
+ @Deprecated
+ public void setState(SharedState state);
+
+ /**
+ * Gets ApplicationConnection instance that created this connector.
+ *
+ * @return The ApplicationConnection as set by
+ * {@link #doInit(String, ApplicationConnection)}
+ */
+ public ApplicationConnection getConnection();
+
+ /**
+ * Tests whether the connector is enabled or not. This method checks that
+ * the connector is enabled in context, i.e. if the parent connector is
+ * disabled, this method must return false.
+ *
+ * @return true if the connector is enabled, false otherwise
+ */
+ public boolean isEnabled();
+
+ /**
+ *
+ * Called once by the framework to initialize the connector.
+ * <p>
+ * Note that the shared state is not yet available at this point nor any
+ * hierarchy information.
+ */
+ public void doInit(String connectorId, ApplicationConnection connection);
+
+ /**
+ * For internal use by the framework: returns the registered RPC
+ * implementations for an RPC interface identifier.
+ *
+ * TODO interface identifier type or format may change
+ *
+ * @param rpcInterfaceId
+ * RPC interface identifier: fully qualified interface type name
+ * @return RPC interface implementations registered for an RPC interface,
+ * not null
+ */
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId);
+
+ /**
+ * Adds a handler that is called whenever some part of the state has been
+ * updated by the server.
+ *
+ * @param handler
+ * The handler that should be added.
+ * @return A handler registration reference that can be used to unregister
+ * the handler
+ */
+ public HandlerRegistration addStateChangeHandler(StateChangeHandler handler);
+
+ /**
+ * Sends the given event to all registered handlers.
+ *
+ * @param event
+ * The event to send.
+ */
+ public void fireEvent(GwtEvent<?> event);
+
+ /**
+ * Event called when connector has been unregistered.
+ */
+ public void onUnregister();
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/SimpleTree.java b/src/com/vaadin/terminal/gwt/client/SimpleTree.java
index 017884c94f..350e0d707d 100644
--- a/src/com/vaadin/terminal/gwt/client/SimpleTree.java
+++ b/src/com/vaadin/terminal/gwt/client/SimpleTree.java
@@ -28,6 +28,7 @@ public class SimpleTree extends ComplexPanel {
Style style = getElement().getStyle();
style.setProperty("whiteSpace", "nowrap");
style.setPadding(3, Unit.PX);
+ style.setPaddingLeft(12, Unit.PX);
style = handle.getStyle();
style.setDisplay(Display.NONE);
@@ -43,7 +44,7 @@ public class SimpleTree extends ComplexPanel {
getElement().appendChild(handle);
getElement().appendChild(text);
style = children.getStyle();
- style.setPaddingLeft(20, Unit.PX);
+ style.setPaddingLeft(9, Unit.PX);
style.setDisplay(Display.NONE);
getElement().appendChild(children);
@@ -109,7 +110,7 @@ public class SimpleTree extends ComplexPanel {
protected void add(Widget child, Element container) {
super.add(child, container);
handle.getStyle().setDisplay(Display.INLINE_BLOCK);
-
+ getElement().getStyle().setPaddingLeft(3, Unit.PX);
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/TooltipInfo.java b/src/com/vaadin/terminal/gwt/client/TooltipInfo.java
index 6f8ddd5237..fb33a56c56 100644
--- a/src/com/vaadin/terminal/gwt/client/TooltipInfo.java
+++ b/src/com/vaadin/terminal/gwt/client/TooltipInfo.java
@@ -7,7 +7,7 @@ public class TooltipInfo {
private String title;
- private UIDL errorUidl;
+ private String errorMessageHtml;
public TooltipInfo() {
}
@@ -24,12 +24,12 @@ public class TooltipInfo {
this.title = title;
}
- public UIDL getErrorUidl() {
- return errorUidl;
+ public String getErrorMessage() {
+ return errorMessageHtml;
}
- public void setErrorUidl(UIDL errorUidl) {
- this.errorUidl = errorUidl;
+ public void setErrorMessage(String errorMessage) {
+ errorMessageHtml = errorMessage;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/UIDL.java b/src/com/vaadin/terminal/gwt/client/UIDL.java
index a6298af8d1..a523016b60 100644
--- a/src/com/vaadin/terminal/gwt/client/UIDL.java
+++ b/src/com/vaadin/terminal/gwt/client/UIDL.java
@@ -16,7 +16,7 @@ import com.vaadin.ui.Component;
/**
* When a component is updated, it's client side widget's
- * {@link Paintable#updateFromUIDL(UIDL, ApplicationConnection)
+ * {@link ComponentConnector#updateFromUIDL(UIDL, ApplicationConnection)
* updateFromUIDL()} will be called with the updated ("changes") UIDL received
* from the server.
* <p>
@@ -55,7 +55,7 @@ public final class UIDL extends JavaScriptObject {
* AbstractComponent.paintContent()}. Note that if the UIDL corresponds to a
* Paintable, a component identifier will be returned instead - this is used
* internally and is not needed within
- * {@link Paintable#updateFromUIDL(UIDL, ApplicationConnection)
+ * {@link ComponentConnector#updateFromUIDL(UIDL, ApplicationConnection)
* updateFromUIDL()}.
*
* @return the name for this section
@@ -493,17 +493,6 @@ public final class UIDL extends JavaScriptObject {
return this.length - 2;
}-*/;
- /**
- * Shorthand that returns the component errors as UIDL. Only applicable for
- * Paintables.
- *
- * @return the error UIDL if available
- */
- public native UIDL getErrors()
- /*-{
- return this[1]['error'];
- }-*/;
-
native boolean isMapAttribute(String name)
/*-{
return typeof this[1][name] == "object";
@@ -516,9 +505,10 @@ public final class UIDL extends JavaScriptObject {
* the name of the attribute
* @return the Paintable referenced by the attribute, if it exists
*/
- public Paintable getPaintableAttribute(String name,
+ public ServerConnector getPaintableAttribute(String name,
ApplicationConnection connection) {
- return connection.getPaintable(getStringAttribute(name));
+ return ConnectorMap.get(connection).getConnector(
+ getStringAttribute(name));
}
/**
@@ -528,9 +518,10 @@ public final class UIDL extends JavaScriptObject {
* the name of the variable
* @return the Paintable referenced by the variable, if it exists
*/
- public Paintable getPaintableVariable(String name,
+ public ServerConnector getPaintableVariable(String name,
ApplicationConnection connection) {
- return connection.getPaintable(getStringVariable(name));
+ return ConnectorMap.get(connection).getConnector(
+ getStringVariable(name));
}
/**
diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java
index b9baf362e4..bfe63caefd 100644
--- a/src/com/vaadin/terminal/gwt/client/Util.java
+++ b/src/com/vaadin/terminal/gwt/client/Util.java
@@ -5,16 +5,13 @@
package com.vaadin.terminal.gwt.client;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+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.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
@@ -26,12 +23,12 @@ 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.Timer;
import com.google.gwt.user.client.Window;
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.terminal.gwt.client.RenderInformation.FloatSize;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
public class Util {
@@ -67,30 +64,6 @@ public class Util {
return el;
}-*/;
- private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
- private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>();
-
- private static Timer lazySizeChangeTimer = new Timer() {
- private boolean lazySizeChangeTimerScheduled = false;
-
- @Override
- public void run() {
- componentSizeUpdated(latelyChangedWidgets);
- latelyChangedWidgets.clear();
- lazySizeChangeTimerScheduled = false;
- }
-
- @Override
- public void schedule(int delayMillis) {
- if (lazySizeChangeTimerScheduled) {
- cancel();
- } else {
- lazySizeChangeTimerScheduled = true;
- }
- super.schedule(delayMillis);
- }
- };
-
/**
* This helper method can be called if components size have been changed
* outside rendering phase. It notifies components parent about the size
@@ -106,61 +79,37 @@ public class Util {
* @param widget
* @param lazy
* run componentSizeUpdated lazyly
- */
- public static void notifyParentOfSizeChange(Paintable widget, boolean lazy) {
- if (lazy) {
- latelyChangedWidgets.add(widget);
- lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
- } else {
- Set<Paintable> widgets = new HashSet<Paintable>();
- widgets.add(widget);
- Util.componentSizeUpdated(widgets);
- }
- }
-
- /**
- * Called when the size of one or more widgets have changed during
- * rendering. Finds parent container and notifies them of the size change.
*
- * @param paintables
+ * @deprecated since 7.0, use
+ * {@link LayoutManager#setNeedsMeasure(ComponentConnector)}
+ * instead
*/
- public static void componentSizeUpdated(Set<Paintable> paintables) {
- if (paintables.isEmpty()) {
- return;
+ @Deprecated
+ public static void notifyParentOfSizeChange(Widget widget, boolean lazy) {
+ ComponentConnector connector = findConnectorFor(widget);
+ if (connector != null) {
+ connector.getLayoutManager().setNeedsMeasure(connector);
+ if (!lazy) {
+ connector.getLayoutManager().layoutNow();
+ }
}
+ }
- Map<Container, Set<Paintable>> childWidgets = new HashMap<Container, Set<Paintable>>();
-
- for (Paintable paintable : paintables) {
- Widget widget = (Widget) paintable;
- if (!widget.isAttached()) {
+ private static ComponentConnector findConnectorFor(Widget widget) {
+ List<ApplicationConnection> runningApplications = ApplicationConfiguration
+ .getRunningApplications();
+ for (ApplicationConnection applicationConnection : runningApplications) {
+ ConnectorMap connectorMap = applicationConnection.getConnectorMap();
+ ComponentConnector connector = connectorMap.getConnector(widget);
+ if (connector == null) {
continue;
}
-
- // ApplicationConnection.getConsole().log(
- // "Widget " + Util.getSimpleName(widget) + " size updated");
- Widget parent = widget.getParent();
- while (parent != null && !(parent instanceof Container)) {
- parent = parent.getParent();
- }
- if (parent != null) {
- Set<Paintable> set = childWidgets.get(parent);
- if (set == null) {
- set = new HashSet<Paintable>();
- childWidgets.put((Container) parent, set);
- }
- set.add(paintable);
- }
- }
-
- Set<Paintable> parentChanges = new HashSet<Paintable>();
- for (Container parent : childWidgets.keySet()) {
- if (!parent.requestLayout(childWidgets.get(parent))) {
- parentChanges.add(parent);
+ if (connector.getConnection() == applicationConnection) {
+ return connector;
}
}
- componentSizeUpdated(parentChanges);
+ return null;
}
public static float parseRelativeSize(String size) {
@@ -176,68 +125,6 @@ public class Util {
}
}
- /**
- * Returns closest parent Widget in hierarchy that implements Container
- * interface
- *
- * @param component
- * @return closest parent Container
- */
- public static Container getLayout(Widget component) {
- Widget parent = component.getParent();
- while (parent != null && !(parent instanceof Container)) {
- parent = parent.getParent();
- }
- if (parent != null) {
- assert ((Container) parent).hasChildComponent(component);
-
- return (Container) parent;
- }
- return null;
- }
-
- /**
- * Detects if current browser is IE.
- *
- * @deprecated use BrowserInfo class instead
- *
- * @return true if IE
- */
- @Deprecated
- public static boolean isIE() {
- return BrowserInfo.get().isIE();
- }
-
- /**
- * Detects if current browser is IE6.
- *
- * @deprecated use BrowserInfo class instead
- *
- * @return true if IE6
- */
- @Deprecated
- public static boolean isIE6() {
- return BrowserInfo.get().isIE6();
- }
-
- /**
- * @deprecated use BrowserInfo class instead
- * @return
- */
- @Deprecated
- public static boolean isIE7() {
- return BrowserInfo.get().isIE7();
- }
-
- /**
- * @deprecated use BrowserInfo class instead
- * @return
- */
- @Deprecated
- public static boolean isFF2() {
- return BrowserInfo.get().isFF2();
- }
-
private static final Element escapeHtmlHelper = DOM.createDiv();
/**
@@ -249,8 +136,8 @@ public class Util {
public static String escapeHTML(String html) {
DOM.setInnerText(escapeHtmlHelper, html);
String escapedText = DOM.getInnerHTML(escapeHtmlHelper);
- if (BrowserInfo.get().isIE() && BrowserInfo.get().getIEVersion() < 9) {
- // #7478 IE6-IE8 "incorrectly" returns "<br>" for newlines set using
+ if (BrowserInfo.get().isIE8()) {
+ // #7478 IE8 "incorrectly" returns "<br>" for newlines set using
// setInnerText. The same for " " which is converted to "&nbsp;"
escapedText = escapedText.replaceAll("<(BR|br)>", "\n");
escapedText = escapedText.replaceAll("&nbsp;", " ");
@@ -275,48 +162,6 @@ public class Util {
}
/**
- * Adds transparent PNG fix to image element; only use for IE6.
- *
- * @param el
- * IMG element
- */
- public native static void addPngFix(Element el)
- /*-{
- el.attachEvent("onload", $entry(function() {
- @com.vaadin.terminal.gwt.client.Util::doIE6PngFix(Lcom/google/gwt/user/client/Element;)(el);
- }),false);
- }-*/;
-
- private native static void doPngFix(Element el, String blankImageUrl)
- /*-{
- var src = el.src;
- if (src.indexOf(".png") < 1) return;
- var w = el.width || 16;
- var h = el.height || 16;
- if(h==30 || w==28) {
- setTimeout(function(){
- el.style.height = el.height + "px";
- el.style.width = el.width + "px";
- el.src = blankImageUrl;
- },10);
- } else {
- el.src = blankImageUrl;
- el.style.height = h + "px";
- el.style.width = w + "px";
- }
- el.style.padding = "0";
- el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='crop')";
- }-*/;
-
- public static void doIE6PngFix(Element el) {
- String blankImageUrl = GWT.getModuleBaseURL() + "ie6pngfix/blank.gif";
- String src = el.getAttribute("src");
- if (src != null && !src.equals(blankImageUrl)) {
- doPngFix(el, blankImageUrl);
- }
- }
-
- /**
* Clones given element as in JavaScript.
*
* Deprecate this if there appears similar method into GWT someday.
@@ -334,11 +179,7 @@ public class Util {
public static int measureHorizontalPaddingAndBorder(Element element,
int paddingGuess) {
String originalWidth = DOM.getStyleAttribute(element, "width");
- String originalOverflow = "";
- if (BrowserInfo.get().isIE6()) {
- originalOverflow = DOM.getStyleAttribute(element, "overflow");
- DOM.setStyleAttribute(element, "overflow", "hidden");
- }
+
int originalOffsetWidth = element.getOffsetWidth();
int widthGuess = (originalOffsetWidth - paddingGuess);
if (widthGuess < 1) {
@@ -348,9 +189,7 @@ public class Util {
int padding = element.getOffsetWidth() - widthGuess;
DOM.setStyleAttribute(element, "width", originalWidth);
- if (BrowserInfo.get().isIE6()) {
- DOM.setStyleAttribute(element, "overflow", originalOverflow);
- }
+
return padding;
}
@@ -378,23 +217,19 @@ public class Util {
int offsetWidth = element.getOffsetWidth();
int offsetHeight = element.getOffsetHeight();
- if (!BrowserInfo.get().isIE7()) {
- if (offsetHeight < 1) {
- offsetHeight = 1;
- }
- if (offsetWidth < 1) {
- offsetWidth = 10;
- }
- element.getStyle().setPropertyPx("height", offsetHeight);
+ if (offsetHeight < 1) {
+ offsetHeight = 1;
+ }
+ if (offsetWidth < 1) {
+ offsetWidth = 10;
}
+ element.getStyle().setPropertyPx("height", offsetHeight);
element.getStyle().setPropertyPx("width", offsetWidth);
borders = element.getOffsetWidth() - element.getClientWidth();
element.getStyle().setProperty("width", width);
- if (!BrowserInfo.get().isIE7()) {
- element.getStyle().setProperty("height", height);
- }
+ element.getStyle().setProperty("height", height);
} else {
borders = element.getOffsetWidth()
- element.getPropertyInt("clientWidth");
@@ -412,7 +247,6 @@ public class Util {
int offsetWidth = element.getOffsetWidth();
int offsetHeight = element.getOffsetHeight();
- // if (BrowserInfo.get().isIE6()) {
if (offsetHeight < 1) {
offsetHeight = 1;
}
@@ -420,7 +254,6 @@ public class Util {
offsetWidth = 10;
}
element.getStyle().setPropertyPx("width", offsetWidth);
- // }
element.getStyle().setPropertyPx("height", offsetHeight);
@@ -428,9 +261,7 @@ public class Util {
- element.getPropertyInt("clientHeight");
element.getStyle().setProperty("height", height);
- // if (BrowserInfo.get().isIE6()) {
element.getStyle().setProperty("width", width);
- // }
} else {
borders = element.getOffsetHeight()
- element.getPropertyInt("clientHeight");
@@ -602,8 +433,7 @@ public class Util {
public static void runWebkitOverflowAutoFix(final Element elem) {
// Add max version if fix lands sometime to Webkit
// Starting from Opera 11.00, also a problem in Opera
- if ((BrowserInfo.get().getWebkitVersion() > 0 || BrowserInfo.get()
- .getOperaVersion() >= 11) && getNativeScrollbarSize() > 0) {
+ if (BrowserInfo.get().requiresOverflowAutoFix()) {
final String originalOverflow = elem.getStyle().getProperty(
"overflow");
if ("hidden".equals(originalOverflow)) {
@@ -662,39 +492,28 @@ public class Util {
}
/**
- * Parses the UIDL parameter and fetches the relative size of the component.
- * If a dimension is not specified as relative it will return -1. If the
- * UIDL does not contain width or height specifications this will return
+ * Parses shared state and fetches the relative size of the component. If a
+ * dimension is not specified as relative it will return -1. If the shared
+ * state does not contain width or height specifications this will return
* null.
*
- * @param uidl
+ * @param state
* @return
*/
- public static FloatSize parseRelativeSize(UIDL uidl) {
- boolean hasAttribute = false;
- String w = "";
- String h = "";
- if (uidl.hasAttribute("width")) {
- hasAttribute = true;
- w = uidl.getStringAttribute("width");
- }
- if (uidl.hasAttribute("height")) {
- hasAttribute = true;
- h = uidl.getStringAttribute("height");
- }
-
- if (!hasAttribute) {
+ public static FloatSize parseRelativeSize(ComponentState state) {
+ if (state.isUndefinedHeight() && state.isUndefinedWidth()) {
return null;
}
- float relativeWidth = Util.parseRelativeSize(w);
- float relativeHeight = Util.parseRelativeSize(h);
+ float relativeWidth = Util.parseRelativeSize(state.getWidth());
+ float relativeHeight = Util.parseRelativeSize(state.getHeight());
FloatSize relativeSize = new FloatSize(relativeWidth, relativeHeight);
return relativeSize;
}
+ @Deprecated
public static boolean isCached(UIDL uidl) {
return uidl.getBooleanAttribute("cached");
}
@@ -714,27 +533,8 @@ public class Util {
}
public static void updateRelativeChildrenAndSendSizeUpdateEvent(
- ApplicationConnection client, HasWidgets container) {
- updateRelativeChildrenAndSendSizeUpdateEvent(client, container,
- (Paintable) container);
- }
-
- public static void updateRelativeChildrenAndSendSizeUpdateEvent(
- ApplicationConnection client, HasWidgets container, Paintable widget) {
- /*
- * Relative sized children must be updated first so the component has
- * the correct outer dimensions when signaling a size change to the
- * parent.
- */
- Iterator<Widget> childIterator = container.iterator();
- while (childIterator.hasNext()) {
- Widget w = childIterator.next();
- client.handleComponentRelativeSize(w);
- }
-
- HashSet<Paintable> widgets = new HashSet<Paintable>();
- widgets.add(widget);
- Util.componentSizeUpdated(widgets);
+ ApplicationConnection client, HasWidgets container, Widget widget) {
+ notifyParentOfSizeChange(widget, false);
}
public static native int getRequiredWidth(
@@ -823,92 +623,13 @@ public class Util {
}-*/;
/**
- * IE7 sometimes "forgets" to render content. This function runs a hack to
- * workaround the bug if needed. This happens easily in framset. See #3295.
- */
- public static void runIE7ZeroSizedBodyFix() {
- if (BrowserInfo.get().isIE7()) {
- int offsetWidth = RootPanel.getBodyElement().getOffsetWidth();
- if (offsetWidth == 0) {
- shakeBodyElement();
- }
- }
- }
-
- /**
- * Does some very small adjustments to body element. We need this just to
- * overcome some IE bugs.
- */
- public static void shakeBodyElement() {
- final DivElement shaker = Document.get().createDivElement();
- RootPanel.getBodyElement().insertBefore(shaker,
- RootPanel.getBodyElement().getFirstChildElement());
- shaker.getStyle().setPropertyPx("height", 0);
- shaker.setInnerHTML("&nbsp;");
- RootPanel.getBodyElement().removeChild(shaker);
-
- }
-
- /**
- * Locates the child component of <literal>parent</literal> which contains
- * the element <literal>element</literal>. The child component is also
- * returned if "element" is part of its caption. If
- * <literal>element</literal> is not part of any child component, null is
- * returned.
- *
- * This method returns the immediate child of the parent that contains the
- * element. See
- * {@link #getPaintableForElement(ApplicationConnection, Container, Element)}
- * for the deepest nested paintable of parent that contains the element.
- *
- * @param client
- * A reference to ApplicationConnection
- * @param parent
- * The widget that contains <literal>element</literal>.
- * @param element
- * An element that is a sub element of the parent
- * @return The Paintable which the element is a part of. Null if the element
- * does not belong to a child.
- */
- public static Paintable getChildPaintableForElement(
- ApplicationConnection client, Container parent, Element element) {
- Element rootElement = ((Widget) parent).getElement();
- while (element != null && element != rootElement) {
- Paintable paintable = client.getPaintable(element);
- if (paintable == null) {
- String ownerPid = VCaption.getCaptionOwnerPid(element);
- if (ownerPid != null) {
- paintable = client.getPaintable(ownerPid);
- }
- }
-
- if (paintable != null) {
- try {
- if (parent.hasChildComponent((Widget) paintable)) {
- return paintable;
- }
- } catch (ClassCastException e) {
- // We assume everything is a widget however there is no need
- // to crash everything if there is a paintable that is not.
- }
- }
-
- element = (Element) element.getParentElement();
- }
-
- return null;
- }
-
- /**
* Locates the nested child component of <literal>parent</literal> which
* contains the element <literal>element</literal>. The child component is
* also returned if "element" is part of its caption. If
* <literal>element</literal> is not part of any child component, null is
* returned.
*
- * This method returns the deepest nested Paintable. See
- * {@link #getChildPaintableForElement(ApplicationConnection, Container, Element)}
- * for the immediate child component of parent that contains the element.
+ * This method returns the deepest nested VPaintableWidget.
*
* @param client
* A reference to ApplicationConnection
@@ -916,18 +637,20 @@ public class Util {
* The widget that contains <literal>element</literal>.
* @param element
* An element that is a sub element of the parent
- * @return The Paintable which the element is a part of. Null if the element
- * does not belong to a child.
+ * @return The VPaintableWidget which the element is a part of. Null if the
+ * element does not belong to a child.
*/
- public static Paintable getPaintableForElement(
+ public static ComponentConnector getConnectorForElement(
ApplicationConnection client, Widget parent, Element element) {
Element rootElement = parent.getElement();
while (element != null && element != rootElement) {
- Paintable paintable = client.getPaintable(element);
+ ComponentConnector paintable = ConnectorMap.get(client)
+ .getConnector(element);
if (paintable == null) {
String ownerPid = VCaption.getCaptionOwnerPid(element);
if (ownerPid != null) {
- paintable = client.getPaintable(ownerPid);
+ paintable = (ComponentConnector) ConnectorMap.get(client)
+ .getConnector(ownerPid);
}
}
@@ -965,6 +688,24 @@ public class Util {
}-*/;
/**
+ * Helper method to find the nearest parent paintable instance by traversing
+ * the DOM upwards from given element.
+ *
+ * @param element
+ * the element to start from
+ */
+ public static ComponentConnector findPaintable(
+ ApplicationConnection client, Element element) {
+ Widget widget = Util.findWidget(element, null);
+ ConnectorMap vPaintableMap = ConnectorMap.get(client);
+ while (widget != null && !vPaintableMap.isConnector(widget)) {
+ widget = widget.getParent();
+ }
+ return vPaintableMap.getConnector(widget);
+
+ }
+
+ /**
* Helper method to find first instance of given Widget type found by
* traversing DOM upwards from given element.
*
@@ -1071,14 +812,34 @@ public class Util {
return idx;
}
- private static void printPaintablesVariables(ArrayList<String[]> vars,
- String id, ApplicationConnection c) {
- Paintable paintable = c.getPaintable(id);
+ private static void printPaintablesInvocations(
+ ArrayList<MethodInvocation> invocations, String id,
+ ApplicationConnection c) {
+ ComponentConnector paintable = (ComponentConnector) ConnectorMap.get(c)
+ .getConnector(id);
if (paintable != null) {
VConsole.log("\t" + id + " (" + paintable.getClass() + ") :");
- for (String[] var : vars) {
- VConsole.log("\t\t" + var[1] + " (" + var[2] + ")" + " : "
- + var[0]);
+ for (MethodInvocation invocation : invocations) {
+ Object[] parameters = invocation.getParameters();
+ String formattedParams = null;
+ if (ApplicationConnection.UPDATE_VARIABLE_METHOD
+ .equals(invocation.getMethodName())
+ && parameters.length == 2) {
+ // name, value
+ Object value = parameters[1];
+ // TODO paintables inside lists/maps get rendered as
+ // components in the debug console
+ String formattedValue = value instanceof ServerConnector ? ((ServerConnector) value)
+ .getConnectorId() : String.valueOf(value);
+ formattedParams = parameters[0] + " : " + formattedValue;
+ }
+ if (null == formattedParams) {
+ formattedParams = (null != parameters) ? Arrays
+ .toString(parameters) : null;
+ }
+ VConsole.log("\t\t" + invocation.getInterfaceName() + "."
+ + invocation.getMethodName() + "(" + formattedParams
+ + ")");
}
} else {
VConsole.log("\t" + id + ": Warning: no corresponding paintable!");
@@ -1086,31 +847,25 @@ public class Util {
}
static void logVariableBurst(ApplicationConnection c,
- ArrayList<String> loggedBurst) {
+ ArrayList<MethodInvocation> loggedBurst) {
try {
VConsole.log("Variable burst to be sent to server:");
String curId = null;
- ArrayList<String[]> vars = new ArrayList<String[]>();
+ ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
for (int i = 0; i < loggedBurst.size(); i++) {
- String value = loggedBurst.get(i++);
- String[] split = loggedBurst
- .get(i)
- .split(String
- .valueOf(ApplicationConnection.VAR_FIELD_SEPARATOR));
- String id = split[0];
+ String id = loggedBurst.get(i).getConnectorId();
if (curId == null) {
curId = id;
} else if (!curId.equals(id)) {
- printPaintablesVariables(vars, curId, c);
- vars.clear();
+ printPaintablesInvocations(invocations, curId, c);
+ invocations.clear();
curId = id;
}
- split[0] = value;
- vars.add(split);
+ invocations.add(loggedBurst.get(i));
}
- if (!vars.isEmpty()) {
- printPaintablesVariables(vars, curId, c);
+ if (!invocations.isEmpty()) {
+ printPaintablesInvocations(invocations, curId, c);
}
} catch (Exception e) {
VConsole.error(e);
@@ -1334,4 +1089,49 @@ public class Util {
boolean touchEvent = Util.isTouchEvent(event);
return touchEvent || event.getButton() == Event.BUTTON_LEFT;
}
+
+ /**
+ * Performs a shallow comparison of the collections.
+ *
+ * @param collection1
+ * The first collection
+ * @param collection2
+ * The second collection
+ * @return true if the collections contain the same elements in the same
+ * order, false otherwise
+ */
+ public static boolean collectionsEquals(Collection collection1,
+ Collection collection2) {
+ if (collection1 == null) {
+ return collection2 == null;
+ }
+ if (collection2 == null) {
+ return false;
+ }
+ Iterator<Object> collection1Iterator = collection1.iterator();
+ Iterator<Object> collection2Iterator = collection2.iterator();
+
+ while (collection1Iterator.hasNext()) {
+ if (!collection2Iterator.hasNext()) {
+ return false;
+ }
+ Object collection1Object = collection1Iterator.next();
+ Object collection2Object = collection2Iterator.next();
+ if (collection1Object != collection2Object) {
+ return false;
+ }
+ }
+ if (collection2Iterator.hasNext()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static String getConnectorString(ServerConnector p) {
+ if (p == null) {
+ return "null";
+ }
+ return getSimpleName(p) + " (" + p.getConnectorId() + ")";
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java b/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java
index aaef981bab..89e106f063 100644
--- a/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java
+++ b/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java
@@ -22,6 +22,9 @@ public class VBrowserDetails implements Serializable {
private boolean isWebKit = false;
private boolean isPresto = false;
+ private boolean isChromeFrameCapable = false;
+ private boolean isChromeFrame = false;
+
private boolean isSafari = false;
private boolean isChrome = false;
private boolean isFirefox = false;
@@ -59,6 +62,10 @@ public class VBrowserDetails implements Serializable {
&& (userAgent.indexOf("webtv") == -1);
isFirefox = userAgent.indexOf(" firefox/") != -1;
+ // chromeframe
+ isChromeFrameCapable = userAgent.indexOf("chromeframe") != -1;
+ isChromeFrame = isChromeFrameCapable && !isChrome;
+
// Rendering engine version
try {
if (isGecko) {
@@ -210,6 +217,24 @@ public class VBrowserDetails implements Serializable {
}
/**
+ * Tests if the browser is capable of running ChromeFrame.
+ *
+ * @return true if it has ChromeFrame, false otherwise
+ */
+ public boolean isChromeFrameCapable() {
+ return isChromeFrameCapable;
+ }
+
+ /**
+ * Tests if the browser is running ChromeFrame.
+ *
+ * @return true if it is ChromeFrame, false otherwise
+ */
+ public boolean isChromeFrame() {
+ return isChromeFrame;
+ }
+
+ /**
* Tests if the browser is Opera.
*
* @return true if it is Opera, false otherwise
@@ -302,4 +327,30 @@ public class VBrowserDetails implements Serializable {
return isLinux;
}
+ /**
+ * Checks if the browser is so old that it simply won't work with a Vaadin
+ * application. NOTE that the browser might still be capable of running
+ * Crome Frame, so you might still want to check
+ * {@link #isChromeFrameCapable()} if this returns true.
+ *
+ * @return true if the browser won't work, false if not the browser is
+ * supported or might work
+ */
+ public boolean isTooOldToFunctionProperly() {
+ if (isIE() && getBrowserMajorVersion() < 8) {
+ return true;
+ }
+ if (isSafari() && getBrowserMajorVersion() < 5) {
+ return true;
+ }
+ if (isFirefox() && getBrowserMajorVersion() < 4) {
+ return true;
+ }
+ if (isOpera() && getBrowserMajorVersion() < 11) {
+ return true;
+ }
+
+ return false;
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/VCaption.java b/src/com/vaadin/terminal/gwt/client/VCaption.java
index c4b61d2544..6f3fcf2c3a 100644
--- a/src/com/vaadin/terminal/gwt/client/VCaption.java
+++ b/src/com/vaadin/terminal/gwt/client/VCaption.java
@@ -8,13 +8,14 @@ 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.terminal.gwt.client.ui.AbstractFieldConnector;
import com.vaadin.terminal.gwt.client.ui.Icon;
public class VCaption extends HTML {
public static final String CLASSNAME = "v-caption";
- private final Paintable owner;
+ private final ComponentConnector owner;
private Element errorIndicatorElement;
@@ -24,38 +25,52 @@ public class VCaption extends HTML {
private Element captionText;
- private Element clearElement;
-
private final ApplicationConnection client;
private boolean placedAfterComponent = false;
- private boolean iconOnloadHandled = false;
private int maxWidth = -1;
- protected static final String ATTRIBUTE_ICON = "icon";
- protected static final String ATTRIBUTE_CAPTION = "caption";
- protected static final String ATTRIBUTE_DESCRIPTION = "description";
- protected static final String ATTRIBUTE_REQUIRED = "required";
- protected static final String ATTRIBUTE_ERROR = "error";
- protected static final String ATTRIBUTE_HIDEERRORS = "hideErrors";
+ private enum InsertPosition {
+ ICON, CAPTION, REQUIRED, ERROR
+ }
- private static final String CLASSNAME_CLEAR = CLASSNAME + "-clearelem";
+ /**
+ * Creates a caption that is not linked to a {@link ComponentConnector}.
+ *
+ * When using this constructor, {@link #getOwner()} returns null.
+ *
+ * @param client
+ * ApplicationConnection
+ * @deprecated all captions should be associated with a paintable widget and
+ * be updated from shared state, not UIDL
+ */
+ @Deprecated
+ public VCaption(ApplicationConnection client) {
+ super();
+ this.client = client;
+ owner = null;
+
+ setStyleName(CLASSNAME);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+
+ }
/**
+ * Creates a caption for a {@link ComponentConnector}.
*
* @param component
- * optional owner of caption. If not set, getOwner will return
- * null
+ * owner of caption, not null
* @param client
+ * ApplicationConnection
*/
- public VCaption(Paintable component, ApplicationConnection client) {
+ public VCaption(ComponentConnector component, ApplicationConnection client) {
super();
this.client = client;
owner = component;
if (client != null && owner != null) {
- setOwnerPid(getElement(), client.getPid(owner));
+ setOwnerPid(getElement(), owner.getConnectorId());
}
setStyleName(CLASSNAME);
@@ -66,13 +81,13 @@ public class VCaption extends HTML {
/**
* Updates the caption from UIDL.
*
- * @param uidl
+ * This method may only be called when the caption has an owner - otherwise,
+ * use {@link #updateCaptionWithoutOwner(UIDL, String, boolean, boolean)}.
+ *
* @return true if the position where the caption should be placed has
* changed
*/
- public boolean updateCaption(UIDL uidl) {
- setVisible(!uidl.getBooleanAttribute("invisible"));
-
+ public boolean updateCaption() {
boolean wasPlacedAfterComponent = placedAfterComponent;
// Caption is placed after component unless there is some part which
@@ -80,25 +95,27 @@ public class VCaption extends HTML {
placedAfterComponent = true;
String style = CLASSNAME;
- if (uidl.hasAttribute("style")) {
- final String[] styles = uidl.getStringAttribute("style").split(" ");
- for (int i = 0; i < styles.length; i++) {
- style += " " + CLASSNAME + "-" + styles[i];
+ if (owner.getState().hasStyles()) {
+ for (String customStyle : owner.getState().getStyles()) {
+ style += " " + CLASSNAME + "-" + customStyle;
}
}
-
- if (uidl.hasAttribute("disabled")) {
+ if (!owner.isEnabled()) {
style += " " + ApplicationConnection.DISABLED_CLASSNAME;
}
-
setStyleName(style);
- boolean hasIcon = uidl.hasAttribute(ATTRIBUTE_ICON);
- boolean hasText = uidl.hasAttribute(ATTRIBUTE_CAPTION);
- boolean hasDescription = uidl.hasAttribute(ATTRIBUTE_DESCRIPTION);
- boolean showRequired = uidl.getBooleanAttribute(ATTRIBUTE_REQUIRED);
- boolean showError = uidl.hasAttribute(ATTRIBUTE_ERROR)
- && !uidl.getBooleanAttribute(ATTRIBUTE_HIDEERRORS);
+ boolean hasIcon = owner.getState().getIcon() != null;
+ boolean showRequired = false;
+ boolean showError = owner.getState().getErrorMessage() != null;
+ if (owner.getState() instanceof AbstractFieldState) {
+ AbstractFieldState abstractFieldState = (AbstractFieldState) owner
+ .getState();
+ showError = showError && !abstractFieldState.isHideErrors();
+ }
+ if (owner instanceof AbstractFieldConnector) {
+ showRequired = ((AbstractFieldConnector) owner).isRequired();
+ }
if (hasIcon) {
if (icon == null) {
@@ -107,13 +124,12 @@ public class VCaption extends HTML {
icon.setHeight("0");
DOM.insertChild(getElement(), icon.getElement(),
- getInsertPosition(ATTRIBUTE_ICON));
+ getInsertPosition(InsertPosition.ICON));
}
// Icon forces the caption to be above the component
placedAfterComponent = false;
- iconOnloadHandled = false;
- icon.setUri(uidl.getStringAttribute(ATTRIBUTE_ICON));
+ icon.setUri(owner.getState().getIcon().getURL());
} else if (icon != null) {
// Remove existing
@@ -121,7 +137,7 @@ public class VCaption extends HTML {
icon = null;
}
- if (hasText) {
+ if (owner.getState().getCaption() != null) {
// A caption text should be shown if the attribute is set
// If the caption is null the ATTRIBUTE_CAPTION should not be set to
// avoid ending up here.
@@ -131,11 +147,11 @@ public class VCaption extends HTML {
captionText.setClassName("v-captiontext");
DOM.insertChild(getElement(), captionText,
- getInsertPosition(ATTRIBUTE_CAPTION));
+ getInsertPosition(InsertPosition.CAPTION));
}
// Update caption text
- String c = uidl.getStringAttribute(ATTRIBUTE_CAPTION);
+ String c = owner.getState().getCaption();
// A text forces the caption to be above the component.
placedAfterComponent = false;
if (c == null || c.trim().equals("")) {
@@ -158,12 +174,10 @@ public class VCaption extends HTML {
captionText = null;
}
- if (hasDescription) {
- if (captionText != null) {
- addStyleDependentName("hasdescription");
- } else {
- removeStyleDependentName("hasdescription");
- }
+ if (owner.getState().hasDescription() && captionText != null) {
+ addStyleDependentName("hasdescription");
+ } else {
+ removeStyleDependentName("hasdescription");
}
if (showRequired) {
@@ -174,7 +188,7 @@ public class VCaption extends HTML {
DOM.setInnerText(requiredFieldIndicator, "*");
DOM.insertChild(getElement(), requiredFieldIndicator,
- getInsertPosition(ATTRIBUTE_REQUIRED));
+ getInsertPosition(InsertPosition.REQUIRED));
}
} else if (requiredFieldIndicator != null) {
// Remove existing
@@ -190,7 +204,7 @@ public class VCaption extends HTML {
"v-errorindicator");
DOM.insertChild(getElement(), errorIndicatorElement,
- getInsertPosition(ATTRIBUTE_ERROR));
+ getInsertPosition(InsertPosition.ERROR));
}
} else if (errorIndicatorElement != null) {
// Remove existing
@@ -198,25 +212,19 @@ public class VCaption extends HTML {
errorIndicatorElement = null;
}
- if (clearElement == null) {
- clearElement = DOM.createDiv();
- clearElement.setClassName(CLASSNAME_CLEAR);
- getElement().appendChild(clearElement);
- }
-
return (wasPlacedAfterComponent != placedAfterComponent);
}
- private int getInsertPosition(String element) {
+ private int getInsertPosition(InsertPosition element) {
int pos = 0;
- if (element.equals(ATTRIBUTE_ICON)) {
+ if (InsertPosition.ICON.equals(element)) {
return pos;
}
if (icon != null) {
pos++;
}
- if (element.equals(ATTRIBUTE_CAPTION)) {
+ if (InsertPosition.CAPTION.equals(element)) {
return pos;
}
@@ -224,19 +232,115 @@ public class VCaption extends HTML {
pos++;
}
- if (element.equals(ATTRIBUTE_REQUIRED)) {
+ if (InsertPosition.REQUIRED.equals(element)) {
return pos;
}
if (requiredFieldIndicator != null) {
pos++;
}
- // if (element.equals(ATTRIBUTE_ERROR)) {
+ // if (InsertPosition.ERROR.equals(element)) {
// }
return pos;
}
+ @Deprecated
+ public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
+ boolean hasDescription, boolean hasError, String iconURL) {
+ boolean wasPlacedAfterComponent = placedAfterComponent;
+
+ // Caption is placed after component unless there is some part which
+ // moves it above.
+ placedAfterComponent = true;
+
+ String style = VCaption.CLASSNAME;
+ if (disabled) {
+ style += " " + ApplicationConnection.DISABLED_CLASSNAME;
+ }
+ setStyleName(style);
+ if (hasDescription) {
+ if (captionText != null) {
+ addStyleDependentName("hasdescription");
+ } else {
+ removeStyleDependentName("hasdescription");
+ }
+ }
+ boolean hasIcon = iconURL != null;
+
+ if (hasIcon) {
+ if (icon == null) {
+ icon = new Icon(client);
+ 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(iconURL);
+
+ } else if (icon != null) {
+ // Remove existing
+ DOM.removeChild(getElement(), icon.getElement());
+ icon = null;
+ }
+
+ if (caption != null) {
+ // A caption text should be shown if the attribute is set
+ // If the caption is null the ATTRIBUTE_CAPTION should not be set to
+ // avoid ending up here.
+
+ if (captionText == null) {
+ captionText = DOM.createDiv();
+ captionText.setClassName("v-captiontext");
+
+ DOM.insertChild(getElement(), captionText,
+ getInsertPosition(InsertPosition.CAPTION));
+ }
+
+ // Update caption text
+ // A text forces the caption to be above the component.
+ placedAfterComponent = false;
+ if (caption.trim().equals("")) {
+ // This is required to ensure that the caption uses space in all
+ // browsers when it is set to the empty string. If there is an
+ // icon, error indicator or required indicator they will ensure
+ // that space is reserved.
+ if (!hasIcon && !hasError) {
+ captionText.setInnerHTML("&nbsp;");
+ }
+ } else {
+ DOM.setInnerText(captionText, caption);
+ }
+
+ } else if (captionText != null) {
+ // Remove existing
+ DOM.removeChild(getElement(), captionText);
+ captionText = null;
+ }
+
+ if (hasError) {
+ if (errorIndicatorElement == null) {
+ errorIndicatorElement = DOM.createDiv();
+ DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
+ DOM.setElementProperty(errorIndicatorElement, "className",
+ "v-errorindicator");
+
+ DOM.insertChild(getElement(), errorIndicatorElement,
+ getInsertPosition(InsertPosition.ERROR));
+ }
+ } else if (errorIndicatorElement != null) {
+ // Remove existing
+ getElement().removeChild(errorIndicatorElement);
+ errorIndicatorElement = null;
+ }
+
+ return (wasPlacedAfterComponent != placedAfterComponent);
+ }
+
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -246,16 +350,10 @@ public class VCaption extends HTML {
}
if (DOM.eventGetType(event) == Event.ONLOAD
- && icon.getElement() == target && !iconOnloadHandled) {
+ && icon.getElement() == target) {
icon.setWidth("");
icon.setHeight("");
- /*
- * IE6 pngFix causes two onload events to be fired and we want to
- * react only to the first one
- */
- iconOnloadHandled = true;
-
// if max width defined, recalculate
if (maxWidth != -1) {
setMaxWidth(maxWidth);
@@ -272,24 +370,21 @@ public class VCaption extends HTML {
* the responsibility of reacting to ONLOAD from VCaption to layouts
*/
if (owner != null) {
- Util.notifyParentOfSizeChange(owner, true);
+ Util.notifyParentOfSizeChange(owner.getWidget(), true);
} else {
VConsole.log("Warning: Icon load event was not propagated because VCaption owner is unknown.");
}
}
}
- public static boolean isNeeded(UIDL uidl) {
- if (uidl.getStringAttribute(ATTRIBUTE_CAPTION) != null) {
- return true;
- }
- if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
+ public static boolean isNeeded(ComponentState state) {
+ if (state.getCaption() != null) {
return true;
}
- if (uidl.hasAttribute(ATTRIBUTE_ICON)) {
+ if (state.getIcon() != null) {
return true;
}
- if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) {
+ if (state.getErrorMessage() != null) {
return true;
}
@@ -301,7 +396,7 @@ public class VCaption extends HTML {
*
* @return owner Widget
*/
- public Paintable getOwner() {
+ public ComponentConnector getOwner() {
return owner;
}
diff --git a/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java b/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java
index dbecf96dd0..a8dabb8652 100644
--- a/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java
+++ b/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java
@@ -5,28 +5,35 @@
package com.vaadin.terminal.gwt.client;
import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
public class VCaptionWrapper extends FlowPanel {
public static final String CLASSNAME = "v-captionwrapper";
VCaption caption;
- Paintable widget;
+ ComponentConnector wrappedConnector;
- public VCaptionWrapper(Paintable toBeWrapped, ApplicationConnection client) {
+ /**
+ * Creates a new caption wrapper panel.
+ *
+ * @param toBeWrapped
+ * paintable that the caption is associated with, not null
+ * @param client
+ * ApplicationConnection
+ */
+ public VCaptionWrapper(ComponentConnector toBeWrapped,
+ ApplicationConnection client) {
caption = new VCaption(toBeWrapped, client);
add(caption);
- widget = toBeWrapped;
- add((Widget) widget);
+ wrappedConnector = toBeWrapped;
+ add(wrappedConnector.getWidget());
setStyleName(CLASSNAME);
}
- public void updateCaption(UIDL uidl) {
- caption.updateCaption(uidl);
- setVisible(!uidl.getBooleanAttribute("invisible"));
+ public void updateCaption() {
+ caption.updateCaption();
}
- public Paintable getPaintable() {
- return widget;
+ public ComponentConnector getWrappedConnector() {
+ return wrappedConnector;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/VConsole.java b/src/com/vaadin/terminal/gwt/client/VConsole.java
index a01fa16558..dee8529a84 100644
--- a/src/com/vaadin/terminal/gwt/client/VConsole.java
+++ b/src/com/vaadin/terminal/gwt/client/VConsole.java
@@ -82,8 +82,8 @@ public class VConsole {
public static void printLayoutProblems(ValueMap meta,
ApplicationConnection applicationConnection,
- Set<Paintable> zeroHeightComponents,
- Set<Paintable> zeroWidthComponents) {
+ Set<ComponentConnector> zeroHeightComponents,
+ Set<ComponentConnector> zeroWidthComponents) {
impl.printLayoutProblems(meta, applicationConnection,
zeroHeightComponents, zeroWidthComponents);
}
diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
index 19b478b098..5eaf78f255 100644
--- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
+++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
@@ -4,6 +4,9 @@
package com.vaadin.terminal.gwt.client;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -19,6 +22,8 @@ import com.google.gwt.dom.client.Style.Unit;
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.shared.HandlerRegistration;
import com.google.gwt.event.shared.UmbrellaException;
import com.google.gwt.http.client.Request;
@@ -27,6 +32,7 @@ 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.http.client.UrlBuilder;
+import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
@@ -48,6 +54,9 @@ import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+import com.vaadin.terminal.gwt.client.ui.root.RootConnector;
+import com.vaadin.terminal.gwt.client.ui.window.WindowConnector;
/**
* A helper console for client side development. The debug console can also be
@@ -90,17 +99,17 @@ public class VDebugConsole extends VOverlay implements Console {
for (ApplicationConnection a : ApplicationConfiguration
.getRunningApplications()) {
- Paintable paintable = Util.getPaintableForElement(a,
- a.getView(), eventTarget);
- if (paintable == null) {
- paintable = Util.getPaintableForElement(a,
+ ComponentConnector connector = Util.getConnectorForElement(
+ a, a.getRootConnector().getWidget(), eventTarget);
+ if (connector == null) {
+ connector = Util.getConnectorForElement(a,
RootPanel.get(), eventTarget);
}
- if (paintable != null) {
- String pid = a.getPid(paintable);
- VUIDLBrowser.highlight(paintable);
+ if (connector != null) {
+ String pid = connector.getConnectorId();
+ VUIDLBrowser.highlight(connector);
label.setText("Currently focused :"
- + paintable.getClass() + " ID:" + pid);
+ + connector.getClass() + " ID:" + pid);
event.cancel();
event.consume();
event.getNativeEvent().stopPropagation();
@@ -119,10 +128,10 @@ public class VDebugConsole extends VOverlay implements Console {
.getClientY());
for (ApplicationConnection a : ApplicationConfiguration
.getRunningApplications()) {
- Paintable paintable = Util.getPaintableForElement(a,
- a.getView(), eventTarget);
+ ComponentConnector paintable = Util.getConnectorForElement(
+ a, a.getRootConnector().getWidget(), eventTarget);
if (paintable == null) {
- paintable = Util.getPaintableForElement(a,
+ paintable = Util.getConnectorForElement(a,
RootPanel.get(), eventTarget);
}
@@ -150,6 +159,7 @@ public class VDebugConsole extends VOverlay implements Console {
private Button analyzeLayout = new Button("AL");
private Button savePosition = new Button("S");
private Button highlight = new Button("H");
+ private Button connectorStats = new Button("CS");
private CheckBox hostedMode = new CheckBox("GWT");
private CheckBox autoScroll = new CheckBox("Autoscroll ");
private HorizontalPanel actions;
@@ -336,11 +346,13 @@ public class VDebugConsole extends VOverlay implements Console {
if (msg == null) {
msg = "null";
}
+ msg = addTimestamp(msg);
// remoteLog(msg);
logToDebugWindow(msg, false);
GWT.log(msg);
consoleLog(msg);
+ System.out.println(msg);
}
private List<String> msgQueue = new LinkedList<String>();
@@ -426,11 +438,22 @@ public class VDebugConsole extends VOverlay implements Console {
if (msg == null) {
msg = "null";
}
-
+ msg = addTimestamp(msg);
logToDebugWindow(msg, true);
GWT.log(msg);
consoleErr(msg);
+ System.out.println(msg);
+
+ }
+
+ DateTimeFormat timestampFormat = DateTimeFormat.getFormat("HH:mm:ss:SSS");
+
+ @SuppressWarnings("deprecation")
+ private String addTimestamp(String msg) {
+ Date date = new Date();
+ String timestamp = timestampFormat.format(date);
+ return timestamp + " " + msg;
}
/*
@@ -496,8 +519,8 @@ public class VDebugConsole extends VOverlay implements Console {
}-*/;
public void printLayoutProblems(ValueMap meta, ApplicationConnection ac,
- Set<Paintable> zeroHeightComponents,
- Set<Paintable> zeroWidthComponents) {
+ Set<ComponentConnector> zeroHeightComponents,
+ Set<ComponentConnector> zeroWidthComponents) {
JsArray<ValueMap> valueMapArray = meta
.getJSValueMapArray("invalidLayouts");
int size = valueMapArray.length();
@@ -534,9 +557,10 @@ public class VDebugConsole extends VOverlay implements Console {
}
private void printClientSideDetectedIssues(
- Set<Paintable> zeroHeightComponents, ApplicationConnection ac) {
- for (final Paintable paintable : zeroHeightComponents) {
- final Container layout = Util.getLayout((Widget) paintable);
+ Set<ComponentConnector> zeroHeightComponents,
+ ApplicationConnection ac) {
+ for (final ComponentConnector paintable : zeroHeightComponents) {
+ final Widget layout = paintable.getParent().getWidget();
VerticalPanel errorDetails = new VerticalPanel();
errorDetails.add(new Label("" + Util.getSimpleName(paintable)
@@ -546,7 +570,7 @@ public class VDebugConsole extends VOverlay implements Console {
emphasisInUi.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
if (paintable != null) {
- Element element2 = ((Widget) layout).getElement();
+ Element element2 = layout.getElement();
Widget.setStyleName(element2, "invalidlayout",
emphasisInUi.getValue());
}
@@ -560,7 +584,8 @@ public class VDebugConsole extends VOverlay implements Console {
private void printLayoutError(ValueMap valueMap, SimpleTree root,
final ApplicationConnection ac) {
final String pid = valueMap.getString("id");
- final Paintable paintable = ac.getPaintable(pid);
+ final ComponentConnector paintable = (ComponentConnector) ConnectorMap
+ .get(ac).getConnector(pid);
SimpleTree errorNode = new SimpleTree();
VerticalPanel errorDetails = new VerticalPanel();
@@ -578,7 +603,7 @@ public class VDebugConsole extends VOverlay implements Console {
emphasisInUi.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
if (paintable != null) {
- Element element2 = ((Widget) paintable).getElement();
+ Element element2 = paintable.getWidget().getElement();
Widget.setStyleName(element2, "invalidlayout",
emphasisInUi.getValue());
}
@@ -614,15 +639,34 @@ public class VDebugConsole extends VOverlay implements Console {
}
public void error(Throwable e) {
+ handleError(e, this);
+ }
+
+ static void handleError(Throwable e, Console target) {
if (e instanceof UmbrellaException) {
UmbrellaException ue = (UmbrellaException) e;
for (Throwable t : ue.getCauses()) {
- error(t);
+ target.error(t);
}
return;
}
- error(Util.getSimpleName(e) + ": " + e.getMessage());
+ String exceptionText = Util.getSimpleName(e);
+ String message = e.getMessage();
+ if (message != null && message.length() != 0) {
+ exceptionText += ": " + e.getMessage();
+ }
+ target.error(exceptionText);
GWT.log(e.getMessage(), e);
+ if (!GWT.isProdMode()) {
+ e.printStackTrace();
+ }
+ try {
+ VNotification.createNotification(VNotification.DELAY_FOREVER).show(
+ "<h1>Uncaught client side exception</h1><br />"
+ + exceptionText, VNotification.CENTERED, "error");
+ } catch (Exception e2) {
+ // Just swallow this exception
+ }
}
public void init() {
@@ -661,6 +705,8 @@ public class VDebugConsole extends VOverlay implements Console {
actions.add(forceLayout);
actions.add(analyzeLayout);
actions.add(highlight);
+ actions.add(connectorStats);
+ connectorStats.setTitle("Show connector statistics for client");
highlight
.setTitle("Select a component and print details about it to the server log and client side console.");
actions.add(savePosition);
@@ -788,6 +834,15 @@ public class VDebugConsole extends VOverlay implements Console {
});
}
+ connectorStats.addClickHandler(new ClickHandler() {
+
+ public void onClick(ClickEvent event) {
+ for (ApplicationConnection a : ApplicationConfiguration
+ .getRunningApplications()) {
+ dumpConnectorInfo(a);
+ }
+ }
+ });
log("Starting Vaadin client side engine. Widgetset: "
+ GWT.getModuleName());
@@ -802,6 +857,93 @@ public class VDebugConsole extends VOverlay implements Console {
}
+ protected void dumpConnectorInfo(ApplicationConnection a) {
+ RootConnector root = a.getRootConnector();
+ log("================");
+ log("Connector hierarchy for Root: " + root.getState().getCaption()
+ + " (" + root.getConnectorId() + ")");
+ Set<ComponentConnector> connectorsInHierarchy = new HashSet<ComponentConnector>();
+ SimpleTree rootHierachy = dumpConnectorHierarchy(root, "",
+ connectorsInHierarchy);
+ if (panel.isAttached()) {
+ rootHierachy.open(true);
+ panel.add(rootHierachy);
+ }
+
+ ConnectorMap connectorMap = a.getConnectorMap();
+ Collection<? extends ServerConnector> registeredConnectors = connectorMap
+ .getConnectors();
+ log("Sub windows:");
+ Set<ComponentConnector> subWindowHierarchyConnectors = new HashSet<ComponentConnector>();
+ for (WindowConnector wc : root.getSubWindows()) {
+ SimpleTree windowHierachy = dumpConnectorHierarchy(wc, "",
+ subWindowHierarchyConnectors);
+ if (panel.isAttached()) {
+ windowHierachy.open(true);
+ panel.add(windowHierachy);
+ }
+ }
+ log("Registered connectors not in hierarchy (should be empty):");
+ for (ServerConnector registeredConnector : registeredConnectors) {
+
+ if (connectorsInHierarchy.contains(registeredConnector)) {
+ continue;
+ }
+
+ if (subWindowHierarchyConnectors.contains(registeredConnector)) {
+ continue;
+ }
+ error(getConnectorString(registeredConnector));
+
+ }
+ log("Unregistered connectors in hierarchy (should be empty):");
+ for (ServerConnector hierarchyConnector : connectorsInHierarchy) {
+ if (!connectorMap.hasConnector(hierarchyConnector.getConnectorId())) {
+ error(getConnectorString(hierarchyConnector));
+ }
+
+ }
+
+ log("================");
+
+ }
+
+ private SimpleTree dumpConnectorHierarchy(
+ final ComponentConnector connector, String indent,
+ Set<ComponentConnector> connectors) {
+ SimpleTree simpleTree = new SimpleTree(getConnectorString(connector)) {
+ @Override
+ protected void select(ClickEvent event) {
+ super.select(event);
+ VUIDLBrowser.highlight(connector);
+ }
+ };
+ simpleTree.addDomHandler(new MouseOutHandler() {
+ public void onMouseOut(MouseOutEvent event) {
+ VUIDLBrowser.deHiglight();
+ }
+ }, MouseOutEvent.getType());
+ connectors.add(connector);
+
+ String msg = indent + "* " + getConnectorString(connector);
+ GWT.log(msg);
+ consoleLog(msg);
+ System.out.println(msg);
+
+ if (connector instanceof ComponentContainerConnector) {
+ for (ComponentConnector c : ((ComponentContainerConnector) connector)
+ .getChildren()) {
+ simpleTree.add(dumpConnectorHierarchy(c, indent + " ",
+ connectors));
+ }
+ }
+ return simpleTree;
+ }
+
+ private static String getConnectorString(ServerConnector connector) {
+ return Util.getConnectorString(connector);
+ }
+
public void setQuietMode(boolean quietDebugMode) {
quietMode = quietDebugMode;
}
diff --git a/src/com/vaadin/terminal/gwt/client/VErrorMessage.java b/src/com/vaadin/terminal/gwt/client/VErrorMessage.java
index 58bdccaf55..add6ee4780 100644
--- a/src/com/vaadin/terminal/gwt/client/VErrorMessage.java
+++ b/src/com/vaadin/terminal/gwt/client/VErrorMessage.java
@@ -4,8 +4,6 @@
package com.vaadin.terminal.gwt.client;
-import java.util.Iterator;
-
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
@@ -20,30 +18,13 @@ public class VErrorMessage extends FlowPanel {
setStyleName(CLASSNAME);
}
- public void updateFromUIDL(UIDL uidl) {
+ public void updateMessage(String htmlErrorMessage) {
clear();
- if (uidl.getChildCount() == 0) {
+ if (htmlErrorMessage == null || htmlErrorMessage.length() == 0) {
add(new HTML(" "));
} else {
- for (final Iterator<?> it = uidl.getChildIterator(); it.hasNext();) {
- final Object child = it.next();
- if (child instanceof String) {
- final String errorMessage = (String) child;
- add(new HTML(errorMessage));
- } else {
- try {
- final VErrorMessage childError = new VErrorMessage();
- childError.updateFromUIDL((UIDL) child);
- add(childError);
- } catch (Exception e) {
- // TODO XML type error, check if this can even happen
- // anymore??
- final UIDL.XML xml = (UIDL.XML) child;
- add(new HTML(xml.getXMLAsString()));
-
- }
- }
- }
+ // pre-formatted on the server as div per child
+ add(new HTML(htmlErrorMessage));
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/VTooltip.java b/src/com/vaadin/terminal/gwt/client/VTooltip.java
index 974e2f1f32..70f4a0de0a 100644
--- a/src/com/vaadin/terminal/gwt/client/VTooltip.java
+++ b/src/com/vaadin/terminal/gwt/client/VTooltip.java
@@ -27,7 +27,7 @@ public class VTooltip extends VOverlay {
private static final int QUICK_OPEN_DELAY = 100;
VErrorMessage em = new VErrorMessage();
Element description = DOM.createDiv();
- private Paintable tooltipOwner;
+ private ComponentConnector tooltipOwner;
private boolean closing = false;
private boolean opening = false;
@@ -56,9 +56,9 @@ public class VTooltip extends VOverlay {
*/
private void show(TooltipInfo info) {
boolean hasContent = false;
- if (info.getErrorUidl() != null) {
+ if (info.getErrorMessage() != null) {
em.setVisible(true);
- em.updateFromUIDL(info.getErrorUidl());
+ em.updateMessage(info.getErrorMessage());
hasContent = true;
} else {
em.setVisible(false);
@@ -115,7 +115,7 @@ public class VTooltip extends VOverlay {
}
}
- public void showTooltip(Paintable owner, Event event, Object key) {
+ public void showTooltip(ComponentConnector owner, Event event, Object key) {
if (closing && tooltipOwner == owner && tooltipKey == key) {
// return to same tooltip, cancel closing
closeTimer.cancel();
@@ -212,7 +212,8 @@ public class VTooltip extends VOverlay {
}
- public void handleTooltipEvent(Event event, Paintable owner, Object key) {
+ public void handleTooltipEvent(Event event, ComponentConnector owner,
+ Object key) {
final int type = DOM.eventGetType(event);
if ((VTooltip.TOOLTIP_EVENTS & type) == type) {
if (type == Event.ONMOUSEOVER) {
diff --git a/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java b/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
index 95d2fd0b5f..9fa973dc29 100644
--- a/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
+++ b/src/com/vaadin/terminal/gwt/client/VUIDLBrowser.java
@@ -24,9 +24,8 @@ import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ui.VUnknownComponent;
-import com.vaadin.terminal.gwt.client.ui.VView;
-import com.vaadin.terminal.gwt.client.ui.VWindow;
+import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.window.VWindow;
public class VUIDLBrowser extends SimpleTree {
private static final String HELP = "Shift click handle to open recursively. Click components to hightlight them on client side. Shift click components to highlight them also on the server side.";
@@ -47,7 +46,9 @@ public class VUIDLBrowser extends SimpleTree {
}
Set<String> keySet = u.getKeySet();
for (String key : keySet) {
- if (key.equals("changes")) {
+ if (key.equals("state")) {
+ // TODO print updated shared states
+ } else if (key.equals("changes")) {
JsArray<UIDL> jsValueMapArray = u.getJSValueMapArray("changes")
.cast();
for (int i = 0; i < jsValueMapArray.length(); i++) {
@@ -77,8 +78,7 @@ public class VUIDLBrowser extends SimpleTree {
try {
String name = uidl.getTag();
try {
- Integer.parseInt(name);
- name = getNodeName(uidl, conf, name);
+ name = getNodeName(uidl, conf, Integer.parseInt(name));
} catch (Exception e) {
// NOP
}
@@ -97,15 +97,12 @@ public class VUIDLBrowser extends SimpleTree {
}
private String getNodeName(UIDL uidl, ApplicationConfiguration conf,
- String name) {
- Class<? extends Paintable> widgetClassByDecodedTag = conf
- .getWidgetClassByEncodedTag(name);
- if (widgetClassByDecodedTag == VUnknownComponent.class) {
- return conf.getUnknownServerClassNameByEncodedTagName(name)
+ int tag) {
+ Class<? extends ComponentConnector> widgetClassByDecodedTag = conf
+ .getWidgetClassByEncodedTag(tag);
+ if (widgetClassByDecodedTag == UnknownComponentConnector.class) {
+ return conf.getUnknownServerClassNameByTag(tag)
+ "(NO CLIENT IMPLEMENTATION FOUND)";
- } else if (widgetClassByDecodedTag == VView.class
- && uidl.hasAttribute("sub")) {
- return "com.vaadin.terminal.gwt.ui.VWindow";
} else {
return widgetClassByDecodedTag.getName();
}
@@ -130,8 +127,8 @@ public class VUIDLBrowser extends SimpleTree {
// same
// host page
for (ApplicationConnection applicationConnection : runningApplications) {
- Paintable paintable = applicationConnection.getPaintable(uidl
- .getId());
+ ComponentConnector paintable = (ComponentConnector) ConnectorMap
+ .get(applicationConnection).getConnector(uidl.getId());
highlight(paintable);
if (event != null && event.getNativeEvent().getShiftKey()) {
applicationConnection.highlightComponent(paintable);
@@ -146,8 +143,7 @@ public class VUIDLBrowser extends SimpleTree {
String nodeName = uidl.getTag();
try {
- Integer.parseInt(nodeName);
- nodeName = getNodeName(uidl, conf, nodeName);
+ nodeName = getNodeName(uidl, conf, Integer.parseInt(nodeName));
} catch (Exception e) {
// NOP
}
@@ -201,7 +197,7 @@ public class VUIDLBrowser extends SimpleTree {
tmp.addItem(name + "=" + value);
}
if (tmp != null) {
- add(tmp);
+ add(tmp);
}
} catch (final Exception e) {
// Ignored, no variables
@@ -243,9 +239,9 @@ public class VUIDLBrowser extends SimpleTree {
}
}
- static void highlight(Paintable paintable) {
- Widget w = (Widget) paintable;
- if (w != null) {
+ static void highlight(ComponentConnector paintable) {
+ if (paintable != null) {
+ Widget w = paintable.getWidget();
Style style = highlight.getStyle();
style.setTop(w.getAbsoluteTop(), Unit.PX);
style.setLeft(w.getAbsoluteLeft(), Unit.PX);
@@ -261,4 +257,4 @@ public class VUIDLBrowser extends SimpleTree {
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ValueMap.java b/src/com/vaadin/terminal/gwt/client/ValueMap.java
index 8d14ef57ce..5deb5feb55 100644
--- a/src/com/vaadin/terminal/gwt/client/ValueMap.java
+++ b/src/com/vaadin/terminal/gwt/client/ValueMap.java
@@ -101,4 +101,9 @@ public final class ValueMap extends JavaScriptObject {
return '' + this[name];
}-*/;
+ native JavaScriptObject getJavaScriptObject(String name)
+ /*-{
+ return this[name];
+ }-*/;
+
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java b/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
index a5c75d27dd..dd69883d58 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetInstantiator.java
@@ -7,5 +7,5 @@ package com.vaadin.terminal.gwt.client;
* A helper class used by WidgetMap implementation. Used by the generated code.
*/
interface WidgetInstantiator {
- public Paintable get();
+ public ComponentConnector get();
}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetMap.java b/src/com/vaadin/terminal/gwt/client/WidgetMap.java
index 51dac20132..af84a11ced 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetMap.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetMap.java
@@ -5,19 +5,61 @@ package com.vaadin.terminal.gwt.client;
import java.util.HashMap;
+import com.vaadin.terminal.gwt.widgetsetutils.WidgetMapGenerator;
+
+/**
+ * Abstract class mapping between {@link ComponentConnector} instances and their
+ * instances.
+ *
+ * A concrete implementation of this class is generated by
+ * {@link WidgetMapGenerator} or one of its subclasses during widgetset
+ * compilation.
+ */
abstract class WidgetMap {
protected static HashMap<Class, WidgetInstantiator> instmap = new HashMap<Class, WidgetInstantiator>();
- public Paintable instantiate(Class<? extends Paintable> classType) {
+ /**
+ * Create a new instance of a connector based on its type.
+ *
+ * @param classType
+ * {@link ComponentConnector} class to instantiate
+ * @return new instance of the connector
+ */
+ public ComponentConnector instantiate(
+ Class<? extends ComponentConnector> classType) {
return instmap.get(classType).get();
}
- public abstract Class<? extends Paintable> getImplementationByServerSideClassName(
+ /**
+ * Return the connector class to use for a fully qualified server side
+ * component class name.
+ *
+ * @param fullyqualifiedName
+ * fully qualified name of the server side component class
+ * @return component connector class to use
+ */
+ public abstract Class<? extends ComponentConnector> getConnectorClassForServerSideClassName(
String fullyqualifiedName);
- public abstract Class<? extends Paintable>[] getDeferredLoadedWidgets();
+ /**
+ * Return the connector classes to load after the initial widgetset load and
+ * start.
+ *
+ * @return component connector class to load after the initial widgetset
+ * loading
+ */
+ public abstract Class<? extends ComponentConnector>[] getDeferredLoadedWidgets();
- public abstract void ensureInstantiator(Class<? extends Paintable> classType);
+ /**
+ * Make sure the code for a (deferred or lazy) component connector type has
+ * been loaded, triggering the load and waiting for its completion if
+ * necessary.
+ *
+ * @param classType
+ * component connector class
+ */
+ public abstract void ensureInstantiator(
+ Class<? extends ComponentConnector> classType);
}
diff --git a/src/com/vaadin/terminal/gwt/client/WidgetSet.java b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
index fb77775549..e47837296d 100644
--- a/src/com/vaadin/terminal/gwt/client/WidgetSet.java
+++ b/src/com/vaadin/terminal/gwt/client/WidgetSet.java
@@ -6,18 +6,7 @@ package com.vaadin.terminal.gwt.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ui.VButton;
-import com.vaadin.terminal.gwt.client.ui.VCheckBox;
-import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
-import com.vaadin.terminal.gwt.client.ui.VListSelect;
-import com.vaadin.terminal.gwt.client.ui.VPasswordField;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical;
-import com.vaadin.terminal.gwt.client.ui.VTextArea;
-import com.vaadin.terminal.gwt.client.ui.VTextField;
-import com.vaadin.terminal.gwt.client.ui.VUnknownComponent;
-import com.vaadin.terminal.gwt.client.ui.VView;
-import com.vaadin.terminal.gwt.client.ui.VWindow;
+import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
public class WidgetSet {
@@ -30,94 +19,61 @@ public class WidgetSet {
/**
* Create an uninitialized component that best matches given UIDL. The
- * component must be a {@link Widget} that implements {@link Paintable}.
+ * component must be a {@link Widget} that implements
+ * {@link ComponentConnector}.
*
- * @param uidl
- * UIDL to be painted with returned component.
+ * @param tag
+ * component type tag for the component to create
* @param client
* the application connection that whishes to instantiate widget
*
* @return New uninitialized and unregistered component that can paint given
* UIDL.
*/
- public Paintable createWidget(UIDL uidl, ApplicationConfiguration conf) {
+ public ComponentConnector createWidget(int tag,
+ ApplicationConfiguration conf) {
/*
* Yes, this (including the generated code in WidgetMap) may look very
* odd code, but due the nature of GWT, we cannot do this any cleaner.
* Luckily this is mostly written by WidgetSetGenerator, here are just
* some hacks. Extra instantiation code is needed if client side widget
* has no "native" counterpart on client side.
- *
- * TODO should try to get rid of these exceptions here
*/
- final Class<? extends Paintable> classType = resolveWidgetType(uidl,
- conf);
- if (classType == null || classType == VUnknownComponent.class) {
- String serverSideName = conf
- .getUnknownServerClassNameByEncodedTagName(uidl.getTag());
- VUnknownComponent c = GWT.create(VUnknownComponent.class);
+ Class<? extends ComponentConnector> classType = resolveInheritedWidgetType(
+ conf, tag);
+
+ if (classType == null || classType == UnknownComponentConnector.class) {
+ String serverSideName = conf.getUnknownServerClassNameByTag(tag);
+ UnknownComponentConnector c = GWT
+ .create(UnknownComponentConnector.class);
c.setServerSideClassName(serverSideName);
return c;
- } else if (VWindow.class == classType) {
- return GWT.create(VWindow.class);
} else {
/*
* let the auto generated code instantiate this type
*/
return widgetMap.instantiate(classType);
}
+ }
+ private Class<? extends ComponentConnector> resolveInheritedWidgetType(
+ ApplicationConfiguration conf, int tag) {
+ Class<? extends ComponentConnector> classType = null;
+ Integer t = tag;
+ do {
+ classType = resolveWidgetType(t, conf);
+ t = conf.getParentTag(t);
+ } while (classType == null && t != null);
+ return classType;
}
- protected Class<? extends Paintable> resolveWidgetType(UIDL uidl,
+ protected Class<? extends ComponentConnector> resolveWidgetType(int tag,
ApplicationConfiguration conf) {
- final String tag = uidl.getTag();
-
- Class<? extends Paintable> widgetClass = conf
+ Class<? extends ComponentConnector> widgetClass = conf
.getWidgetClassByEncodedTag(tag);
- // add our historical quirks
-
- if (widgetClass == VButton.class && uidl.hasAttribute("type")) {
- return VCheckBox.class;
- } else if (widgetClass == VView.class && uidl.hasAttribute("sub")) {
- return VWindow.class;
- } else if (widgetClass == VFilterSelect.class) {
- if (uidl.hasAttribute("type")) {
- final String type = uidl.getStringAttribute("type").intern();
- if ("legacy-multi" == type) {
- return VListSelect.class;
- }
- }
- } else if (widgetClass == VTextField.class) {
- if (uidl.hasAttribute("multiline")) {
- return VTextArea.class;
- } else if (uidl.hasAttribute("secret")) {
- return VPasswordField.class;
- }
- } else if (widgetClass == VSplitPanelHorizontal.class
- && uidl.hasAttribute("vertical")) {
- return VSplitPanelVertical.class;
- }
-
return widgetClass;
-
- }
-
- /**
- * Test if the given component implementation conforms to UIDL.
- *
- * @param currentWidget
- * Current implementation of the component
- * @param uidl
- * UIDL to test against
- * @return true iff createWidget would return a new component of the same
- * class than currentWidget
- */
- public boolean isCorrectImplementation(Widget currentWidget, UIDL uidl,
- ApplicationConfiguration conf) {
- return currentWidget.getClass() == resolveWidgetType(uidl, conf);
}
/**
@@ -125,44 +81,29 @@ public class WidgetSet {
* limitation, widgetset must have function that returns Class by its fully
* qualified name.
*
- * @param fullyQualifiedName
+ * @param tag
* @param applicationConfiguration
* @return
*/
- public Class<? extends Paintable> getImplementationByClassName(
- String fullyqualifiedName) {
- if (fullyqualifiedName == null) {
- return VUnknownComponent.class;
- }
- Class<? extends Paintable> implementationByServerSideClassName = widgetMap
- .getImplementationByServerSideClassName(fullyqualifiedName);
-
- /*
- * Also ensure that our historical quirks have their instantiators
- * loaded. Without these, legacy code will throw NPEs when e.g. a Select
- * is in multiselect mode, causing the clientside implementation to
- * *actually* be VListSelect, when the annotation says VFilterSelect
- */
- if (fullyqualifiedName.equals("com.vaadin.ui.Button")) {
- loadImplementation(VCheckBox.class);
- } else if (fullyqualifiedName.equals("com.vaadin.ui.Select")) {
- loadImplementation(VListSelect.class);
- } else if (fullyqualifiedName.equals("com.vaadin.ui.TextField")) {
- loadImplementation(VTextArea.class);
- loadImplementation(VPasswordField.class);
- } else if (fullyqualifiedName.equals("com.vaadin.ui.SplitPanel")) {
- loadImplementation(VSplitPanelVertical.class);
- }
-
- return implementationByServerSideClassName;
-
+ public Class<? extends ComponentConnector> getConnectorClassByTag(int tag,
+ ApplicationConfiguration conf) {
+ Class<? extends ComponentConnector> connectorClass = null;
+ Integer t = tag;
+ do {
+ String serverSideClassName = conf.getServerSideClassNameForTag(t);
+ connectorClass = widgetMap
+ .getConnectorClassForServerSideClassName(serverSideClassName);
+ t = conf.getParentTag(t);
+ } while (connectorClass == UnknownComponentConnector.class && t != null);
+
+ return connectorClass;
}
- public Class<? extends Paintable>[] getDeferredLoadedWidgets() {
+ public Class<? extends ComponentConnector>[] getDeferredLoadedWidgets() {
return widgetMap.getDeferredLoadedWidgets();
}
- public void loadImplementation(Class<? extends Paintable> nextType) {
+ public void loadImplementation(Class<? extends ComponentConnector> nextType) {
widgetMap.ensureInstantiator(nextType);
}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/AbstractServerConnectorEvent.java b/src/com/vaadin/terminal/gwt/client/communication/AbstractServerConnectorEvent.java
new file mode 100644
index 0000000000..b465e3ad7e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/AbstractServerConnectorEvent.java
@@ -0,0 +1,33 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+
+public abstract class AbstractServerConnectorEvent<H extends EventHandler>
+ extends GwtEvent<H> {
+ private ServerConnector connector;
+
+ protected AbstractServerConnectorEvent() {
+ }
+
+ public ServerConnector getConnector() {
+ return connector;
+ }
+
+ public void setConnector(ServerConnector connector) {
+ this.connector = connector;
+ }
+
+ /**
+ * Sends this event to the given handler.
+ *
+ * @param handler
+ * The handler to dispatch.
+ */
+ @Override
+ public abstract void dispatch(H handler);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java b/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java
new file mode 100644
index 0000000000..45dbe69454
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java
@@ -0,0 +1,23 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+/**
+ * Interface to be extended by all server to client RPC interfaces.
+ *
+ * On the server side, proxies of the interface can be obtained from
+ * AbstractComponent. On the client, RPC implementations can be registered with
+ * AbstractConnector.registerRpc().
+ *
+ * Note: Currently, each RPC interface may not contain multiple methods with the
+ * same name, even if their parameter lists would differ.
+ *
+ * @since 7.0
+ */
+public interface ClientRpc extends Serializable {
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/FieldRpc.java b/src/com/vaadin/terminal/gwt/client/communication/FieldRpc.java
new file mode 100644
index 0000000000..de464f1fb9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/FieldRpc.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+public class FieldRpc {
+ public interface FocusServerRpc extends ServerRpc {
+ public void focus();
+ }
+
+ public interface BlurServerRpc extends ServerRpc {
+ public void blur();
+ }
+
+ public interface FocusAndBlurServerRpc extends FocusServerRpc,
+ BlurServerRpc {
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/InitializableServerRpc.java b/src/com/vaadin/terminal/gwt/client/communication/InitializableServerRpc.java
new file mode 100644
index 0000000000..0270de316e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/InitializableServerRpc.java
@@ -0,0 +1,26 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.vaadin.terminal.gwt.client.ServerConnector;
+
+/**
+ * Initialization support for client to server RPC interfaces.
+ *
+ * This is in a separate interface used by the GWT generator class. The init
+ * method is not in {@link ServerRpc} because then also server side proxies
+ * would have to implement the initialization method.
+ *
+ * @since 7.0
+ */
+public interface InitializableServerRpc extends ServerRpc {
+ /**
+ * Associates the RPC proxy with a connector. Called by generated code.
+ * Should never be called manually.
+ *
+ * @param connector
+ * The connector the ServerRPC instance is assigned to.
+ */
+ public void initRpc(ServerConnector connector);
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java b/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
new file mode 100644
index 0000000000..c626d31d0a
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
@@ -0,0 +1,60 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.google.gwt.json.client.JSONObject;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.server.JsonCodec;
+
+/**
+ * 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.
+ *
+ * The {@link #serialize(Object, ConnectorMap)} and
+ * {@link #deserialize(JSONObject, ConnectorMap)} methods must be symmetric so
+ * they can be chained and produce the original result (or an equal result).
+ *
+ * Each {@link JSONSerializer} implementation can handle an object of a single
+ * type - see {@link SerializerMap}.
+ *
+ * @since 7.0
+ */
+public interface JSONSerializer<T> {
+
+ /**
+ * Creates and deserializes an object received from the server. Must be
+ * compatible with {@link #serialize(Object, ConnectorMap)} and also with
+ * the server side
+ * {@link JsonCodec#encode(Object, com.vaadin.terminal.gwt.server.PaintableIdMapper)}
+ * .
+ *
+ * @param jsonValue
+ * JSON map from property name to property value
+ * @param idMapper
+ * mapper from paintable id to paintable, used to decode
+ * references to paintables
+ * @return A deserialized object
+ */
+ T deserialize(JSONObject jsonValue, ConnectorMap idMapper,
+ ApplicationConnection connection);
+
+ /**
+ * Serialize the given object into JSON. Must be compatible with
+ * {@link #deserialize(JSONObject, ConnectorMap)} and also with the server
+ * side
+ * {@link JsonCodec#decode(com.vaadin.external.json.JSONArray, com.vaadin.terminal.gwt.server.PaintableIdMapper)}
+ *
+ * @param value
+ * The object to serialize
+ * @param idMapper
+ * mapper from paintable id to paintable, used to decode
+ * references to paintables
+ * @return A JSON serialized version of the object
+ */
+ JSONObject serialize(T value, ConnectorMap idMapper,
+ ApplicationConnection connection);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
new file mode 100644
index 0000000000..2b58c13f3e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
@@ -0,0 +1,171 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONString;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+
+/**
+ * Client side decoder for decodeing shared state and other values from JSON
+ * received from the server.
+ *
+ * Currently, basic data types as well as Map, String[] and Object[] are
+ * supported, where maps and Object[] can contain other supported data types.
+ *
+ * TODO extensible type support
+ *
+ * @since 7.0
+ */
+public class JsonDecoder {
+ static SerializerMap serializerMap = GWT.create(SerializerMap.class);
+
+ /**
+ * Decode a JSON array with two elements (type and value) into a client-side
+ * type, recursively if necessary.
+ *
+ * @param jsonArray
+ * JSON array with two elements
+ * @param idMapper
+ * mapper between connector ID and {@link ServerConnector}
+ * objects
+ * @param connection
+ * reference to the current ApplicationConnection
+ * @return decoded value (does not contain JSON types)
+ */
+ public static Object decodeValue(JSONArray jsonArray,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ String type = ((JSONString) jsonArray.get(0)).stringValue();
+ return decodeValue(type, jsonArray.get(1), idMapper, connection);
+ }
+
+ private static Object decodeValue(String variableType, Object value,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ Object val = null;
+ // TODO type checks etc.
+ if (JsonEncoder.VTYPE_NULL.equals(variableType)) {
+ val = null;
+ } else if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) {
+ val = decodeArray((JSONArray) value, idMapper, connection);
+ } else if (JsonEncoder.VTYPE_MAP.equals(variableType)) {
+ val = decodeMap((JSONObject) value, idMapper, connection);
+ } else if (JsonEncoder.VTYPE_MAP_CONNECTOR.equals(variableType)) {
+ val = decodeConnectorMap((JSONObject) value, idMapper, connection);
+ } else if (JsonEncoder.VTYPE_LIST.equals(variableType)) {
+ val = decodeList((JSONArray) value, idMapper, connection);
+ } else if (JsonEncoder.VTYPE_SET.equals(variableType)) {
+ val = decodeSet((JSONArray) value, idMapper, connection);
+ } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) {
+ val = decodeStringArray((JSONArray) value);
+ } else if (JsonEncoder.VTYPE_STRING.equals(variableType)) {
+ val = ((JSONString) value).stringValue();
+ } else if (JsonEncoder.VTYPE_INTEGER.equals(variableType)) {
+ // TODO handle properly
+ val = Integer.valueOf(String.valueOf(value));
+ } else if (JsonEncoder.VTYPE_LONG.equals(variableType)) {
+ // TODO handle properly
+ val = Long.valueOf(String.valueOf(value));
+ } else if (JsonEncoder.VTYPE_FLOAT.equals(variableType)) {
+ // TODO handle properly
+ val = Float.valueOf(String.valueOf(value));
+ } else if (JsonEncoder.VTYPE_DOUBLE.equals(variableType)) {
+ // TODO handle properly
+ val = Double.valueOf(String.valueOf(value));
+ } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) {
+ // TODO handle properly
+ val = Boolean.valueOf(String.valueOf(value));
+ } else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) {
+ val = idMapper.getConnector(((JSONString) value).stringValue());
+ } else {
+ // object, class name as type
+ JSONSerializer serializer = serializerMap
+ .getSerializer(variableType);
+ // TODO handle case with no serializer found
+ Object object = serializer.deserialize((JSONObject) value,
+ idMapper, connection);
+ return object;
+ }
+
+ return val;
+ }
+
+ private static Map<String, Object> decodeMap(JSONObject jsonMap,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ Iterator<String> it = jsonMap.keySet().iterator();
+ while (it.hasNext()) {
+ String key = it.next();
+ map.put(key,
+ decodeValue((JSONArray) jsonMap.get(key), idMapper,
+ connection));
+ }
+ return map;
+ }
+
+ private static Map<Connector, Object> decodeConnectorMap(
+ JSONObject jsonMap, ConnectorMap idMapper,
+ ApplicationConnection connection) {
+ HashMap<Connector, Object> map = new HashMap<Connector, Object>();
+ Iterator<String> it = jsonMap.keySet().iterator();
+ while (it.hasNext()) {
+ String connectorId = it.next();
+ Connector connector = idMapper.getConnector(connectorId);
+ map.put(connector,
+ decodeValue((JSONArray) jsonMap.get(connectorId), idMapper,
+ connection));
+ }
+ return map;
+ }
+
+ private static String[] decodeStringArray(JSONArray jsonArray) {
+ int size = jsonArray.size();
+ List<String> tokens = new ArrayList<String>(size);
+ for (int i = 0; i < size; ++i) {
+ tokens.add(String.valueOf(jsonArray.get(i)));
+ }
+ return tokens.toArray(new String[tokens.size()]);
+ }
+
+ private static Object[] decodeArray(JSONArray jsonArray,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ List<Object> list = decodeList(jsonArray, idMapper, connection);
+ return list.toArray(new Object[list.size()]);
+ }
+
+ private static List<Object> decodeList(JSONArray jsonArray,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ List<Object> tokens = new ArrayList<Object>();
+ for (int i = 0; i < jsonArray.size(); ++i) {
+ // each entry always has two elements: type and value
+ JSONArray entryArray = (JSONArray) jsonArray.get(i);
+ tokens.add(decodeValue(entryArray, idMapper, connection));
+ }
+ return tokens;
+ }
+
+ private static Set<Object> decodeSet(JSONArray jsonArray,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ Set<Object> tokens = new HashSet<Object>();
+ for (int i = 0; i < jsonArray.size(); ++i) {
+ // each entry always has two elements: type and value
+ JSONArray entryArray = (JSONArray) jsonArray.get(i);
+ tokens.add(decodeValue(entryArray, idMapper, connection));
+ }
+ return tokens;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
new file mode 100644
index 0000000000..fdc06b0e21
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
@@ -0,0 +1,207 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONBoolean;
+import com.google.gwt.json.client.JSONNull;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONString;
+import com.google.gwt.json.client.JSONValue;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+
+/**
+ * Encoder for converting RPC parameters and other values to JSON for transfer
+ * between the client and the server.
+ *
+ * Currently, basic data types as well as Map, String[] and Object[] are
+ * supported, where maps and Object[] can contain other supported data types.
+ *
+ * TODO extensible type support
+ *
+ * @since 7.0
+ */
+public class JsonEncoder {
+
+ public static final String VTYPE_CONNECTOR = "c";
+ public static final String VTYPE_BOOLEAN = "b";
+ public static final String VTYPE_DOUBLE = "d";
+ public static final String VTYPE_FLOAT = "f";
+ public static final String VTYPE_LONG = "l";
+ public static final String VTYPE_INTEGER = "i";
+ public static final String VTYPE_STRING = "s";
+ public static final String VTYPE_ARRAY = "a";
+ public static final String VTYPE_STRINGARRAY = "S";
+ public static final String VTYPE_MAP = "m";
+ // Hack to support Map<Connector,?>. Should be replaced by generic support
+ // for any object as key (#8602)
+ @Deprecated
+ public static final String VTYPE_MAP_CONNECTOR = "M";
+ public static final String VTYPE_LIST = "L";
+ public static final String VTYPE_SET = "q";
+ public static final String VTYPE_NULL = "n";
+
+ /**
+ * Encode a value to a JSON representation for transport from the client to
+ * the server.
+ *
+ * @param value
+ * value to convert
+ * @param connectorMap
+ * mapper from connectors to connector IDs
+ * @param connection
+ * @return JSON representation of the value
+ */
+ public static JSONValue encode(Object value, ConnectorMap connectorMap,
+ ApplicationConnection connection) {
+ if (null == value) {
+ return combineTypeAndValue(VTYPE_NULL, JSONNull.getInstance());
+ } else if (value instanceof String[]) {
+ String[] array = (String[]) value;
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < array.length; ++i) {
+ jsonArray.set(i, new JSONString(array[i]));
+ }
+ return combineTypeAndValue(VTYPE_STRINGARRAY, jsonArray);
+ } else if (value instanceof String) {
+ return combineTypeAndValue(VTYPE_STRING, new JSONString(
+ (String) value));
+ } else if (value instanceof Boolean) {
+ return combineTypeAndValue(VTYPE_BOOLEAN,
+ JSONBoolean.getInstance((Boolean) value));
+ } else if (value instanceof Object[]) {
+ return encodeObjectArray((Object[]) value, connectorMap, connection);
+ } else if (value instanceof Map) {
+ Map<Object, Object> map = (Map<Object, Object>) value;
+ JSONObject jsonMap = new JSONObject();
+ String type = VTYPE_MAP;
+ for (Object mapKey : map.keySet()) {
+ Object mapValue = map.get(mapKey);
+ if (mapKey instanceof Connector) {
+ mapKey = ((Connector) mapKey).getConnectorId();
+ type = VTYPE_MAP_CONNECTOR;
+ }
+
+ if (!(mapKey instanceof String)) {
+ throw new RuntimeException(
+ "Only Map<String,?> and Map<Connector,?> is currently supported."
+ + " Failed map used "
+ + mapKey.getClass().getName() + " as keys");
+ }
+ jsonMap.put((String) mapKey,
+ encode(mapValue, connectorMap, connection));
+ }
+ return combineTypeAndValue(type, jsonMap);
+ } else if (value instanceof Connector) {
+ Connector connector = (Connector) value;
+ return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString(
+ connector.getConnectorId()));
+ } else if (value instanceof Collection) {
+ return encodeCollection((Collection) value, connectorMap,
+ connection);
+ } else {
+ String transportType = getTransportType(value);
+ if (transportType != null) {
+ return combineTypeAndValue(transportType,
+ new JSONString(String.valueOf(value)));
+ } else {
+ // Try to find a generated serializer object, class name is the
+ // type
+ transportType = value.getClass().getName();
+ JSONSerializer serializer = JsonDecoder.serializerMap
+ .getSerializer(transportType);
+
+ // TODO handle case with no serializer found
+ return combineTypeAndValue(transportType,
+ serializer.serialize(value, connectorMap, connection));
+ }
+ }
+ }
+
+ private static JSONValue encodeObjectArray(Object[] array,
+ ConnectorMap connectorMap, ApplicationConnection connection) {
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < array.length; ++i) {
+ // TODO handle object graph loops?
+ jsonArray.set(i, encode(array[i], connectorMap, connection));
+ }
+ return combineTypeAndValue(VTYPE_ARRAY, jsonArray);
+ }
+
+ private static JSONValue encodeCollection(Collection collection,
+ ConnectorMap connectorMap, ApplicationConnection connection) {
+ JSONArray jsonArray = new JSONArray();
+ int idx = 0;
+ for (Object o : collection) {
+ JSONValue encodedObject = encode(o, connectorMap, connection);
+ jsonArray.set(idx++, encodedObject);
+ }
+ if (collection instanceof Set) {
+ return combineTypeAndValue(VTYPE_SET, jsonArray);
+ } else if (collection instanceof List) {
+ return combineTypeAndValue(VTYPE_LIST, jsonArray);
+ } else {
+ throw new RuntimeException("Unsupport collection type: "
+ + collection.getClass().getName());
+ }
+
+ }
+
+ private static JSONValue combineTypeAndValue(String type, JSONValue value) {
+ JSONArray outerArray = new JSONArray();
+ outerArray.set(0, new JSONString(type));
+ outerArray.set(1, value);
+ return outerArray;
+ }
+
+ /**
+ * Returns the transport type for the given value. Only returns a transport
+ * type for internally handled values.
+ *
+ * @param value
+ * The value that should be transported
+ * @return One of the JsonEncode.VTYPE_ constants or null if the value
+ * cannot be transported using an internally handled type.
+ */
+ private static String getTransportType(Object value) {
+ if (value == null) {
+ return VTYPE_NULL;
+ } else if (value instanceof String) {
+ return VTYPE_STRING;
+ } else if (value instanceof Connector) {
+ return VTYPE_CONNECTOR;
+ } else if (value instanceof Boolean) {
+ return VTYPE_BOOLEAN;
+ } else if (value instanceof Integer) {
+ return VTYPE_INTEGER;
+ } else if (value instanceof Float) {
+ return VTYPE_FLOAT;
+ } else if (value instanceof Double) {
+ return VTYPE_DOUBLE;
+ } else if (value instanceof Long) {
+ return VTYPE_LONG;
+ } else if (value instanceof List) {
+ return VTYPE_LIST;
+ } else if (value instanceof Set) {
+ return VTYPE_SET;
+ } else if (value instanceof Enum) {
+ return VTYPE_STRING; // transported as string representation
+ } else if (value instanceof String[]) {
+ return VTYPE_STRINGARRAY;
+ } else if (value instanceof Object[]) {
+ return VTYPE_ARRAY;
+ } else if (value instanceof Map) {
+ return VTYPE_MAP;
+ }
+ return null;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java
new file mode 100644
index 0000000000..e61775a640
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java
@@ -0,0 +1,62 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Information needed by the framework to send an RPC method invocation from the
+ * client to the server or vice versa.
+ *
+ * @since 7.0
+ */
+public class MethodInvocation implements Serializable {
+
+ private final String connectorId;
+ private final String interfaceName;
+ private final String methodName;
+ private Object[] parameters;
+
+ public MethodInvocation(String connectorId, String interfaceName,
+ String methodName) {
+ this.connectorId = connectorId;
+ this.interfaceName = interfaceName;
+ this.methodName = methodName;
+ }
+
+ public MethodInvocation(String connectorId, String interfaceName,
+ String methodName, Object[] parameters) {
+ this(connectorId, interfaceName, methodName);
+ setParameters(parameters);
+ }
+
+ public String getConnectorId() {
+ return connectorId;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Object[] parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public String toString() {
+ return connectorId + ":" + interfaceName + "." + methodName + "("
+ + Arrays.toString(parameters) + ")";
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
new file mode 100644
index 0000000000..302e6eaa55
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
@@ -0,0 +1,32 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+
+/**
+ * Client side RPC manager that can invoke methods based on RPC calls received
+ * from the server.
+ *
+ * A GWT generator is used to create an implementation of this class at
+ * run-time.
+ *
+ * @since 7.0
+ */
+public interface RpcManager extends Serializable {
+ /**
+ * Perform server to client RPC invocation.
+ *
+ * @param invocation
+ * method to invoke
+ * @param connectorMap
+ * mapper used to find Connector for the method call and any
+ * connectors referenced in parameters
+ */
+ public void applyInvocation(MethodInvocation invocation,
+ ConnectorMap connectorMap);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcProxy.java b/src/com/vaadin/terminal/gwt/client/communication/RpcProxy.java
new file mode 100644
index 0000000000..113ec1f1b1
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcProxy.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+
+/**
+ * Class for creating proxy instances for Client to Server RPC.
+ *
+ * @since 7.0
+ */
+public class RpcProxy {
+
+ private static RpcProxyCreator impl = GWT.create(RpcProxyCreator.class);
+
+ /**
+ * Create a proxy class for the given Rpc interface and assign it to the
+ * given connector.
+ *
+ * @param rpcInterface
+ * The rpc interface to construct a proxy for
+ * @param connector
+ * The connector this proxy is connected to
+ * @return A proxy class used for calling Rpc methods.
+ */
+ public static <T extends ServerRpc> T create(Class<T> rpcInterface,
+ ServerConnector connector) {
+ return impl.create(rpcInterface, connector);
+ }
+
+ public interface RpcProxyCreator {
+ <T extends ServerRpc> T create(Class<T> rpcInterface,
+ ServerConnector connector);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java b/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java
new file mode 100644
index 0000000000..0750474d24
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java
@@ -0,0 +1,34 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.vaadin.terminal.gwt.widgetsetutils.SerializerMapGenerator;
+
+/**
+ * Provide a mapping from a type (communicated between the server and the
+ * client) and a {@link JSONSerializer} instance.
+ *
+ * An implementation of this class is created at GWT compilation time by
+ * {@link SerializerMapGenerator}, so this interface can be instantiated with
+ * GWT.create().
+ *
+ * @since 7.0
+ */
+public interface SerializerMap {
+
+ /**
+ * Returns a serializer instance for a given type.
+ *
+ * @param type
+ * type communicated on between the server and the client
+ * (currently fully qualified class name)
+ * @return serializer instance, not null
+ * @throws RuntimeException
+ * if no serializer is found
+ */
+ // TODO better error handling in javadoc and in generator
+ public JSONSerializer getSerializer(String type);
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java b/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java
new file mode 100644
index 0000000000..664c4a391c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java
@@ -0,0 +1,15 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+/**
+ * Interface to be extended by all client to server RPC interfaces.
+ *
+ * @since 7.0
+ */
+public interface ServerRpc extends Serializable {
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/SharedState.java b/src/com/vaadin/terminal/gwt/client/communication/SharedState.java
new file mode 100644
index 0000000000..266d6bcbf2
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/SharedState.java
@@ -0,0 +1,41 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+
+/**
+ * Interface to be implemented by all shared state classes used to communicate
+ * basic information about a {@link Connector} from server to client.
+ *
+ * Shared state classes have to be declared in client side packages to be
+ * accessible both for server and client code. They can be static nested classes
+ * of a {@link ServerConnector}.
+ *
+ * Shared state objects are only sent from the server to the client, and any
+ * modifications from the client should be performed via an RPC call that
+ * modifies the authoritative state on the server.
+ *
+ * A shared state class should be a bean with getters and setters for each
+ * field. Supported data types are simple Java types, other beans and maps and
+ * arrays of these.
+ *
+ * On the client side the connector should override
+ * {@link AbstractComponentConnector#createState()} to create the correct state
+ * class and {@link AbstractComponentConnector#getState()} override the return
+ * type.
+ *
+ * Subclasses of a {@link Connector} using shared state should also provide a
+ * subclass of the shared state class of the parent class to extend the state. A
+ * single {@link Connector} can only have one shared state object.
+ *
+ * @since 7.0
+ */
+public class SharedState implements Serializable {
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java b/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java
new file mode 100644
index 0000000000..39ecbc022c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java
@@ -0,0 +1,34 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+
+public class StateChangeEvent extends
+ AbstractServerConnectorEvent<StateChangeHandler> {
+ /**
+ * Type of this event, used by the event bus.
+ */
+ public static final Type<StateChangeHandler> TYPE = new Type<StateChangeHandler>();
+
+ @Override
+ public Type<StateChangeHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ public StateChangeEvent() {
+ }
+
+ @Override
+ public void dispatch(StateChangeHandler listener) {
+ listener.onStateChanged(this);
+ }
+
+ public interface StateChangeHandler extends Serializable, EventHandler {
+ public void onStateChanged(StateChangeEvent stateChangeEvent);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/URLReference.java b/src/com/vaadin/terminal/gwt/client/communication/URLReference.java
new file mode 100644
index 0000000000..569c4eff47
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/URLReference.java
@@ -0,0 +1,31 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+public class URLReference implements Serializable {
+
+ private String URL;
+
+ /**
+ * Returns the URL that this object refers to.
+ * <p>
+ * Note that the URL can use special protocols like theme://
+ *
+ * @return The URL for this reference or null if unknown.
+ */
+ public String getURL() {
+ return URL;
+ }
+
+ /**
+ * Sets the URL that this object refers to
+ *
+ * @param URL
+ */
+ public void setURL(String URL) {
+ this.URL = URL;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java b/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
new file mode 100644
index 0000000000..4fefc7f845
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
@@ -0,0 +1,32 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.communication;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+
+public class URLReference_Serializer implements JSONSerializer<URLReference> {
+
+ public URLReference deserialize(JSONObject jsonValue,
+ ConnectorMap idMapper, ApplicationConnection connection) {
+ URLReference reference = GWT.create(URLReference.class);
+ JSONArray jsonURL = (JSONArray) jsonValue.get("URL");
+ String URL = (String) JsonDecoder.decodeValue(jsonURL, idMapper,
+ connection);
+ reference.setURL(connection.translateVaadinUri(URL));
+ return reference;
+ }
+
+ public JSONObject serialize(URLReference value, ConnectorMap idMapper,
+ ApplicationConnection connection) {
+ JSONObject json = new JSONObject();
+ json.put("URL",
+ JsonEncoder.encode(value.getURL(), idMapper, connection));
+ return json;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java
new file mode 100644
index 0000000000..31204aa0c6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractClickEventHandler.java
@@ -0,0 +1,161 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.ContextMenuEvent;
+import com.google.gwt.event.dom.client.ContextMenuHandler;
+import com.google.gwt.event.dom.client.DomEvent;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+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.vaadin.terminal.gwt.client.ComponentConnector;
+
+public abstract class AbstractClickEventHandler implements DoubleClickHandler,
+ ContextMenuHandler, MouseUpHandler {
+
+ private HandlerRegistration doubleClickHandlerRegistration;
+ private HandlerRegistration mouseUpHandlerRegistration;
+ private HandlerRegistration contextMenuHandlerRegistration;
+
+ protected ComponentConnector connector;
+ private String clickEventIdentifier;
+
+ public AbstractClickEventHandler(ComponentConnector connector,
+ String clickEventIdentifier) {
+ this.connector = connector;
+ this.clickEventIdentifier = clickEventIdentifier;
+ }
+
+ public void handleEventHandlerRegistration() {
+ // Handle registering/unregistering of click handler depending on if
+ // server side listeners have been added or removed.
+ if (hasEventListener()) {
+ if (mouseUpHandlerRegistration == null) {
+ mouseUpHandlerRegistration = registerHandler(this,
+ MouseUpEvent.getType());
+ contextMenuHandlerRegistration = registerHandler(this,
+ ContextMenuEvent.getType());
+ doubleClickHandlerRegistration = registerHandler(this,
+ DoubleClickEvent.getType());
+ }
+ } else {
+ if (mouseUpHandlerRegistration != null) {
+ // Remove existing handlers
+ doubleClickHandlerRegistration.removeHandler();
+ mouseUpHandlerRegistration.removeHandler();
+ contextMenuHandlerRegistration.removeHandler();
+
+ contextMenuHandlerRegistration = null;
+ mouseUpHandlerRegistration = null;
+ doubleClickHandlerRegistration = null;
+
+ }
+ }
+
+ }
+
+ /**
+ * Registers the given handler to the widget so that the necessary events
+ * are passed to this {@link ClickEventHandler}.
+ * <p>
+ * By default registers the handler with the connector root widget.
+ * </p>
+ *
+ * @param <H>
+ * @param handler
+ * The handler to register
+ * @param type
+ * The type of the handler.
+ * @return A reference for the registration of the handler.
+ */
+ protected <H extends EventHandler> HandlerRegistration registerHandler(
+ final H handler, DomEvent.Type<H> type) {
+ return connector.getWidget().addDomHandler(handler, type);
+ }
+
+ /**
+ * Checks if there is a server side event listener registered for clicks
+ *
+ * @return true if there is a server side event listener registered, false
+ * otherwise
+ */
+ public boolean hasEventListener() {
+ return connector.hasEventListener(clickEventIdentifier);
+ }
+
+ /**
+ * Event handler for context menu. Prevents the browser context menu from
+ * popping up if there is a listener for right clicks.
+ */
+ public void onContextMenu(ContextMenuEvent event) {
+ if (hasEventListener() && shouldFireEvent(event)) {
+ // Prevent showing the browser's context menu when there is a right
+ // click listener.
+ event.preventDefault();
+ }
+ }
+
+ /**
+ * Event handler for mouse up. This is used to detect all single click
+ * events.
+ */
+ public void onMouseUp(MouseUpEvent event) {
+ // TODO For perfect accuracy we should check that a mousedown has
+ // occured on this element before this mouseup and that no mouseup
+ // has occured anywhere after that.
+ if (hasEventListener() && shouldFireEvent(event)) {
+ // "Click" with left, right or middle button
+ fireClick(event.getNativeEvent());
+ }
+ }
+
+ /**
+ * Sends the click event based on the given native event.
+ *
+ * @param event
+ * The native event that caused this click event
+ */
+ protected abstract void fireClick(NativeEvent event);
+
+ /**
+ * Called before firing a click event. Allows sub classes to decide if this
+ * in an event that should cause an event or not.
+ *
+ * @param event
+ * The user event
+ * @return true if the event should be fired, false otherwise
+ */
+ protected boolean shouldFireEvent(DomEvent<?> event) {
+ return true;
+ }
+
+ /**
+ * Event handler for double clicks. Used to fire double click events. Note
+ * that browsers typically fail to prevent the second click event so a
+ * double click will result in two click events and one double click event.
+ */
+ public void onDoubleClick(DoubleClickEvent event) {
+ if (hasEventListener() && shouldFireEvent(event)) {
+ fireClick(event.getNativeEvent());
+ }
+ }
+
+ /**
+ * Click event calculates and returns coordinates relative to the element
+ * returned by this method. Default implementation uses the root element of
+ * the widget. Override to provide a different relative element.
+ *
+ * @return The Element used for calculating relative coordinates for a click
+ * or null if no relative coordinates can be calculated.
+ */
+ protected Element getRelativeToElement() {
+ return connector.getWidget().getElement();
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java
new file mode 100644
index 0000000000..fdb04f0ddf
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java
@@ -0,0 +1,353 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.Set;
+
+import com.google.gwt.user.client.ui.Focusable;
+import com.google.gwt.user.client.ui.HasEnabled;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.TooltipInfo;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.root.RootConnector;
+
+public abstract class AbstractComponentConnector extends AbstractConnector
+ implements ComponentConnector {
+
+ private ComponentContainerConnector parent;
+
+ private Widget widget;
+
+ // shared state from the server to the client
+ private ComponentState state;
+
+ private String lastKnownWidth = "";
+ private String lastKnownHeight = "";
+
+ /**
+ * Default constructor
+ */
+ public AbstractComponentConnector() {
+ }
+
+ /**
+ * Creates and returns the widget for this VPaintableWidget. This method
+ * should only be called once when initializing the paintable.
+ *
+ * @return
+ */
+ protected abstract Widget createWidget();
+
+ /**
+ * Returns the widget associated with this paintable. The widget returned by
+ * this method must not changed during the life time of the paintable.
+ *
+ * @return The widget associated with this paintable
+ */
+ public Widget getWidget() {
+ if (widget == null) {
+ widget = createWidget();
+ }
+
+ return widget;
+ }
+
+ /**
+ * Returns the shared state object for this connector.
+ *
+ * If overriding this method to return a more specific type, also
+ * {@link #createState()} must be overridden.
+ *
+ * @return current shared state (not null)
+ */
+ public ComponentState getState() {
+ return state;
+ }
+
+ @Deprecated
+ public static boolean isRealUpdate(UIDL uidl) {
+ return !uidl.hasAttribute("cached");
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ ConnectorMap paintableMap = ConnectorMap.get(getConnection());
+
+ if (getState().getDebugId() != null) {
+ getWidget().getElement().setId(getState().getDebugId());
+ } else {
+ getWidget().getElement().removeAttribute("id");
+
+ }
+
+ /*
+ * Disabled state may affect (override) tabindex so the order must be
+ * first setting tabindex, then enabled state.
+ */
+ if (state instanceof TabIndexState && getWidget() instanceof Focusable) {
+ ((Focusable) getWidget()).setTabIndex(((TabIndexState) state)
+ .getTabIndex());
+ }
+
+ setWidgetEnabled(isEnabled());
+
+ // Style names
+ String styleName = getStyleNames(getWidget().getStylePrimaryName());
+ getWidget().setStyleName(styleName);
+
+ // Update tooltip
+ TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null);
+ if (getState().hasDescription()) {
+ tooltipInfo.setTitle(getState().getDescription());
+ } else {
+ tooltipInfo.setTitle(null);
+ }
+ // add error info to tooltip if present
+ tooltipInfo.setErrorMessage(getState().getErrorMessage());
+
+ // Set captions
+ if (delegateCaptionHandling()) {
+ ComponentContainerConnector parent = getParent();
+ if (parent != null) {
+ parent.updateCaption(this);
+ } else if (!(this instanceof RootConnector)) {
+ VConsole.error("Parent of connector "
+ + Util.getConnectorString(this)
+ + " is null. This is typically an indication of a broken component hierarchy");
+ }
+ }
+
+ /*
+ * updateComponentSize need to be after caption update so caption can be
+ * taken into account
+ */
+
+ updateComponentSize();
+ }
+
+ public void setWidgetEnabled(boolean widgetEnabled) {
+ if (getWidget() instanceof HasEnabled) {
+ ((HasEnabled) getWidget()).setEnabled(widgetEnabled);
+ }
+ }
+
+ private void updateComponentSize() {
+ String newWidth = getState().getWidth();
+ String newHeight = getState().getHeight();
+
+ // Parent should be updated if either dimension changed between relative
+ // and non-relative
+ if (newWidth.endsWith("%") != lastKnownWidth.endsWith("%")) {
+ ComponentContainerConnector parent = getParent();
+ if (parent instanceof ManagedLayout) {
+ getLayoutManager().setNeedsHorizontalLayout(
+ (ManagedLayout) parent);
+ }
+ }
+
+ if (newHeight.endsWith("%") != lastKnownHeight.endsWith("%")) {
+ ComponentContainerConnector parent = getParent();
+ if (parent instanceof ManagedLayout) {
+ getLayoutManager().setNeedsVerticalLayout(
+ (ManagedLayout) parent);
+ }
+ }
+
+ lastKnownWidth = newWidth;
+ lastKnownHeight = newHeight;
+
+ // Set defined sizes
+ Widget widget = getWidget();
+
+ widget.setStyleName("v-has-width", !isUndefinedWidth());
+ widget.setStyleName("v-has-height", !isUndefinedHeight());
+
+ widget.setHeight(newHeight);
+ widget.setWidth(newWidth);
+ }
+
+ public boolean isRelativeHeight() {
+ return getState().getHeight().endsWith("%");
+ }
+
+ public boolean isRelativeWidth() {
+ return getState().getWidth().endsWith("%");
+ }
+
+ public boolean isUndefinedHeight() {
+ return getState().getHeight().length() == 0;
+ }
+
+ public boolean isUndefinedWidth() {
+ return getState().getWidth().length() == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#isEnabled()
+ */
+ public boolean isEnabled() {
+ if (!getState().isEnabled()) {
+ return false;
+ }
+
+ if (getParent() == null) {
+ return true;
+ } else {
+ return getParent().isEnabled();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.ComponentConnector#delegateCaptionHandling
+ * ()
+ */
+ public boolean delegateCaptionHandling() {
+ return true;
+ }
+
+ /**
+ * Generates the style name for the widget based on the given primary style
+ * name and the shared state.
+ * <p>
+ * This method can be overridden to provide additional style names for the
+ * component
+ * </p>
+ *
+ * @param primaryStyleName
+ * The primary style name to use when generating the final style
+ * names
+ * @return The style names, settable using
+ * {@link Widget#setStyleName(String)}
+ */
+ protected String getStyleNames(String primaryStyleName) {
+ ComponentState state = getState();
+
+ StringBuilder styleBuf = new StringBuilder();
+ styleBuf.append(primaryStyleName);
+ styleBuf.append(" v-connector");
+
+ // Uses connector methods to enable connectors to take hierarchy or
+ // multiple state variables into account
+ if (!isEnabled()) {
+ styleBuf.append(" ");
+ styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME);
+ }
+ if (isReadOnly()) {
+ styleBuf.append(" ");
+ styleBuf.append("v-readonly");
+ }
+
+ // add additional styles as css classes, prefixed with component default
+ // stylename
+ if (state.hasStyles()) {
+ for (String style : state.getStyles()) {
+ styleBuf.append(" ");
+ styleBuf.append(primaryStyleName);
+ styleBuf.append("-");
+ styleBuf.append(style);
+ styleBuf.append(" ");
+ styleBuf.append(style);
+ }
+ }
+
+ // add error classname to components w/ error
+ if (null != state.getErrorMessage()) {
+ styleBuf.append(" ");
+ styleBuf.append(primaryStyleName);
+ styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT);
+ }
+
+ return styleBuf.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ComponentConnector#isReadOnly()
+ */
+ @Deprecated
+ public boolean isReadOnly() {
+ return getState().isReadOnly();
+ }
+
+ /**
+ * Sets the shared state for the paintable widget.
+ *
+ * @param new shared state (must be compatible with the return value of
+ * {@link #getState()} - {@link ComponentState} if
+ * {@link #getState()} is not overridden
+ */
+ public final void setState(SharedState state) {
+ this.state = (ComponentState) state;
+ }
+
+ public LayoutManager getLayoutManager() {
+ return LayoutManager.get(getConnection());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#getParent()
+ */
+ public ComponentContainerConnector getParent() {
+ return parent;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.Connector#setParent(com.vaadin.terminal
+ * .gwt.client.ComponentContainerConnector)
+ */
+ public void setParent(ComponentContainerConnector parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Checks if there is a registered server side listener for the given event
+ * identifier.
+ *
+ * @param eventIdentifier
+ * The identifier to check for
+ * @return true if an event listener has been registered with the given
+ * event identifier on the server side, false otherwise
+ */
+ public boolean hasEventListener(String eventIdentifier) {
+ Set<String> reg = getState().getRegisteredEventListeners();
+ return (reg != null && reg.contains(eventIdentifier));
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+
+ // Show an error if widget is still attached to DOM. It should never be
+ // at this point.
+ if (getWidget() != null && getWidget().isAttached()) {
+ getWidget().removeFromParent();
+ VConsole.error("Widget is still attached to the DOM after the connector ("
+ + Util.getConnectorString(this)
+ + ") has been unregistered. Widget was removed.");
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java
new file mode 100644
index 0000000000..526631e4b2
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java
@@ -0,0 +1,110 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+
+public abstract class AbstractComponentContainerConnector extends
+ AbstractComponentConnector implements ComponentContainerConnector,
+ ConnectorHierarchyChangeHandler {
+
+ List<ComponentConnector> children;
+
+ private final boolean debugLogging = false;
+
+ /**
+ * Temporary storage for last enabled state to be able to see if it has
+ * changed. Can be removed once we are able to listen specifically for
+ * enabled changes in the state. Widget.isEnabled() cannot be used as all
+ * Widgets do not implement HasEnabled
+ */
+ private boolean lastWidgetEnabledState = true;
+
+ /**
+ * Default constructor
+ */
+ public AbstractComponentContainerConnector() {
+ addConnectorHierarchyChangeHandler(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.ComponentContainerConnector#getChildren()
+ */
+ public List<ComponentConnector> getChildren() {
+ if (children == null) {
+ return new LinkedList<ComponentConnector>();
+ }
+
+ return children;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.ComponentContainerConnector#setChildren
+ * (java.util.Collection)
+ */
+ public void setChildren(List<ComponentConnector> children) {
+ this.children = children;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ComponentContainerConnector#
+ * connectorHierarchyChanged
+ * (com.vaadin.terminal.gwt.client.ConnectorHierarchyChangedEvent)
+ */
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ if (debugLogging) {
+ VConsole.log("Hierarchy changed for "
+ + Util.getConnectorString(this));
+ String oldChildren = "* Old children: ";
+ for (ComponentConnector child : event.getOldChildren()) {
+ oldChildren += Util.getConnectorString(child) + " ";
+ }
+ VConsole.log(oldChildren);
+
+ String newChildren = "* New children: ";
+ for (ComponentConnector child : getChildren()) {
+ newChildren += Util.getConnectorString(child) + " ";
+ }
+ VConsole.log(newChildren);
+ }
+ }
+
+ public HandlerRegistration addConnectorHierarchyChangeHandler(
+ ConnectorHierarchyChangeHandler handler) {
+ return ensureHandlerManager().addHandler(
+ ConnectorHierarchyChangeEvent.TYPE, handler);
+ }
+
+ @Override
+ public void setWidgetEnabled(boolean widgetEnabled) {
+ if (lastWidgetEnabledState == widgetEnabled) {
+ return;
+ }
+ lastWidgetEnabledState = widgetEnabled;
+
+ super.setWidgetEnabled(widgetEnabled);
+ for (ComponentConnector c : getChildren()) {
+ // Update children as they might be affected by the enabled state of
+ // their parent
+ c.setWidgetEnabled(c.isEnabled());
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
new file mode 100644
index 0000000000..ef74228ae8
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
@@ -0,0 +1,186 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HandlerManager;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+
+/**
+ * An abstract implementation of Connector.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public abstract class AbstractConnector implements ServerConnector,
+ StateChangeHandler {
+
+ private ApplicationConnection connection;
+ private String id;
+
+ private HandlerManager handlerManager;
+ private Map<String, Collection<ClientRpc>> rpcImplementations;
+ private final boolean debugLogging = false;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.VPaintable#getConnection()
+ */
+ public final ApplicationConnection getConnection() {
+ return connection;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#getId()
+ */
+ public String getConnectorId() {
+ return id;
+ }
+
+ /**
+ * Called once by the framework to initialize the connector.
+ * <p>
+ * Note that the shared state is not yet available when this method is
+ * called.
+ * <p>
+ * Connector classes should override {@link #init()} instead of this method.
+ */
+ public final void doInit(String connectorId,
+ ApplicationConnection connection) {
+ this.connection = connection;
+ id = connectorId;
+
+ addStateChangeHandler(this);
+ init();
+ }
+
+ /**
+ * Called when the connector has been initialized. Override this method to
+ * perform initialization of the connector.
+ */
+ // FIXME: It might make sense to make this abstract to force users to
+ // use init instead of constructor, where connection and id has not yet been
+ // set.
+ protected void init() {
+
+ }
+
+ /**
+ * Registers an implementation for a server to client RPC interface.
+ *
+ * Multiple registrations can be made for a single interface, in which case
+ * all of them receive corresponding RPC calls.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation that should receive RPC calls
+ * @param <T>
+ * The type of the RPC interface that is being registered
+ */
+ protected <T extends ClientRpc> void registerRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null == rpcImplementations) {
+ rpcImplementations = new HashMap<String, Collection<ClientRpc>>();
+ }
+ if (null == rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.put(rpcInterfaceId, new ArrayList<ClientRpc>());
+ }
+ rpcImplementations.get(rpcInterfaceId).add(implementation);
+ }
+
+ /**
+ * Unregisters an implementation for a server to client RPC interface.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation to unregister
+ */
+ protected <T extends ClientRpc> void unregisterRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null != rpcImplementations
+ && null != rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.get(rpcInterfaceId).remove(implementation);
+ }
+ }
+
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId) {
+ if (null == rpcImplementations) {
+ return Collections.emptyList();
+ }
+ return (Collection<T>) rpcImplementations.get(rpcInterfaceId);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
+ */
+ public boolean isConnectorEnabled() {
+ // Client side can always receive message from the server
+ return true;
+ }
+
+ public void fireEvent(GwtEvent<?> event) {
+ if (handlerManager != null) {
+ handlerManager.fireEvent(event);
+ }
+ }
+
+ protected HandlerManager ensureHandlerManager() {
+ if (handlerManager == null) {
+ handlerManager = new HandlerManager(this);
+ }
+
+ return handlerManager;
+ }
+
+ public HandlerRegistration addStateChangeHandler(StateChangeHandler handler) {
+ return ensureHandlerManager()
+ .addHandler(StateChangeEvent.TYPE, handler);
+ }
+
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ if (debugLogging) {
+ VConsole.log("State change event for "
+ + Util.getConnectorString(stateChangeEvent.getConnector())
+ + " received by " + Util.getConnectorString(this));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.ServerConnector#onUnregister()
+ */
+ public void onUnregister() {
+ if (debugLogging) {
+ VConsole.log("Unregistered connector "
+ + Util.getConnectorString(this));
+ }
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractFieldConnector.java
new file mode 100644
index 0000000000..4be0f02c2a
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractFieldConnector.java
@@ -0,0 +1,54 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+
+public abstract class AbstractFieldConnector extends AbstractComponentConnector {
+
+ @Override
+ public AbstractFieldState getState() {
+ return (AbstractFieldState) super.getState();
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly() || getState().isPropertyReadOnly();
+ }
+
+ public boolean isModified() {
+ return getState().isModified();
+ }
+
+ /**
+ * Checks whether the required indicator should be shown for the field.
+ *
+ * Required indicators are hidden if the field or its data source is
+ * read-only.
+ *
+ * @return true if required indicator should be shown
+ */
+ public boolean isRequired() {
+ return getState().isRequired() && !isReadOnly();
+ }
+
+ @Override
+ protected String getStyleNames(String primaryStyleName) {
+ String styleNames = super.getStyleNames(primaryStyleName);
+
+ if (isModified()) {
+ // add modified classname to Fields
+ styleNames += " " + ApplicationConnection.MODIFIED_CLASSNAME;
+ }
+
+ if (isRequired()) {
+ // add required classname to Fields
+ styleNames += " " + primaryStyleName
+ + ApplicationConnection.REQUIRED_CLASSNAME_EXT;
+ }
+
+ return styleNames;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutConnector.java
new file mode 100644
index 0000000000..175e67807f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutConnector.java
@@ -0,0 +1,13 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+public abstract class AbstractLayoutConnector extends
+ AbstractComponentContainerConnector {
+
+ @Override
+ public AbstractLayoutState getState() {
+ return (AbstractLayoutState) super.getState();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutState.java
new file mode 100644
index 0000000000..fee5ea746a
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractLayoutState.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+
+public class AbstractLayoutState extends ComponentState {
+ private int marginsBitmask;
+
+ public int getMarginsBitmask() {
+ return marginsBitmask;
+ }
+
+ public void setMarginsBitmask(int marginsBitmask) {
+ this.marginsBitmask = marginsBitmask;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
index 3deb140d30..cffdb1e68a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/ClickEventHandler.java
@@ -3,134 +3,48 @@
*/
package com.vaadin.terminal.gwt.client.ui;
-import java.util.HashMap;
-import java.util.Map;
-
import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.event.dom.client.ContextMenuEvent;
-import com.google.gwt.event.dom.client.ContextMenuHandler;
-import com.google.gwt.event.dom.client.DomEvent;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-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.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-
-public abstract class ClickEventHandler implements DoubleClickHandler,
- ContextMenuHandler, MouseUpHandler {
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
- private HandlerRegistration doubleClickHandlerRegistration;
- private HandlerRegistration mouseUpHandlerRegistration;
- private HandlerRegistration contextMenuHandlerRegistration;
+public abstract class ClickEventHandler extends AbstractClickEventHandler {
- protected String clickEventIdentifier;
- protected Paintable paintable;
- private ApplicationConnection client;
+ public static final String CLICK_EVENT_IDENTIFIER = "click";
- public ClickEventHandler(Paintable paintable, String clickEventIdentifier) {
- this.paintable = paintable;
- this.clickEventIdentifier = clickEventIdentifier;
+ public ClickEventHandler(ComponentConnector connector) {
+ this(connector, CLICK_EVENT_IDENTIFIER);
}
- public void handleEventHandlerRegistration(ApplicationConnection client) {
- this.client = client;
- // Handle registering/unregistering of click handler depending on if
- // server side listeners have been added or removed.
- if (hasEventListener()) {
- if (mouseUpHandlerRegistration == null) {
- mouseUpHandlerRegistration = registerHandler(this,
- MouseUpEvent.getType());
- contextMenuHandlerRegistration = registerHandler(this,
- ContextMenuEvent.getType());
- doubleClickHandlerRegistration = registerHandler(this,
- DoubleClickEvent.getType());
- }
- } else {
- if (mouseUpHandlerRegistration != null) {
- // Remove existing handlers
- doubleClickHandlerRegistration.removeHandler();
- mouseUpHandlerRegistration.removeHandler();
- contextMenuHandlerRegistration.removeHandler();
-
- contextMenuHandlerRegistration = null;
- mouseUpHandlerRegistration = null;
- doubleClickHandlerRegistration = null;
-
- }
- }
-
- }
-
- protected abstract <H extends EventHandler> HandlerRegistration registerHandler(
- final H handler, DomEvent.Type<H> type);
-
- protected ApplicationConnection getApplicationConnection() {
- return client;
- }
-
- public boolean hasEventListener() {
- return getApplicationConnection().hasEventListeners(paintable,
- clickEventIdentifier);
+ public ClickEventHandler(ComponentConnector connector,
+ String clickEventIdentifier) {
+ super(connector, clickEventIdentifier);
}
+ /**
+ * Sends the click event based on the given native event. Delegates actual
+ * sending to {@link #fireClick(MouseEventDetails)}.
+ *
+ * @param event
+ * The native event that caused this click event
+ */
protected void fireClick(NativeEvent event) {
- ApplicationConnection client = getApplicationConnection();
- String pid = getApplicationConnection().getPid(paintable);
-
- MouseEventDetails mouseDetails = new MouseEventDetails(event,
- getRelativeToElement());
-
- Map<String, Object> parameters = new HashMap<String, Object>();
- parameters.put("mouseDetails", mouseDetails.serialize());
- client.updateVariable(pid, clickEventIdentifier, parameters, true);
-
- }
-
- public void onContextMenu(ContextMenuEvent event) {
- if (hasEventListener()) {
- // Prevent showing the browser's context menu when there is a right
- // click listener.
- event.preventDefault();
- }
-
- }
-
- public void onMouseUp(MouseUpEvent event) {
- // TODO For perfect accuracy we should check that a mousedown has
- // occured on this element before this mouseup and that no mouseup
- // has occured anywhere after that.
- if (hasEventListener()) {
- // "Click" with left, right or middle button
- fireClick(event.getNativeEvent());
- }
- }
-
- public void onDoubleClick(DoubleClickEvent event) {
- if (hasEventListener()) {
- fireClick(event.getNativeEvent());
- }
+ MouseEventDetails mouseDetails = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event, getRelativeToElement());
+ fireClick(event, mouseDetails);
}
/**
- * Click event calculates and returns coordinates relative to the element
- * returned by this method. Default implementation uses the root element of
- * the widget. Override to provide a different relative element.
+ * Sends the click event to the server. Must be implemented by sub classes,
+ * typically by calling an RPC method.
*
- * @return The Element used for calculating relative coordinates for a click
- * or null if no relative coordinates can be calculated.
+ * @param event
+ * The event that caused this click to be fired
+ *
+ * @param mouseDetails
+ * The mouse details for the event
*/
- protected Element getRelativeToElement() {
- if (paintable instanceof Widget) {
- return ((Widget) paintable).getElement();
- }
-
- return null;
- }
+ protected abstract void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails);
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java b/src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java
new file mode 100644
index 0000000000..37d6443f55
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java
@@ -0,0 +1,18 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public interface ClickRpc extends ServerRpc {
+ /**
+ * Called when a click event has occurred and there are server side
+ * listeners for the event.
+ *
+ * @param mouseDetails
+ * Details about the mouse when the event took place
+ */
+ public void click(MouseEventDetails mouseDetails);
+} \ No newline at end of file
diff --git a/src/com/vaadin/ui/ClientWidget.java b/src/com/vaadin/terminal/gwt/client/ui/Connect.java
index 8817f8f776..0581bdb99c 100644
--- a/src/com/vaadin/ui/ClientWidget.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/Connect.java
@@ -1,42 +1,39 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-/**
- *
- */
-package com.vaadin.ui;
+package com.vaadin.terminal.gwt.client.ui;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.server.ClientConnector;
import com.vaadin.terminal.gwt.widgetsetutils.CustomWidgetMapGenerator;
import com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator;
import com.vaadin.terminal.gwt.widgetsetutils.LazyWidgetMapGenerator;
import com.vaadin.terminal.gwt.widgetsetutils.WidgetMapGenerator;
/**
- * Annotation defining the default client side counterpart in GWT terminal for
- * {@link Component}.
+ * Annotation defining the server side connector that this ClientSideConnector
+ * should connect to. The value must always by a class extending
+ * {@link ClientConnector}.
* <p>
- * With this annotation server side Vaadin component is marked to have a client
- * side counterpart. The value of the annotation is the class of client side
+ * With this annotation client side Vaadin connector is marked to have a server
+ * side counterpart. The value of the annotation is the class of server side
* implementation.
- * <p>
- * Note, even though client side implementation is needed during development,
- * one may safely remove them from the classpath of the production server.
*
- * @since 6.2
+ * @since 7.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
-public @interface ClientWidget {
+public @interface Connect {
+
/**
- * @return the client side counterpart for the annotated component
+ * @return the server side counterpart for the annotated component connector
*/
- Class<? extends Paintable> value();
+ Class<? extends Connector> value();
/**
* Depending on the used WidgetMap generator, these optional hints may be
@@ -49,7 +46,7 @@ public @interface ClientWidget {
* is not included in the initial JavaScript application loaded when the
* application starts. Instead the implementation is loaded to the client
* when it is first needed. Lazy loaded widget can be achieved by giving
- * {@link LoadStyle#LAZY} value in ClientWidget annotation.
+ * {@link LoadStyle#LAZY} value in {@link Connect} annotation.
* <p>
* Lazy loaded widgets don't stress the size and startup time of the client
* side as much as eagerly loaded widgets. On the other hand there is a
@@ -93,5 +90,4 @@ public @interface ClientWidget {
*/
LAZY
}
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusElementPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusElementPanel.java
index 1b97406ae0..4984c4ce3b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/FocusElementPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/FocusElementPanel.java
@@ -13,11 +13,9 @@ 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;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
/**
* A panel that contains an always visible 0x0 size element that holds the focus
- * for all browsers but IE6.
*/
public class FocusElementPanel extends SimpleFocusablePanel {
@@ -30,22 +28,20 @@ public class FocusElementPanel extends SimpleFocusablePanel {
@Override
public void setWidget(Widget w) {
super.setWidget(w);
- if (!BrowserInfo.get().isIE6()) {
- if (focusElement.getParentElement() == null) {
- Style style = focusElement.getStyle();
- style.setPosition(Position.FIXED);
- style.setTop(0, Unit.PX);
- 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);
- // revert to original, not focusable
- getElement().setPropertyObject("tabIndex", null);
- } else {
- moveFocusElementAfterWidget();
- }
+ if (focusElement.getParentElement() == null) {
+ Style style = focusElement.getStyle();
+ style.setPosition(Position.FIXED);
+ style.setTop(0, Unit.PX);
+ 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);
+ // revert to original, not focusable
+ getElement().setPropertyObject("tabIndex", null);
+ } else {
+ moveFocusElementAfterWidget();
}
}
@@ -58,28 +54,20 @@ public class FocusElementPanel extends SimpleFocusablePanel {
@Override
public void setFocus(boolean focus) {
- if (BrowserInfo.get().isIE6()) {
- super.setFocus(focus);
+ if (focus) {
+ FocusImpl.getFocusImplForPanel().focus(
+ (Element) focusElement.cast());
} else {
- if (focus) {
- FocusImpl.getFocusImplForPanel().focus(
- (Element) focusElement.cast());
- } else {
- FocusImpl.getFocusImplForPanel().blur(
- (Element) focusElement.cast());
- }
+ FocusImpl.getFocusImplForPanel()
+ .blur((Element) focusElement.cast());
}
}
@Override
public void setTabIndex(int tabIndex) {
- if (BrowserInfo.get().isIE6()) {
- super.setTabIndex(tabIndex);
- } else {
- getElement().setTabIndex(-1);
- if (focusElement != null) {
- focusElement.setTabIndex(tabIndex);
- }
+ getElement().setTabIndex(-1);
+ if (focusElement != null) {
+ focusElement.setTabIndex(tabIndex);
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
index 96cb4b8a35..533d6a78ae 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
@@ -24,7 +24,6 @@ import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.VConsole;
/**
* A scrollhandlers similar to {@link ScrollPanel}.
@@ -60,18 +59,9 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
if (useFakeFocusElement()) {
if (focusElement.getParentElement() == null) {
Style style = focusElement.getStyle();
- if (BrowserInfo.get().isIE6()) {
- style.setOverflow(Overflow.HIDDEN);
- style.setHeight(0, Unit.PX);
- style.setWidth(0, Unit.PX);
- style.setPosition(Position.ABSOLUTE);
-
- addScrollHandler(this);
- } else {
- style.setPosition(Position.FIXED);
- style.setTop(0, Unit.PX);
- style.setLeft(0, Unit.PX);
- }
+ style.setPosition(Position.FIXED);
+ style.setTop(0, Unit.PX);
+ style.setLeft(0, Unit.PX);
getElement().appendChild(focusElement);
/* Sink from focusElemet too as focusa and blur don't bubble */
DOM.sinkEvents(
diff --git a/src/com/vaadin/terminal/gwt/client/ui/Icon.java b/src/com/vaadin/terminal/gwt/client/ui/Icon.java
index fd2229fc8d..b64605aac9 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/Icon.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/Icon.java
@@ -19,7 +19,6 @@ public class Icon extends UIObject {
DOM.setElementProperty(getElement(), "alt", "");
setStyleName(CLASSNAME);
this.client = client;
- client.addPngFix(getElement());
}
public Icon(ApplicationConnection client, String uidlUri) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java
index 3a4048907f..7a5d85e34b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java
@@ -3,39 +3,37 @@
*/
package com.vaadin.terminal.gwt.client.ui;
-import java.util.HashMap;
-import java.util.Map;
-
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.Element;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
+
+public abstract class LayoutClickEventHandler extends AbstractClickEventHandler {
-public abstract class LayoutClickEventHandler extends ClickEventHandler {
+ public static final String LAYOUT_CLICK_EVENT_IDENTIFIER = "lClick";
- public LayoutClickEventHandler(Paintable paintable,
+ public LayoutClickEventHandler(ComponentConnector connector) {
+ this(connector, LAYOUT_CLICK_EVENT_IDENTIFIER);
+ }
+
+ public LayoutClickEventHandler(ComponentConnector connector,
String clickEventIdentifier) {
- super(paintable, clickEventIdentifier);
+ super(connector, clickEventIdentifier);
}
- protected abstract Paintable getChildComponent(Element element);
+ protected abstract ComponentConnector getChildComponent(Element element);
+
+ protected ComponentConnector getChildComponent(NativeEvent event) {
+ return getChildComponent((Element) event.getEventTarget().cast());
+ }
@Override
protected void fireClick(NativeEvent event) {
- ApplicationConnection client = getApplicationConnection();
- String pid = getApplicationConnection().getPid(paintable);
-
- MouseEventDetails mouseDetails = new MouseEventDetails(event,
- getRelativeToElement());
- Paintable childComponent = getChildComponent((Element) event
- .getEventTarget().cast());
-
- Map<String, Object> parameters = new HashMap<String, Object>();
- parameters.put("mouseDetails", mouseDetails.serialize());
- parameters.put("component", childComponent);
-
- client.updateVariable(pid, clickEventIdentifier, parameters, true);
+ MouseEventDetails mouseDetails = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event, getRelativeToElement());
+ getLayoutClickRPC().layoutClick(mouseDetails, getChildComponent(event));
}
+ protected abstract LayoutClickRpc getLayoutClickRPC();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java
new file mode 100644
index 0000000000..5b76f398a9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java
@@ -0,0 +1,22 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public interface LayoutClickRpc extends ServerRpc {
+ /**
+ * Called when a layout click event has occurred and there are server side
+ * listeners for the event.
+ *
+ * @param mouseDetails
+ * Details about the mouse when the event took place
+ * @param clickedConnector
+ * The child component that was the target of the event
+ */
+ public void layoutClick(MouseEventDetails mouseDetails,
+ Connector clickedConnector);
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/ManagedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/ManagedLayout.java
new file mode 100644
index 0000000000..6d0271ee40
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/ManagedLayout.java
@@ -0,0 +1,10 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+
+public interface ManagedLayout extends ComponentConnector {
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java b/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java
new file mode 100644
index 0000000000..1e067bf6fb
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java
@@ -0,0 +1,130 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+
+public abstract class MediaBaseConnector extends AbstractComponentConnector
+ implements Paintable {
+
+ public static final String TAG_SOURCE = "src";
+
+ public static final String ATTR_MUTED = "muted";
+ public static final String ATTR_CONTROLS = "ctrl";
+ public static final String ATTR_AUTOPLAY = "auto";
+ public static final String ATTR_RESOURCE = "res";
+ public static final String ATTR_RESOURCE_TYPE = "type";
+ public static final String ATTR_HTML = "html";
+ public static final String ATTR_ALT_TEXT = "alt";
+
+ /**
+ * Server to client RPC interface for controlling playback of the media.
+ *
+ * @since 7.0
+ */
+ public static interface MediaControl extends ClientRpc {
+ /**
+ * Start playing the media.
+ */
+ public void play();
+
+ /**
+ * Pause playback of the media.
+ */
+ public void pause();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ registerRpc(MediaControl.class, new MediaControl() {
+ public void play() {
+ getWidget().play();
+ }
+
+ public void pause() {
+ getWidget().pause();
+ }
+ });
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().setControls(shouldShowControls(uidl));
+ getWidget().setAutoplay(shouldAutoplay(uidl));
+ getWidget().setMuted(isMediaMuted(uidl));
+
+ // Add all sources
+ for (int ix = 0; ix < uidl.getChildCount(); ix++) {
+ UIDL child = uidl.getChildUIDL(ix);
+ if (TAG_SOURCE.equals(child.getTag())) {
+ getWidget()
+ .addSource(getSourceUrl(child), getSourceType(child));
+ }
+ }
+ setAltText(uidl);
+ }
+
+ protected boolean shouldShowControls(UIDL uidl) {
+ return uidl.getBooleanAttribute(ATTR_CONTROLS);
+ }
+
+ private boolean shouldAutoplay(UIDL uidl) {
+ return uidl.getBooleanAttribute(ATTR_AUTOPLAY);
+ }
+
+ private boolean isMediaMuted(UIDL uidl) {
+ return uidl.getBooleanAttribute(ATTR_MUTED);
+ }
+
+ private boolean allowHtmlContent(UIDL uidl) {
+ return uidl.getBooleanAttribute(ATTR_HTML);
+ }
+
+ @Override
+ public VMediaBase getWidget() {
+ return (VMediaBase) super.getWidget();
+ }
+
+ /**
+ * @param uidl
+ * @return the URL of a resource to be used as a source for the media
+ */
+ private String getSourceUrl(UIDL uidl) {
+ String url = getConnection().translateVaadinUri(
+ uidl.getStringAttribute(MediaBaseConnector.ATTR_RESOURCE));
+ if (url == null) {
+ return "";
+ }
+ return url;
+ }
+
+ /**
+ * @param uidl
+ * @return the mime type of the media
+ */
+ private String getSourceType(UIDL uidl) {
+ return uidl.getStringAttribute(MediaBaseConnector.ATTR_RESOURCE_TYPE);
+ }
+
+ private void setAltText(UIDL uidl) {
+ String alt = uidl.getStringAttribute(MediaBaseConnector.ATTR_ALT_TEXT);
+
+ if (alt == null || "".equals(alt)) {
+ alt = getWidget().getDefaultAltHtml();
+ } else if (!allowHtmlContent(uidl)) {
+ alt = Util.escapeHTML(alt);
+ }
+ getWidget().setAltText(alt);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/PostLayoutListener.java b/src/com/vaadin/terminal/gwt/client/ui/PostLayoutListener.java
new file mode 100644
index 0000000000..feb7494f87
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/PostLayoutListener.java
@@ -0,0 +1,8 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+public interface PostLayoutListener {
+ public void postLayout();
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/ShortcutActionHandler.java b/src/com/vaadin/terminal/gwt/client/ui/ShortcutActionHandler.java
index 934fcaa8b7..37e9ab4a69 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/ShortcutActionHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/ShortcutActionHandler.java
@@ -15,11 +15,9 @@ import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.KeyboardListenerCollection;
-import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea;
@@ -34,9 +32,8 @@ import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea;
public class ShortcutActionHandler {
/**
- * An interface implemented by those users (most often {@link Container}s,
- * but HasWidgets at least) of this helper class that want to support
- * special components like {@link VRichTextArea} that don't properly
+ * An interface implemented by those users of this helper class that want to
+ * support special components like {@link VRichTextArea} that don't properly
* propagate key down events. Those components can build support for
* shortcut actions by traversing the closest
* {@link ShortcutActionHandlerOwner} from the component hierarchy an
@@ -52,12 +49,12 @@ public class ShortcutActionHandler {
}
/**
- * A focusable {@link Paintable} implementing this interface will be
- * notified before shortcut actions are handled if it will be the target of
- * the action (most commonly means it is the focused component during the
+ * A focusable {@link ComponentConnector} implementing this interface will
+ * be notified before shortcut actions are handled if it will be the target
+ * of the action (most commonly means it is the focused component during the
* keyboard combination is triggered by the user).
*/
- public interface BeforeShortcutActionListener extends Paintable {
+ public interface BeforeShortcutActionListener extends ComponentConnector {
/**
* This method is called by ShortcutActionHandler before firing the
* shortcut if the Paintable is currently focused (aka the target of the
@@ -111,7 +108,7 @@ public class ShortcutActionHandler {
}
}
- public void handleKeyboardEvent(final Event event, Paintable target) {
+ public void handleKeyboardEvent(final Event event, ComponentConnector target) {
final int modifiers = KeyboardListenerCollection
.getKeyboardModifiers(event);
final char keyCode = (char) DOM.eventGetKeyCode(event);
@@ -133,16 +130,12 @@ public class ShortcutActionHandler {
}
private void fireAction(final Event event, final ShortcutAction a,
- Paintable target) {
+ ComponentConnector target) {
final Element et = DOM.eventGetTarget(event);
if (target == null) {
- Widget w = Util.findWidget(et, null);
- while (w != null && !(w instanceof Paintable)) {
- w = w.getParent();
- }
- target = (Paintable) w;
+ target = Util.findPaintable(client, et);
}
- final Paintable finalTarget = target;
+ final ComponentConnector finalTarget = target;
event.preventDefault();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/SimpleManagedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/SimpleManagedLayout.java
new file mode 100644
index 0000000000..9ccb29a750
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/SimpleManagedLayout.java
@@ -0,0 +1,8 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+public interface SimpleManagedLayout extends ManagedLayout {
+ public void layout();
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/TabIndexState.java b/src/com/vaadin/terminal/gwt/client/ui/TabIndexState.java
new file mode 100644
index 0000000000..7ffb328add
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/TabIndexState.java
@@ -0,0 +1,29 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+/**
+ * Interface implemented by state classes that support tab indexes.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public interface TabIndexState {
+ /**
+ * Gets the <i>tabulator index</i> of the field.
+ *
+ * @return the tab index for the Field
+ */
+ public int getTabIndex();
+
+ /**
+ * Sets the <i>tabulator index</i> of the field.
+ *
+ * @param tabIndex
+ * the tab index to set
+ */
+ public void setTabIndex(int tabIndex);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/Table.java b/src/com/vaadin/terminal/gwt/client/ui/Table.java
deleted file mode 100644
index 4b3ba0422e..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/Table.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.user.client.ui.HasWidgets;
-import com.vaadin.terminal.gwt.client.Paintable;
-
-public interface Table extends Paintable, HasWidgets {
- final int SELECT_MODE_NONE = 0;
- final int SELECT_MODE_SINGLE = 1;
- final int SELECT_MODE_MULTI = 2;
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/UnknownComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/UnknownComponentConnector.java
new file mode 100644
index 0000000000..94eff44eee
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/UnknownComponentConnector.java
@@ -0,0 +1,39 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+
+public class UnknownComponentConnector extends AbstractComponentConnector {
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VUnknownComponent.class);
+ }
+
+ @Override
+ public VUnknownComponent getWidget() {
+ return (VUnknownComponent) super.getWidget();
+ }
+
+ public void setServerSideClassName(String serverClassName) {
+ getWidget()
+ .setCaption(
+ "Widgetset does not contain implementation for "
+ + serverClassName
+ + ". Check its component connector's @Connect mapping, widgetsets "
+ + "GWT module description file and re-compile your"
+ + " widgetset. In case you have downloaded a vaadin"
+ + " add-on package, you might want to refer to "
+ + "<a href='http://vaadin.com/using-addons'>add-on "
+ + "instructions</a>.");
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java
deleted file mode 100644
index a6abc411f8..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Style;
-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.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;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VCaption;
-import com.vaadin.terminal.gwt.client.VConsole;
-
-public class VAbsoluteLayout extends ComplexPanel implements Container {
-
- /** Tag name for widget creation */
- public static final String TAGNAME = "absolutelayout";
-
- /** Class name, prefix in styling */
- public static final String CLASSNAME = "v-absolutelayout";
-
- private DivElement marginElement;
-
- protected final Element canvas = DOM.createDiv();
-
- // private int excessPixelsHorizontal;
- //
- // private int excessPixelsVertical;
-
- private Object previousStyleName;
-
- private Map<String, AbsoluteWrapper> pidToComponentWrappper = new HashMap<String, AbsoluteWrapper>();
-
- protected ApplicationConnection client;
-
- private boolean rendering;
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this, EventId.LAYOUT_CLICK) {
-
- @Override
- protected Paintable getChildComponent(Element element) {
- return getComponent(element);
- }
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- public VAbsoluteLayout() {
- setElement(Document.get().createDivElement());
- setStyleName(CLASSNAME);
- marginElement = Document.get().createDivElement();
- canvas.getStyle().setProperty("position", "relative");
- canvas.getStyle().setProperty("overflow", "hidden");
- marginElement.appendChild(canvas);
- getElement().appendChild(marginElement);
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- // TODO needs some special handling for components with only on edge
- // horizontally or vertically defined
- AbsoluteWrapper wrapper = (AbsoluteWrapper) child.getParent();
- int w;
- if (wrapper.left != null && wrapper.right != null) {
- w = wrapper.getOffsetWidth();
- } else if (wrapper.right != null) {
- // left == null
- // available width == right edge == offsetleft + width
- w = wrapper.getOffsetWidth() + wrapper.getElement().getOffsetLeft();
- } else {
- // left != null && right == null || left == null &&
- // right == null
- // available width == canvas width - offset left
- w = canvas.getOffsetWidth() - wrapper.getElement().getOffsetLeft();
- }
- int h;
- if (wrapper.top != null && wrapper.bottom != null) {
- h = wrapper.getOffsetHeight();
- } else if (wrapper.bottom != null) {
- // top not defined, available space 0... bottom of wrapper
- h = wrapper.getElement().getOffsetTop() + wrapper.getOffsetHeight();
- } else {
- // top defined or both undefined, available space == canvas - top
- h = canvas.getOffsetHeight() - wrapper.getElement().getOffsetTop();
- }
-
- return new RenderSpace(w, h);
- }
-
- public boolean hasChildComponent(Widget component) {
- for (Iterator<Entry<String, AbsoluteWrapper>> iterator = pidToComponentWrappper
- .entrySet().iterator(); iterator.hasNext();) {
- if (iterator.next().getValue().paintable == component) {
- return true;
- }
- }
- return false;
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- for (Widget wrapper : getChildren()) {
- AbsoluteWrapper w = (AbsoluteWrapper) wrapper;
- if (w.getWidget() == oldComponent) {
- w.setWidget(newComponent);
- return;
- }
- }
- }
-
- public boolean requestLayout(Set<Paintable> children) {
- // component inside an absolute panel never affects parent nor the
- // layout
- return true;
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- AbsoluteWrapper parent2 = (AbsoluteWrapper) ((Widget) component)
- .getParent();
- parent2.updateCaption(uidl);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- this.client = client;
- // TODO margin handling
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- HashSet<String> unrenderedPids = new HashSet<String>(
- pidToComponentWrappper.keySet());
-
- for (Iterator<Object> childIterator = uidl.getChildIterator(); childIterator
- .hasNext();) {
- UIDL cc = (UIDL) childIterator.next();
- if (cc.getTag().equals("cc")) {
- UIDL componentUIDL = cc.getChildUIDL(0);
- unrenderedPids.remove(componentUIDL.getId());
- getWrapper(client, componentUIDL).updateFromUIDL(cc);
- }
- }
-
- for (String pid : unrenderedPids) {
- AbsoluteWrapper absoluteWrapper = pidToComponentWrappper.get(pid);
- pidToComponentWrappper.remove(pid);
- absoluteWrapper.destroy();
- }
- rendering = false;
- }
-
- private AbsoluteWrapper getWrapper(ApplicationConnection client,
- UIDL componentUIDL) {
- AbsoluteWrapper wrapper = pidToComponentWrappper.get(componentUIDL
- .getId());
- if (wrapper == null) {
- wrapper = new AbsoluteWrapper(client.getPaintable(componentUIDL));
- pidToComponentWrappper.put(componentUIDL.getId(), wrapper);
- add(wrapper);
- }
- return wrapper;
-
- }
-
- @Override
- public void add(Widget child) {
- super.add(child, canvas);
- }
-
- @Override
- public void setStyleName(String style) {
- super.setStyleName(style);
- if (previousStyleName == null || !previousStyleName.equals(style)) {
- // excessPixelsHorizontal = -1;
- // excessPixelsVertical = -1;
- }
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- // TODO do this so that canvas gets the sized properly (the area
- // inside marginals)
- canvas.getStyle().setProperty("width", width);
-
- if (!rendering) {
- if (BrowserInfo.get().isIE6()) {
- relayoutWrappersForIe6();
- }
- relayoutRelativeChildren();
- }
- }
-
- private void relayoutRelativeChildren() {
- for (Widget widget : getChildren()) {
- if (widget instanceof AbsoluteWrapper) {
- AbsoluteWrapper w = (AbsoluteWrapper) widget;
- client.handleComponentRelativeSize(w.getWidget());
- w.updateCaptionPosition();
- }
- }
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- // TODO do this so that canvas gets the sized properly (the area
- // inside marginals)
- canvas.getStyle().setProperty("height", height);
-
- if (!rendering) {
- if (BrowserInfo.get().isIE6()) {
- relayoutWrappersForIe6();
- }
- relayoutRelativeChildren();
- }
- }
-
- private void relayoutWrappersForIe6() {
- for (Widget wrapper : getChildren()) {
- if (wrapper instanceof AbsoluteWrapper) {
- ((AbsoluteWrapper) wrapper).ie6Layout();
- }
- }
- }
-
- public class AbsoluteWrapper extends SimplePanel {
- private String css;
- private String left;
- private String top;
- private String right;
- private String bottom;
- private String zIndex;
-
- private Paintable paintable;
- private VCaption caption;
-
- public AbsoluteWrapper(Paintable paintable) {
- this.paintable = paintable;
- setStyleName(CLASSNAME + "-wrapper");
- }
-
- public void updateCaption(UIDL uidl) {
-
- boolean captionIsNeeded = VCaption.isNeeded(uidl);
- if (captionIsNeeded) {
- if (caption == null) {
- caption = new VCaption(paintable, client);
- VAbsoluteLayout.this.add(caption);
- }
- caption.updateCaption(uidl);
- updateCaptionPosition();
- } else {
- if (caption != null) {
- caption.removeFromParent();
- caption = null;
- }
- }
- }
-
- @Override
- public void setWidget(Widget w) {
- // this fixes #5457 (Widget implementation can change on-the-fly)
- paintable = (Paintable) w;
- super.setWidget(w);
- }
-
- public void destroy() {
- if (caption != null) {
- caption.removeFromParent();
- }
- client.unregisterPaintable(paintable);
- removeFromParent();
- }
-
- public void updateFromUIDL(UIDL componentUIDL) {
- setPosition(componentUIDL.getStringAttribute("css"));
- if (getWidget() != paintable) {
- setWidget((Widget) paintable);
- }
- UIDL childUIDL = componentUIDL.getChildUIDL(0);
- paintable.updateFromUIDL(childUIDL, client);
- if (childUIDL.hasAttribute("cached")) {
- // child may need relative size adjustment if wrapper details
- // have changed this could be optimized (check if wrapper size
- // has changed)
- client.handleComponentRelativeSize((Widget) paintable);
- }
- }
-
- public void setPosition(String stringAttribute) {
- if (css == null || !css.equals(stringAttribute)) {
- css = stringAttribute;
- top = right = bottom = left = zIndex = null;
- if (!css.equals("")) {
- String[] properties = css.split(";");
- for (int i = 0; i < properties.length; i++) {
- String[] keyValue = properties[i].split(":");
- if (keyValue[0].equals("left")) {
- left = keyValue[1];
- } else if (keyValue[0].equals("top")) {
- top = keyValue[1];
- } else if (keyValue[0].equals("right")) {
- right = keyValue[1];
- } else if (keyValue[0].equals("bottom")) {
- bottom = keyValue[1];
- } else if (keyValue[0].equals("z-index")) {
- zIndex = keyValue[1];
- }
- }
- }
- // ensure ne values
- Style style = getElement().getStyle();
- /*
- * IE8 dies when nulling zIndex, even in IE7 mode. All other css
- * properties (and even in older IE's) accept null values just
- * fine. Assign empty string instead of null.
- */
- if (zIndex != null) {
- style.setProperty("zIndex", zIndex);
- } else {
- style.setProperty("zIndex", "");
- }
- style.setProperty("top", top);
- style.setProperty("left", left);
- style.setProperty("right", right);
- style.setProperty("bottom", bottom);
-
- if (BrowserInfo.get().isIE6()) {
- ie6Layout();
- }
- }
- updateCaptionPosition();
- }
-
- private void updateCaptionPosition() {
- if (caption != null) {
- Style style = caption.getElement().getStyle();
- style.setProperty("position", "absolute");
- style.setPropertyPx("left", getElement().getOffsetLeft());
- style.setPropertyPx("top", getElement().getOffsetTop()
- - caption.getHeight());
- }
- }
-
- private void ie6Layout() {
- // special handling for IE6 is needed, it does not support
- // setting both left/right or top/bottom
- Style style = getElement().getStyle();
- if (bottom != null && top != null) {
- // define height for wrapper to simulate bottom property
- int bottompixels = measureForIE6(bottom, true);
- VConsole.log("ALB" + bottompixels);
- int height = canvas.getOffsetHeight() - bottompixels
- - getElement().getOffsetTop();
- VConsole.log("ALB" + height);
- if (height < 0) {
- height = 0;
- }
- style.setPropertyPx("height", height);
- } else {
- // reset possibly existing value
- style.setProperty("height", "");
- }
- if (left != null && right != null) {
- // define width for wrapper to simulate right property
- int rightPixels = measureForIE6(right, false);
- VConsole.log("ALR" + rightPixels);
- int width = canvas.getOffsetWidth() - rightPixels
- - getElement().getOffsetLeft();
- VConsole.log("ALR" + width);
- if (width < 0) {
- width = 0;
- }
- style.setPropertyPx("width", width);
- } else {
- // reset possibly existing value
- style.setProperty("width", "");
- }
- }
-
- }
-
- private Element measureElement;
-
- private int measureForIE6(String cssLength, boolean vertical) {
- if (measureElement == null) {
- measureElement = DOM.createDiv();
- measureElement.getStyle().setProperty("position", "absolute");
- canvas.appendChild(measureElement);
- }
- if (vertical) {
- measureElement.getStyle().setProperty("height", cssLength);
- return measureElement.getOffsetHeight();
- } else {
- measureElement.getStyle().setProperty("width", cssLength);
- return measureElement.getOffsetWidth();
- }
- }
-
- /**
- * 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.
- */
- private Paintable getComponent(Element element) {
- return Util.getPaintableForElement(client, this, element);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java b/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java
deleted file mode 100644
index b1eda728dc..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.dom.client.InputElement;
-import com.google.gwt.dom.client.LabelElement;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-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.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.EventHelper;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements
- Paintable, Field, FocusHandler, BlurHandler {
-
- public static final String CLASSNAME = "v-checkbox";
-
- String id;
-
- boolean immediate;
-
- ApplicationConnection client;
-
- private Element errorIndicatorElement;
-
- private Icon icon;
-
- private HandlerRegistration focusHandlerRegistration;
- private HandlerRegistration blurHandlerRegistration;
-
- public VCheckBox() {
- setStyleName(CLASSNAME);
- addClickHandler(new ClickHandler() {
-
- public void onClick(ClickEvent event) {
- if (id == null || client == null || !isEnabled()) {
- return;
- }
-
- // Add mouse details
- MouseEventDetails details = new MouseEventDetails(
- event.getNativeEvent(), getElement());
- client.updateVariable(id, "mousedetails", details.serialize(),
- false);
- client.updateVariable(id, "state", getValue(), immediate);
- }
-
- });
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- Element el = DOM.getFirstChild(getElement());
- while (el != null) {
- DOM.sinkEvents(el,
- (DOM.getEventsSunk(el) | VTooltip.TOOLTIP_EVENTS));
- el = DOM.getNextSibling(el);
- }
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Save details
- this.client = client;
- id = uidl.getId();
-
- // Ensure correct implementation
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
-
- focusHandlerRegistration = EventHelper.updateFocusHandler(this, client,
- focusHandlerRegistration);
- blurHandlerRegistration = EventHelper.updateBlurHandler(this, client,
- blurHandlerRegistration);
-
- if (uidl.hasAttribute("error")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createSpan();
- errorIndicatorElement.setInnerHTML("&nbsp;");
- DOM.setElementProperty(errorIndicatorElement, "className",
- "v-errorindicator");
- DOM.appendChild(getElement(), errorIndicatorElement);
- DOM.sinkEvents(errorIndicatorElement, VTooltip.TOOLTIP_EVENTS
- | Event.ONCLICK);
- } else {
- DOM.setStyleAttribute(errorIndicatorElement, "display", "");
- }
- } else if (errorIndicatorElement != null) {
- DOM.setStyleAttribute(errorIndicatorElement, "display", "none");
- }
-
- if (uidl.hasAttribute("readonly")) {
- setEnabled(false);
- }
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
- DOM.insertChild(getElement(), icon.getElement(), 1);
- icon.sinkEvents(VTooltip.TOOLTIP_EVENTS);
- icon.sinkEvents(Event.ONCLICK);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- } else if (icon != null) {
- // detach icon
- DOM.removeChild(getElement(), icon.getElement());
- icon = null;
- }
-
- // Set text
- setText(uidl.getStringAttribute("caption"));
- setValue(uidl.getBooleanVariable("state"));
- immediate = uidl.getBooleanAttribute("immediate");
- }
-
- @Override
- public void setText(String text) {
- super.setText(text);
- if (BrowserInfo.get().isIE() && BrowserInfo.get().getIEVersion() < 8) {
-
- boolean breakLink = text == null || "".equals(text);
-
- // break or create link between label element and checkbox, to
- // enable native focus outline around checkbox element itself, if
- // caption is not present
- NodeList<Node> childNodes = getElement().getChildNodes();
- String id = null;
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.getItem(i);
- if (item.getNodeName().toLowerCase().equals("input")) {
- InputElement input = (InputElement) item;
- id = input.getId();
- }
- if (item.getNodeName().toLowerCase().equals("label")) {
- LabelElement label = (LabelElement) item;
- if (breakLink) {
- label.setHtmlFor("");
- } else {
- label.setHtmlFor(id);
- }
- }
- }
- }
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- if (icon != null && (event.getTypeInt() == Event.ONCLICK)
- && (DOM.eventGetTarget(event) == icon.getElement())) {
- // Click on icon should do nothing if widget is disabled
- if (isEnabled()) {
- setValue(!getValue());
- }
- }
- super.onBrowserEvent(event);
- if (event.getTypeInt() == Event.ONLOAD) {
- Util.notifyParentOfSizeChange(this, true);
- }
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- }
-
- public void onFocus(FocusEvent arg0) {
- client.updateVariable(id, EventId.FOCUS, "", true);
- }
-
- public void onBlur(BlurEvent arg0) {
- client.updateVariable(id, EventId.BLUR, "", true);
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java b/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java
index 8158fd90c2..692e13bd94 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java
@@ -31,7 +31,6 @@ import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.impl.FocusImpl;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.Focusable;
import com.vaadin.terminal.gwt.client.Util;
@@ -45,8 +44,8 @@ public class VContextMenu extends VOverlay implements SubPartAware {
private int top;
- private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(
- 100, new ScheduledCommand() {
+ private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(100,
+ new ScheduledCommand() {
public void execute() {
imagesLoaded();
}
@@ -218,11 +217,6 @@ public class VContextMenu extends VOverlay implements SubPartAware {
public void onLoad(LoadEvent event) {
// Handle icon onload events to ensure shadow is resized correctly
- if (BrowserInfo.get().isIE6()) {
- // Ensure PNG transparency works in IE6
- Util.doIE6PngFix((Element) Element.as(event.getNativeEvent()
- .getEventTarget()));
- }
delayedImageLoadExecutioner.trigger();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java
deleted file mode 100644
index 585180f8f4..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import com.google.gwt.dom.client.Style;
-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.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.StyleConstants;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VCaption;
-import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.ValueMap;
-
-public class VCssLayout extends SimplePanel implements Paintable, Container {
- public static final String TAGNAME = "csslayout";
- public static final String CLASSNAME = "v-" + TAGNAME;
-
- private FlowPane panel = new FlowPane();
-
- private Element margin = DOM.createDiv();
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this, EventId.LAYOUT_CLICK) {
-
- @Override
- protected Paintable getChildComponent(Element element) {
- return panel.getComponent(element);
- }
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- private boolean hasHeight;
- private boolean hasWidth;
- private boolean rendering;
-
- public VCssLayout() {
- super();
- getElement().appendChild(margin);
- setStyleName(CLASSNAME);
- margin.setClassName(CLASSNAME + "-margin");
- setWidget(panel);
- }
-
- @Override
- protected Element getContainerElement() {
- return margin;
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- // panel.setWidth(width);
- hasWidth = width != null && !width.equals("");
- if (!rendering) {
- panel.updateRelativeSizes();
- }
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- // panel.setHeight(height);
- hasHeight = height != null && !height.equals("");
- if (!rendering) {
- panel.updateRelativeSizes();
- }
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
- clickEventHandler.handleEventHandlerRegistration(client);
-
- final VMarginInfo margins = new VMarginInfo(
- uidl.getIntAttribute("margins"));
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP,
- margins.hasTop());
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT,
- margins.hasRight());
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM,
- margins.hasBottom());
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT,
- margins.hasLeft());
-
- setStyleName(margin, CLASSNAME + "-" + "spacing",
- uidl.hasAttribute("spacing"));
- panel.updateFromUIDL(uidl, client);
- rendering = false;
- }
-
- public boolean hasChildComponent(Widget component) {
- return panel.hasChildComponent(component);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- panel.replaceChildComponent(oldComponent, newComponent);
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- panel.updateCaption(component, uidl);
- }
-
- public class FlowPane extends FlowPanel {
-
- private final HashMap<Widget, VCaption> widgetToCaption = new HashMap<Widget, VCaption>();
- private ApplicationConnection client;
- private int lastIndex;
-
- public FlowPane() {
- super();
- setStyleName(CLASSNAME + "-container");
- }
-
- public void updateRelativeSizes() {
- for (Widget w : getChildren()) {
- if (w instanceof Paintable) {
- client.handleComponentRelativeSize(w);
- }
- }
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- // for later requests
- this.client = client;
-
- final Collection<Widget> oldWidgets = new HashSet<Widget>();
- for (final Iterator<Widget> iterator = iterator(); iterator
- .hasNext();) {
- oldWidgets.add(iterator.next());
- }
-
- ValueMap mapAttribute = null;
- if (uidl.hasAttribute("css")) {
- mapAttribute = uidl.getMapAttribute("css");
- }
-
- lastIndex = 0;
- for (final Iterator<Object> i = uidl.getChildIterator(); i
- .hasNext();) {
- final UIDL r = (UIDL) i.next();
- final Paintable child = client.getPaintable(r);
- final Widget widget = (Widget) child;
- if (widget.getParent() == this) {
- oldWidgets.remove(child);
- VCaption vCaption = widgetToCaption.get(child);
- if (vCaption != null) {
- addOrMove(vCaption, lastIndex++);
- oldWidgets.remove(vCaption);
- }
- }
-
- addOrMove(widget, lastIndex++);
- if (mapAttribute != null && mapAttribute.containsKey(r.getId())) {
- String css = null;
- try {
- Style style = widget.getElement().getStyle();
- css = mapAttribute.getString(r.getId());
- String[] cssRules = css.split(";");
- for (int j = 0; j < cssRules.length; j++) {
- String[] rule = cssRules[j].split(":");
- if (rule.length == 0) {
- continue;
- } else {
- style.setProperty(
- makeCamelCase(rule[0].trim()),
- rule[1].trim());
- }
- }
- } catch (Exception e) {
- VConsole.log("CssLayout encounterd invalid css string: "
- + css);
- }
- }
-
- if (!r.getBooleanAttribute("cached")) {
- child.updateFromUIDL(r, client);
- }
- }
-
- // loop oldWidgetWrappers that where not re-attached and unregister
- // them
- for (Widget w : oldWidgets) {
- remove(w);
- if (w instanceof Paintable) {
- final Paintable p = (Paintable) w;
- client.unregisterPaintable(p);
- }
- VCaption vCaption = widgetToCaption.remove(w);
- if (vCaption != null) {
- remove(vCaption);
- }
- }
- }
-
- private void addOrMove(Widget child, int index) {
- if (child.getParent() == this) {
- int currentIndex = getWidgetIndex(child);
- if (index == currentIndex) {
- return;
- }
- }
- insert(child, index);
- }
-
- public boolean hasChildComponent(Widget component) {
- return component.getParent() == this;
- }
-
- public void replaceChildComponent(Widget oldComponent,
- Widget newComponent) {
- VCaption caption = widgetToCaption.get(oldComponent);
- if (caption != null) {
- remove(caption);
- widgetToCaption.remove(oldComponent);
- }
- int index = getWidgetIndex(oldComponent);
- if (index >= 0) {
- remove(oldComponent);
- insert(newComponent, index);
- }
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- VCaption caption = widgetToCaption.get(component);
- if (VCaption.isNeeded(uidl)) {
- Widget widget = (Widget) component;
- if (caption == null) {
- caption = new VCaption(component, client);
- widgetToCaption.put(widget, caption);
- insert(caption, getWidgetIndex(widget));
- lastIndex++;
- } else if (!caption.isAttached()) {
- insert(caption, getWidgetIndex(widget));
- lastIndex++;
- }
- caption.updateCaption(uidl);
- } else if (caption != null) {
- remove(caption);
- widgetToCaption.remove(component);
- }
- }
-
- private Paintable getComponent(Element element) {
- return Util
- .getPaintableForElement(client, VCssLayout.this, element);
- }
-
- }
-
- private RenderSpace space;
-
- public RenderSpace getAllocatedSpace(Widget child) {
- if (space == null) {
- space = new RenderSpace(-1, -1) {
- @Override
- public int getWidth() {
- if (BrowserInfo.get().isIE()) {
- int width = getOffsetWidth();
- int margins = margin.getOffsetWidth()
- - panel.getOffsetWidth();
- return width - margins;
- } else {
- return panel.getOffsetWidth();
- }
- }
-
- @Override
- public int getHeight() {
- int height = getOffsetHeight();
- int margins = margin.getOffsetHeight()
- - panel.getOffsetHeight();
- return height - margins;
- }
- };
- }
- return space;
- }
-
- public boolean requestLayout(Set<Paintable> children) {
- if (hasSize()) {
- return true;
- } else {
- // Size may have changed
- // TODO optimize this: cache size if not fixed, handle both width
- // and height separately
- return false;
- }
- }
-
- private boolean hasSize() {
- return hasWidth && hasHeight;
- }
-
- private static final String makeCamelCase(String cssProperty) {
- // TODO this might be cleaner to implement with regexp
- while (cssProperty.contains("-")) {
- int indexOf = cssProperty.indexOf("-");
- cssProperty = cssProperty.substring(0, indexOf)
- + String.valueOf(cssProperty.charAt(indexOf + 1))
- .toUpperCase() + cssProperty.substring(indexOf + 2);
- }
- if ("float".equals(cssProperty)) {
- if (BrowserInfo.get().isIE()) {
- return "styleFloat";
- } else {
- return "cssFloat";
- }
- }
- return cssProperty;
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java b/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java
deleted file mode 100644
index 1fb3f297ad..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.Set;
-
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-
-public class VCustomComponent extends SimplePanel implements Container {
-
- private static final String CLASSNAME = "v-customcomponent";
- private String height;
- private ApplicationConnection client;
- private boolean rendering;
- private String width;
- private RenderSpace renderSpace = new RenderSpace();
-
- public VCustomComponent() {
- super();
- setStyleName(CLASSNAME);
- }
-
- public void updateFromUIDL(UIDL uidl, final ApplicationConnection client) {
- rendering = true;
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
- this.client = client;
-
- final UIDL child = uidl.getChildUIDL(0);
- if (child != null) {
- final Paintable p = client.getPaintable(child);
- if (p != getWidget()) {
- if (getWidget() != null) {
- client.unregisterPaintable((Paintable) getWidget());
- clear();
- }
- setWidget((Widget) p);
- }
- p.updateFromUIDL(child, client);
- }
-
- boolean updateDynamicSize = updateDynamicSize();
- if (updateDynamicSize) {
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- // FIXME deferred relative size update needed to fix some
- // scrollbar issues in sampler. This must be the wrong way
- // to do it. Might be that some other component is broken.
- client.handleComponentRelativeSize(VCustomComponent.this);
-
- }
- });
- }
-
- renderSpace.setWidth(getElement().getOffsetWidth());
- renderSpace.setHeight(getElement().getOffsetHeight());
-
- /*
- * Needed to update client size if the size of this component has
- * changed and the child uses relative size(s).
- */
- client.runDescendentsLayout(this);
-
- rendering = false;
- }
-
- private boolean updateDynamicSize() {
- boolean updated = false;
- if (isDynamicWidth()) {
- int childWidth = Util.getRequiredWidth(getWidget());
- getElement().getStyle().setPropertyPx("width", childWidth);
- updated = true;
- }
- if (isDynamicHeight()) {
- int childHeight = Util.getRequiredHeight(getWidget());
- getElement().getStyle().setPropertyPx("height", childHeight);
- updated = true;
- }
-
- return updated;
- }
-
- protected boolean isDynamicWidth() {
- return width == null || width.equals("");
- }
-
- protected boolean isDynamicHeight() {
- return height == null || height.equals("");
- }
-
- public boolean hasChildComponent(Widget component) {
- if (getWidget() == component) {
- return true;
- } else {
- return false;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- if (hasChildComponent(oldComponent)) {
- clear();
- setWidget(newComponent);
- } else {
- throw new IllegalStateException();
- }
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP, custom component dont render composition roots caption
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- // If a child grows in size, it will not necessarily be calculated
- // correctly unless we remove previous size definitions
- if (isDynamicWidth()) {
- getElement().getStyle().setProperty("width", "");
- }
- if (isDynamicHeight()) {
- getElement().getStyle().setProperty("height", "");
- }
-
- return !updateDynamicSize();
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- return renderSpace;
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- renderSpace.setHeight(getElement().getOffsetHeight());
-
- if (!height.equals(this.height)) {
- this.height = height;
- if (!rendering) {
- client.handleComponentRelativeSize(getWidget());
- }
- }
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- renderSpace.setWidth(getElement().getOffsetWidth());
-
- if (!width.equals(this.width)) {
- this.width = width;
- if (!rendering) {
- client.handleComponentRelativeSize(getWidget());
- }
- }
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java
deleted file mode 100644
index 2783db99d1..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import com.google.gwt.dom.client.Document;
-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.dom.client.Style.Unit;
-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.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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-public class VEmbedded extends HTML implements Paintable {
- public static final String CLICK_EVENT_IDENTIFIER = "click";
- public static final String ALTERNATE_TEXT = "alt";
-
- private static String CLASSNAME = "v-embedded";
-
- private String height;
- private String width;
- private Element browserElement;
-
- private String type;
-
- private ApplicationConnection client;
-
- private final ClickEventHandler clickEventHandler = new ClickEventHandler(
- this, CLICK_EVENT_IDENTIFIER) {
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
-
- };
-
- public VEmbedded() {
- setStyleName(CLASSNAME);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
- this.client = client;
-
- boolean clearBrowserElement = true;
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- if (uidl.hasAttribute("type")) {
- type = uidl.getStringAttribute("type");
- if (type.equals("image")) {
- addStyleName(CLASSNAME + "-image");
- Element el = null;
- boolean created = false;
- NodeList<Node> nodes = getElement().getChildNodes();
- if (nodes != null && nodes.getLength() == 1) {
- Node n = nodes.getItem(0);
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- Element e = (Element) n;
- if (e.getTagName().equals("IMG")) {
- el = e;
- }
- }
- }
- if (el == null) {
- setHTML("");
- el = DOM.createImg();
- created = true;
- client.addPngFix(el);
- DOM.sinkEvents(el, Event.ONLOAD);
- }
-
- // Set attributes
- Style style = el.getStyle();
- String w = uidl.getStringAttribute("width");
- if (w != null) {
- style.setProperty("width", w);
- } else {
- style.setProperty("width", "");
- }
- String h = uidl.getStringAttribute("height");
- if (h != null) {
- style.setProperty("height", h);
- } else {
- style.setProperty("height", "");
- }
- DOM.setElementProperty(el, "src", getSrc(uidl, client));
-
- if (uidl.hasAttribute(ALTERNATE_TEXT)) {
- el.setPropertyString(ALTERNATE_TEXT,
- uidl.getStringAttribute(ALTERNATE_TEXT));
- }
-
- if (created) {
- // insert in dom late
- getElement().appendChild(el);
- }
-
- /*
- * Sink tooltip events so tooltip is displayed when hovering the
- * image.
- */
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
-
- } else if (type.equals("browser")) {
- addStyleName(CLASSNAME + "-browser");
- if (browserElement == null) {
- setHTML("<iframe width=\"100%\" height=\"100%\" frameborder=\"0\""
- + " allowTransparency=\"true\" src=\"\""
- + " name=\"" + uidl.getId() + "\"></iframe>");
- browserElement = DOM.getFirstChild(getElement());
- }
- DOM.setElementAttribute(browserElement, "src",
- getSrc(uidl, client));
- clearBrowserElement = false;
- } else {
- VConsole.log("Unknown Embedded type '" + type + "'");
- }
- } else if (uidl.hasAttribute("mimetype")) {
- final String mime = uidl.getStringAttribute("mimetype");
- if (mime.equals("application/x-shockwave-flash")) {
- // Handle embedding of Flash
- addStyleName(CLASSNAME + "-flash");
- setHTML(createFlashEmbed(uidl));
- } else if (mime.equals("image/svg+xml")) {
- addStyleName(CLASSNAME + "-svg");
- String data;
- Map<String, String> parameters = getParameters(uidl);
- if (parameters.get("data") == null) {
- data = getSrc(uidl, client);
- } else {
- data = "data:image/svg+xml," + parameters.get("data");
- }
- setHTML("");
- ObjectElement obj = Document.get().createObjectElement();
- obj.setType(mime);
- obj.setData(data);
- if (width != null) {
- obj.getStyle().setProperty("width", "100%");
- }
- if (height != null) {
- obj.getStyle().setProperty("height", "100%");
- }
- if (uidl.hasAttribute("classid")) {
- obj.setAttribute("classid",
- uidl.getStringAttribute("classid"));
- }
- if (uidl.hasAttribute("codebase")) {
- obj.setAttribute("codebase",
- uidl.getStringAttribute("codebase"));
- }
- if (uidl.hasAttribute("codetype")) {
- obj.setAttribute("codetype",
- uidl.getStringAttribute("codetype"));
- }
- if (uidl.hasAttribute("archive")) {
- obj.setAttribute("archive",
- uidl.getStringAttribute("archive"));
- }
- if (uidl.hasAttribute("standby")) {
- obj.setAttribute("standby",
- uidl.getStringAttribute("standby"));
- }
- getElement().appendChild(obj);
- if (uidl.hasAttribute(ALTERNATE_TEXT)) {
- obj.setInnerText(uidl.getStringAttribute(ALTERNATE_TEXT));
- }
- } else {
- VConsole.log("Unknown Embedded mimetype '" + mime + "'");
- }
- } else {
- VConsole.log("Unknown Embedded; no type or mimetype attribute");
- }
-
- if (clearBrowserElement) {
- browserElement = null;
- }
- }
-
- /**
- * Creates the Object and Embed tags for the Flash plugin so it works
- * cross-browser
- *
- * @param uidl
- * The UIDL
- * @return Tags concatenated into a string
- */
- private String createFlashEmbed(UIDL uidl) {
- /*
- * To ensure cross-browser compatibility we are using the twice-cooked
- * method to embed flash i.e. we add a OBJECT tag for IE ActiveX and
- * inside it a EMBED for all other browsers.
- */
-
- StringBuilder html = new StringBuilder();
-
- // Start the object tag
- html.append("<object ");
-
- /*
- * Add classid required for ActiveX to recognize the flash. This is a
- * predefined value which ActiveX recognizes and must be the given
- * value. More info can be found on
- * http://kb2.adobe.com/cps/415/tn_4150.html. Allow user to override
- * this by setting his own classid.
- */
- if (uidl.hasAttribute("classid")) {
- html.append("classid=\""
- + Util.escapeAttribute(uidl.getStringAttribute("classid"))
- + "\" ");
- } else {
- html.append("classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" ");
- }
-
- /*
- * Add codebase required for ActiveX and must be exactly this according
- * to http://kb2.adobe.com/cps/415/tn_4150.html to work with the above
- * given classid. Again, see more info on
- * http://kb2.adobe.com/cps/415/tn_4150.html. Limiting Flash version to
- * 6.0.0.0 and above. Allow user to override this by setting his own
- * codebase
- */
- if (uidl.hasAttribute("codebase")) {
- html.append("codebase=\""
- + Util.escapeAttribute(uidl.getStringAttribute("codebase"))
- + "\" ");
- } else {
- html.append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ");
- }
-
- // Add width and height
- html.append("width=\"" + Util.escapeAttribute(width) + "\" ");
- html.append("height=\"" + Util.escapeAttribute(height) + "\" ");
- html.append("type=\"application/x-shockwave-flash\" ");
-
- // Codetype
- if (uidl.hasAttribute("codetype")) {
- html.append("codetype=\""
- + Util.escapeAttribute(uidl.getStringAttribute("codetype"))
- + "\" ");
- }
-
- // Standby
- if (uidl.hasAttribute("standby")) {
- html.append("standby=\""
- + Util.escapeAttribute(uidl.getStringAttribute("standby"))
- + "\" ");
- }
-
- // Archive
- if (uidl.hasAttribute("archive")) {
- html.append("archive=\""
- + Util.escapeAttribute(uidl.getStringAttribute("archive"))
- + "\" ");
- }
-
- // End object tag
- html.append(">");
-
- // Ensure we have an movie parameter
- Map<String, String> parameters = getParameters(uidl);
- if (parameters.get("movie") == null) {
- parameters.put("movie", getSrc(uidl, client));
- }
-
- // Add parameters to OBJECT
- for (String name : parameters.keySet()) {
- html.append("<param ");
- html.append("name=\"" + Util.escapeAttribute(name) + "\" ");
- html.append("value=\"" + Util.escapeAttribute(parameters.get(name))
- + "\" ");
- html.append("/>");
- }
-
- // Build inner EMBED tag
- html.append("<embed ");
- html.append("src=\"" + Util.escapeAttribute(getSrc(uidl, client))
- + "\" ");
- html.append("width=\"" + Util.escapeAttribute(width) + "\" ");
- html.append("height=\"" + Util.escapeAttribute(height) + "\" ");
- html.append("type=\"application/x-shockwave-flash\" ");
-
- // Add the parameters to the Embed
- for (String name : parameters.keySet()) {
- html.append(Util.escapeAttribute(name));
- html.append("=");
- html.append("\"" + Util.escapeAttribute(parameters.get(name))
- + "\"");
- }
-
- // End embed tag
- html.append("></embed>");
-
- if (uidl.hasAttribute(ALTERNATE_TEXT)) {
- html.append(uidl.getStringAttribute(ALTERNATE_TEXT));
- }
-
- // End object tag
- html.append("</object>");
-
- return html.toString();
- }
-
- /**
- * Returns a map (name -> value) of all parameters in the UIDL.
- *
- * @param uidl
- * @return
- */
- private static Map<String, String> getParameters(UIDL uidl) {
- Map<String, String> parameters = new HashMap<String, String>();
-
- Iterator<Object> childIterator = uidl.getChildIterator();
- while (childIterator.hasNext()) {
-
- Object child = childIterator.next();
- if (child instanceof UIDL) {
-
- UIDL childUIDL = (UIDL) child;
- if (childUIDL.getTag().equals("embeddedparam")) {
- String name = childUIDL.getStringAttribute("name");
- String value = childUIDL.getStringAttribute("value");
- parameters.put(name, value);
- }
- }
-
- }
-
- return parameters;
- }
-
- /**
- * Helper to return translated src-attribute from embedded's UIDL
- *
- * @param uidl
- * @param client
- * @return
- */
- private String getSrc(UIDL uidl, ApplicationConnection client) {
- String url = client.translateVaadinUri(uidl.getStringAttribute("src"));
- if (url == null) {
- return "";
- }
- return url;
- }
-
- @Override
- public void setWidth(String width) {
- this.width = width;
- if (isDynamicHeight()) {
- int oldHeight = getOffsetHeight();
- super.setWidth(width);
- int newHeight = getOffsetHeight();
- /*
- * Must notify parent if the height changes as a result of a width
- * change
- */
- if (oldHeight != newHeight) {
- Util.notifyParentOfSizeChange(this, false);
- }
- } else {
- super.setWidth(width);
- }
-
- }
-
- private boolean isDynamicWidth() {
- return width == null || width.equals("");
- }
-
- private boolean isDynamicHeight() {
- return height == null || height.equals("");
- }
-
- @Override
- public void setHeight(String height) {
- this.height = height;
- super.setHeight(height);
- }
-
- @Override
- protected void onDetach() {
- if (BrowserInfo.get().isIE()) {
- // Force browser to fire unload event when component is detached
- // from the view (IE doesn't do this automatically)
- if (browserElement != null) {
- /*
- * src was previously set to javascript:false, but this was not
- * enough to overcome a bug when detaching an iframe with a pdf
- * loaded in IE9. about:blank seems to cause the adobe reader
- * plugin to unload properly before the iframe is removed. See
- * #7855
- */
- DOM.setElementAttribute(browserElement, "src", "about:blank");
- }
- }
- super.onDetach();
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (DOM.eventGetType(event) == Event.ONLOAD) {
- if ("image".equals(type)) {
- updateElementDynamicSizeFromImage();
- }
- Util.notifyParentOfSizeChange(this, true);
- }
-
- client.handleTooltipEvent(event, this);
- }
-
- /**
- * Updates the size of the embedded component's element if size is
- * undefined. Without this embeddeds containing images will remain the wrong
- * size in certain cases (e.g. #6304).
- */
- private void updateElementDynamicSizeFromImage() {
- if (isDynamicWidth()) {
- getElement().getStyle().setWidth(
- getElement().getFirstChildElement().getOffsetWidth(),
- Unit.PX);
- }
- if (isDynamicHeight()) {
- getElement().getStyle().setHeight(
- getElement().getFirstChildElement().getOffsetHeight(),
- Unit.PX);
- }
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VForm.java b/src/com/vaadin/terminal/gwt/client/ui/VForm.java
deleted file mode 100644
index c0a6e5b275..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VForm.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.Set;
-
-import com.google.gwt.dom.client.Style.Display;
-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;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.VErrorMessage;
-
-public class VForm extends ComplexPanel implements Container, KeyDownHandler {
-
- protected String id;
-
- private String height = "";
-
- private String width = "";
-
- public static final String CLASSNAME = "v-form";
-
- private Container lo;
- private Element legend = DOM.createLegend();
- private Element caption = DOM.createSpan();
- private Element errorIndicatorElement = DOM.createDiv();
- private Element desc = DOM.createDiv();
- private Icon icon;
- private VErrorMessage errorMessage = new VErrorMessage();
-
- private Element fieldContainer = DOM.createDiv();
-
- private Element footerContainer = DOM.createDiv();
-
- private Element fieldSet = DOM.createFieldSet();
-
- private Container footer;
-
- private ApplicationConnection client;
-
- private RenderInformation renderInformation = new RenderInformation();
-
- private int borderPaddingHorizontal = -1;
-
- private boolean rendering = false;
-
- ShortcutActionHandler shortcutHandler;
-
- private HandlerRegistration keyDownRegistration;
-
- public VForm() {
- setElement(DOM.createDiv());
- getElement().appendChild(fieldSet);
- setStyleName(CLASSNAME);
- fieldSet.appendChild(legend);
- legend.appendChild(caption);
- errorIndicatorElement.setClassName("v-errorindicator");
- errorIndicatorElement.getStyle().setDisplay(Display.NONE);
- errorIndicatorElement.setInnerText(" "); // needed for IE
- desc.setClassName("v-form-description");
- fieldSet.appendChild(desc); // Adding description for initial padding
- // measurements, removed later if no
- // description is set
- fieldSet.appendChild(fieldContainer);
- errorMessage.setVisible(false);
- errorMessage.setStyleName(CLASSNAME + "-errormessage");
- fieldSet.appendChild(errorMessage.getElement());
- fieldSet.appendChild(footerContainer);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- this.client = client;
- id = uidl.getId();
-
- if (client.updateComponent(this, uidl, false)) {
- rendering = false;
- return;
- }
-
- boolean legendEmpty = true;
- if (uidl.hasAttribute("caption")) {
- caption.setInnerText(uidl.getStringAttribute("caption"));
- legendEmpty = false;
- } else {
- caption.setInnerText("");
- }
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
- legend.insertFirst(icon.getElement());
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- legendEmpty = false;
- } else {
- if (icon != null) {
- legend.removeChild(icon.getElement());
- }
- }
- if (legendEmpty) {
- addStyleDependentName("nocaption");
- } else {
- removeStyleDependentName("nocaption");
- }
-
- if (uidl.hasAttribute("error")) {
- final UIDL errorUidl = uidl.getErrors();
- errorMessage.updateFromUIDL(errorUidl);
- errorMessage.setVisible(true);
-
- } else {
- errorMessage.setVisible(false);
- }
-
- if (uidl.hasAttribute("description")) {
- desc.setInnerHTML(uidl.getStringAttribute("description"));
- if (desc.getParentElement() == null) {
- fieldSet.insertAfter(desc, legend);
- }
- } else {
- desc.setInnerHTML("");
- if (desc.getParentElement() != null) {
- fieldSet.removeChild(desc);
- }
- }
-
- updateSize();
-
- // first render footer so it will be easier to handle relative height of
- // main layout
- if (uidl.getChildCount() > 1
- && !uidl.getChildUIDL(1).getTag().equals("actions")) {
- // render footer
- Container newFooter = (Container) client.getPaintable(uidl
- .getChildUIDL(1));
- if (footer == null) {
- add((Widget) newFooter, footerContainer);
- footer = newFooter;
- } else if (newFooter != footer) {
- remove((Widget) footer);
- client.unregisterPaintable(footer);
- add((Widget) newFooter, footerContainer);
- }
- footer = newFooter;
- footer.updateFromUIDL(uidl.getChildUIDL(1), client);
- // needed for the main layout to know the space it has available
- updateSize();
- } else {
- if (footer != null) {
- remove((Widget) footer);
- client.unregisterPaintable(footer);
- // needed for the main layout to know the space it has available
- updateSize();
- }
- }
-
- final UIDL layoutUidl = uidl.getChildUIDL(0);
- Container newLo = (Container) client.getPaintable(layoutUidl);
- if (lo == null) {
- lo = newLo;
- add((Widget) lo, fieldContainer);
- } else if (lo != newLo) {
- client.unregisterPaintable(lo);
- remove((Widget) lo);
- lo = newLo;
- add((Widget) lo, fieldContainer);
- }
- lo.updateFromUIDL(layoutUidl, client);
-
- // also recalculates size of the footer if undefined size form - see
- // #3710
- updateSize();
- client.runDescendentsLayout(this);
-
- // We may have actions attached
- if (uidl.getChildCount() > 1) {
- UIDL childUidl = uidl.getChildByTagName("actions");
- if (childUidl != null) {
- if (shortcutHandler == null) {
- shortcutHandler = new ShortcutActionHandler(id, client);
- keyDownRegistration = addDomHandler(this,
- KeyDownEvent.getType());
- }
- shortcutHandler.updateActionMap(childUidl);
- }
- } else if (shortcutHandler != null) {
- keyDownRegistration.removeHandler();
- shortcutHandler = null;
- keyDownRegistration = null;
- }
-
- rendering = false;
- }
-
- public void updateSize() {
-
- renderInformation.updateSize(getElement());
-
- renderInformation.setContentAreaHeight(renderInformation
- .getRenderedSize().getHeight() - getSpaceConsumedVertically());
- if (BrowserInfo.get().isIE6()) {
- getElement().getStyle().setProperty("overflow", "hidden");
- }
- renderInformation.setContentAreaWidth(renderInformation
- .getRenderedSize().getWidth() - borderPaddingHorizontal);
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- if (child == lo) {
- return renderInformation.getContentAreaSize();
- } else if (child == footer) {
- return new RenderSpace(renderInformation.getContentAreaSize()
- .getWidth(), 0);
- } else {
- VConsole.error("Invalid child requested RenderSpace information");
- return null;
- }
- }
-
- public boolean hasChildComponent(Widget component) {
- return component != null && (component == lo || component == footer);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- if (!hasChildComponent(oldComponent)) {
- throw new IllegalArgumentException(
- "Old component is not inside this Container");
- }
- remove(oldComponent);
- if (oldComponent == lo) {
- lo = (Container) newComponent;
- add((Widget) lo, fieldContainer);
- } else {
- footer = (Container) newComponent;
- add((Widget) footer, footerContainer);
- }
-
- }
-
- public boolean requestLayout(Set<Paintable> child) {
-
- if (height != null && !"".equals(height) && width != null
- && !"".equals(width)) {
- /*
- * If the height and width has been specified the child components
- * cannot make the size of the layout change
- */
-
- return true;
- }
-
- if (renderInformation.updateSize(getElement())) {
- return false;
- } else {
- return true;
- }
-
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP form don't render caption for neither field layout nor footer
- // layout
- }
-
- @Override
- public void setHeight(String height) {
- if (this.height.equals(height)) {
- return;
- }
-
- this.height = height;
- super.setHeight(height);
-
- updateSize();
- }
-
- /**
- * @return pixels consumed by decoration, captions, descrioptiosn etc.. In
- * other words space, not used by the actual layout in form.
- */
- private int getSpaceConsumedVertically() {
- int offsetHeight2 = fieldSet.getOffsetHeight();
- int offsetHeight3 = fieldContainer.getOffsetHeight();
- int borderPadding = offsetHeight2 - offsetHeight3;
- return borderPadding;
- }
-
- @Override
- public void setWidth(String width) {
- if (borderPaddingHorizontal < 0) {
- // measure excess size lazily after stylename setting, but before
- // setting width
- int ow = getOffsetWidth();
- int dow = desc.getOffsetWidth();
- borderPaddingHorizontal = ow - dow;
- }
- if (Util.equals(this.width, width)) {
- return;
- }
-
- this.width = width;
- super.setWidth(width);
-
- updateSize();
-
- if (!rendering && height.equals("")) {
- // Width might affect height
- Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this);
- }
- }
-
- public void onKeyDown(KeyDownEvent event) {
- shortcutHandler.handleKeyboardEvent(Event.as(event.getNativeEvent()));
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java
deleted file mode 100644
index 174e66b7aa..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-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.FlexTable;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.StyleConstants;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-/**
- * Two col Layout that places caption on left col and field on right col
- */
-public class VFormLayout extends SimplePanel implements Container {
-
- private final static String CLASSNAME = "v-formlayout";
-
- private ApplicationConnection client;
- private VFormLayoutTable table;
-
- private String width = "";
- private String height = "";
-
- private boolean rendering = false;
-
- public VFormLayout() {
- super();
- setStyleName(CLASSNAME);
- table = new VFormLayoutTable();
- setWidget(table);
- }
-
- /**
- * Parses the stylenames from an uidl
- *
- * @param uidl
- * The uidl to get the stylenames from
- * @return An array of stylenames
- */
- private String[] getStylesFromUIDL(UIDL uidl) {
- List<String> styles = new ArrayList<String>();
- if (uidl.hasAttribute("style")) {
- String[] stylesnames = uidl.getStringAttribute("style").split(" ");
- for (String name : stylesnames) {
- styles.add(name);
- }
- }
-
- if (uidl.hasAttribute("disabled")) {
- styles.add(ApplicationConnection.DISABLED_CLASSNAME);
- }
-
- return styles.toArray(new String[styles.size()]);
- }
-
- public class VFormLayoutTable extends FlexTable implements ClickHandler {
-
- private static final int COLUMN_CAPTION = 0;
- private static final int COLUMN_ERRORFLAG = 1;
- private static final int COLUMN_WIDGET = 2;
-
- private HashMap<Paintable, Caption> componentToCaption = new HashMap<Paintable, Caption>();
- private HashMap<Paintable, ErrorFlag> componentToError = new HashMap<Paintable, ErrorFlag>();
-
- public VFormLayoutTable() {
- DOM.setElementProperty(getElement(), "cellPadding", "0");
- DOM.setElementProperty(getElement(), "cellSpacing", "0");
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- final VMarginInfo margins = new VMarginInfo(
- uidl.getIntAttribute("margins"));
-
- Element margin = getElement();
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP,
- margins.hasTop());
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT,
- margins.hasRight());
- setStyleName(margin,
- CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM,
- margins.hasBottom());
- setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT,
- margins.hasLeft());
-
- setStyleName(margin, CLASSNAME + "-" + "spacing",
- uidl.hasAttribute("spacing"));
-
- int i = 0;
- for (final Iterator<?> it = uidl.getChildIterator(); it.hasNext(); i++) {
- prepareCell(i, 1);
- final UIDL childUidl = (UIDL) it.next();
- final Paintable p = client.getPaintable(childUidl);
- Caption caption = componentToCaption.get(p);
- if (caption == null) {
- caption = new Caption(p, client);
- caption.addClickHandler(this);
- componentToCaption.put(p, caption);
- }
- ErrorFlag error = componentToError.get(p);
- if (error == null) {
- error = new ErrorFlag();
- componentToError.put(p, error);
- }
- prepareCell(i, COLUMN_WIDGET);
- final Paintable oldComponent = (Paintable) getWidget(i,
- COLUMN_WIDGET);
- if (oldComponent == null) {
- setWidget(i, COLUMN_WIDGET, (Widget) p);
- } else if (oldComponent != p) {
- client.unregisterPaintable(oldComponent);
- setWidget(i, COLUMN_WIDGET, (Widget) p);
- }
- getCellFormatter().setStyleName(i, COLUMN_WIDGET,
- CLASSNAME + "-contentcell");
- getCellFormatter().setStyleName(i, COLUMN_CAPTION,
- CLASSNAME + "-captioncell");
- setWidget(i, COLUMN_CAPTION, caption);
-
- setContentWidth(i);
-
- getCellFormatter().setStyleName(i, COLUMN_ERRORFLAG,
- CLASSNAME + "-errorcell");
- setWidget(i, COLUMN_ERRORFLAG, error);
-
- p.updateFromUIDL(childUidl, client);
-
- String rowstyles = CLASSNAME + "-row";
- if (i == 0) {
- rowstyles += " " + CLASSNAME + "-firstrow";
- }
- if (!it.hasNext()) {
- rowstyles += " " + CLASSNAME + "-lastrow";
- }
-
- getRowFormatter().setStyleName(i, rowstyles);
-
- }
-
- while (getRowCount() > i) {
- final Paintable p = (Paintable) getWidget(i, COLUMN_WIDGET);
- client.unregisterPaintable(p);
- componentToCaption.remove(p);
- removeRow(i);
- }
-
- /*
- * Must update relative sized fields last when it is clear how much
- * space they are allowed to use
- */
- for (Paintable p : componentToCaption.keySet()) {
- client.handleComponentRelativeSize((Widget) p);
- }
- }
-
- public void setContentWidths() {
- for (int row = 0; row < getRowCount(); row++) {
- setContentWidth(row);
- }
- }
-
- private void setContentWidth(int row) {
- String width = "";
- if (!isDynamicWidth()) {
- width = "100%";
- }
- getCellFormatter().setWidth(row, COLUMN_WIDGET, width);
- }
-
- public void replaceChildComponent(Widget oldComponent,
- Widget newComponent) {
- int i;
- for (i = 0; i < getRowCount(); i++) {
- Widget candidate = getWidget(i, COLUMN_WIDGET);
- if (oldComponent == candidate) {
- Caption oldCap = componentToCaption.get(oldComponent);
- final Caption newCap = new Caption(
- (Paintable) newComponent, client);
- newCap.addClickHandler(this);
- newCap.setStyleName(oldCap.getStyleName());
- componentToCaption.put((Paintable) newComponent, newCap);
- ErrorFlag error = componentToError.get(newComponent);
- if (error == null) {
- error = new ErrorFlag();
- componentToError.put((Paintable) newComponent, error);
- }
-
- setWidget(i, COLUMN_CAPTION, newCap);
- setWidget(i, COLUMN_ERRORFLAG, error);
- setWidget(i, COLUMN_WIDGET, newComponent);
- break;
- }
- }
-
- }
-
- public boolean hasChildComponent(Widget component) {
- return componentToCaption.containsKey(component);
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- final Caption c = componentToCaption.get(component);
- if (c != null) {
- c.updateCaption(uidl);
- }
- final ErrorFlag e = componentToError.get(component);
- if (e != null) {
- e.updateFromUIDL(uidl, component);
- }
-
- }
-
- public int getAllocatedWidth(Widget child, int availableWidth) {
- Caption caption = componentToCaption.get(child);
- ErrorFlag error = componentToError.get(child);
- int width = availableWidth;
-
- if (caption != null) {
- width -= DOM.getParent(caption.getElement()).getOffsetWidth();
- }
- if (error != null) {
- width -= DOM.getParent(error.getElement()).getOffsetWidth();
- }
-
- return width;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt
- * .event.dom.client.ClickEvent)
- */
- public void onClick(ClickEvent event) {
- Caption caption = (Caption) event.getSource();
- if (caption.getOwner() != null) {
- if (caption.getOwner() instanceof Focusable) {
- ((Focusable) caption.getOwner()).focus();
- } else if (caption.getOwner() instanceof com.google.gwt.user.client.ui.Focusable) {
- ((com.google.gwt.user.client.ui.Focusable) caption
- .getOwner()).setFocus(true);
- }
- }
- }
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
-
- this.client = client;
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
-
- table.updateFromUIDL(uidl, client);
-
- rendering = false;
- }
-
- public boolean isDynamicWidth() {
- return width.equals("");
- }
-
- public boolean hasChildComponent(Widget component) {
- return table.hasChildComponent(component);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- table.replaceChildComponent(oldComponent, newComponent);
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- table.updateCaption(component, uidl);
- }
-
- public class Caption extends HTML {
-
- public static final String CLASSNAME = "v-caption";
-
- private final Paintable owner;
-
- private Element requiredFieldIndicator;
-
- private Icon icon;
-
- private Element captionText;
-
- private final ApplicationConnection client;
-
- /**
- *
- * @param component
- * optional owner of caption. If not set, getOwner will
- * return null
- * @param client
- */
- public Caption(Paintable component, ApplicationConnection client) {
- super();
- this.client = client;
- owner = component;
-
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- }
-
- private void setStyles(String[] styles) {
- String styleName = CLASSNAME;
-
- if (styles != null) {
- for (String style : styles) {
- if (ApplicationConnection.DISABLED_CLASSNAME.equals(style)) {
- // Add v-disabled also without classname prefix so
- // generic v-disabled CSS rules work
- styleName += " " + style;
- }
-
- styleName += " " + CLASSNAME + "-" + style;
- }
- }
-
- setStyleName(styleName);
- }
-
- public void updateCaption(UIDL uidl) {
- setVisible(!uidl.getBooleanAttribute("invisible"));
-
- // Update styles as they might have changed when the caption changed
- setStyles(getStylesFromUIDL(uidl));
-
- boolean isEmpty = true;
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
-
- DOM.insertChild(getElement(), icon.getElement(), 0);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- isEmpty = false;
- } else {
- if (icon != null) {
- DOM.removeChild(getElement(), icon.getElement());
- icon = null;
- }
-
- }
-
- if (uidl.hasAttribute("caption")) {
- if (captionText == null) {
- captionText = DOM.createSpan();
- DOM.insertChild(getElement(), captionText, icon == null ? 0
- : 1);
- }
- String c = uidl.getStringAttribute("caption");
- if (c == null) {
- c = "";
- } else {
- isEmpty = false;
- }
- DOM.setInnerText(captionText, c);
- } else {
- // TODO should span also be removed
- }
-
- if (uidl.hasAttribute("description")) {
- if (captionText != null) {
- addStyleDependentName("hasdescription");
- } else {
- removeStyleDependentName("hasdescription");
- }
- }
-
- if (uidl.getBooleanAttribute("required")) {
- if (requiredFieldIndicator == null) {
- requiredFieldIndicator = DOM.createSpan();
- DOM.setInnerText(requiredFieldIndicator, "*");
- DOM.setElementProperty(requiredFieldIndicator, "className",
- "v-required-field-indicator");
- DOM.appendChild(getElement(), requiredFieldIndicator);
- }
- } else {
- if (requiredFieldIndicator != null) {
- DOM.removeChild(getElement(), requiredFieldIndicator);
- requiredFieldIndicator = null;
- }
- }
-
- // Workaround for IE weirdness, sometimes returns bad height in some
- // circumstances when Caption is empty. See #1444
- // IE7 bugs more often. I wonder what happens when IE8 arrives...
- if (BrowserInfo.get().isIE()) {
- if (isEmpty) {
- setHeight("0px");
- DOM.setStyleAttribute(getElement(), "overflow", "hidden");
- } else {
- setHeight("");
- DOM.setStyleAttribute(getElement(), "overflow", "");
- }
-
- }
-
- }
-
- /**
- * Returns Paintable for which this Caption belongs to.
- *
- * @return owner Widget
- */
- public Paintable getOwner() {
- return owner;
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (client != null) {
- client.handleTooltipEvent(event, owner);
- }
- }
- }
-
- private class ErrorFlag extends HTML {
- private static final String CLASSNAME = VFormLayout.CLASSNAME
- + "-error-indicator";
- Element errorIndicatorElement;
- private Paintable owner;
-
- public ErrorFlag() {
- setStyleName(CLASSNAME);
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- }
-
- public void updateFromUIDL(UIDL uidl, Paintable component) {
- owner = component;
- if (uidl.hasAttribute("error")
- && !uidl.getBooleanAttribute("hideErrors")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createDiv();
- DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
- DOM.setElementProperty(errorIndicatorElement, "className",
- "v-errorindicator");
- DOM.appendChild(getElement(), errorIndicatorElement);
- }
-
- } else if (errorIndicatorElement != null) {
- DOM.removeChild(getElement(), errorIndicatorElement);
- errorIndicatorElement = null;
- }
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (owner != null) {
- client.handleTooltipEvent(event, owner);
- }
- }
-
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- if (height.equals("") || width.equals("")) {
- // A dynamic size might change due to children changes
- return false;
- }
-
- return true;
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- int width = 0;
- int height = 0;
-
- if (!this.width.equals("")) {
- int availableWidth = getOffsetWidth();
- width = table.getAllocatedWidth(child, availableWidth);
- }
-
- return new RenderSpace(width, height, false);
- }
-
- @Override
- public void setHeight(String height) {
- if (this.height.equals(height)) {
- return;
- }
-
- this.height = height;
- super.setHeight(height);
- }
-
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
-
- this.width = width;
- super.setWidth(width);
-
- if (!rendering) {
- table.setContentWidths();
- if (height.equals("")) {
- // Width might affect height
- Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this);
- }
- }
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java
deleted file mode 100644
index 82b3eabf40..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java
+++ /dev/null
@@ -1,1132 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Document;
-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.ui.AbsolutePanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.StyleConstants;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout;
-import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer;
-
-public class VGridLayout extends SimplePanel implements Paintable, Container {
-
- public static final String CLASSNAME = "v-gridlayout";
-
- private DivElement margin = Document.get().createDivElement();
-
- private final AbsolutePanel canvas = new AbsolutePanel();
-
- private ApplicationConnection client;
-
- protected HashMap<Widget, ChildComponentContainer> widgetToComponentContainer = new HashMap<Widget, ChildComponentContainer>();
-
- private HashMap<Paintable, Cell> paintableToCell = new HashMap<Paintable, Cell>();
-
- private int spacingPixelsHorizontal;
- private int spacingPixelsVertical;
-
- private int[] columnWidths;
- private int[] rowHeights;
-
- private String height;
-
- private String width;
-
- private int[] colExpandRatioArray;
-
- private int[] rowExpandRatioArray;
-
- private int[] minColumnWidths;
-
- private int[] minRowHeights;
-
- private boolean rendering;
-
- private HashMap<Widget, ChildComponentContainer> nonRenderedWidgets;
-
- private boolean sizeChangedDuringRendering = false;
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this, EventId.LAYOUT_CLICK) {
-
- @Override
- protected Paintable getChildComponent(Element element) {
- return getComponent(element);
- }
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- public VGridLayout() {
- super();
- getElement().appendChild(margin);
- setStyleName(CLASSNAME);
- setWidget(canvas);
- }
-
- @Override
- protected Element getContainerElement() {
- return margin.cast();
- }
-
- /**
- * Returns the column widths measured in pixels
- *
- * @return
- */
- protected int[] getColumnWidths() {
- return columnWidths;
- }
-
- /**
- * Returns the row heights measured in pixels
- *
- * @return
- */
- protected int[] getRowHeights() {
- return rowHeights;
- }
-
- /**
- * Returns the spacing between the cells horizontally in pixels
- *
- * @return
- */
- protected int getHorizontalSpacing() {
- return spacingPixelsHorizontal;
- }
-
- /**
- * Returns the spacing between the cells vertically in pixels
- *
- * @return
- */
- protected int getVerticalSpacing() {
- return spacingPixelsVertical;
- }
-
- @SuppressWarnings("unchecked")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- this.client = client;
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
- clickEventHandler.handleEventHandlerRegistration(client);
-
- canvas.setWidth("0px");
-
- handleMargins(uidl);
- detectSpacing(uidl);
-
- int cols = uidl.getIntAttribute("w");
- int rows = uidl.getIntAttribute("h");
-
- columnWidths = new int[cols];
- rowHeights = new int[rows];
-
- if (cells == null) {
- cells = new Cell[cols][rows];
- } else if (cells.length != cols || cells[0].length != rows) {
- Cell[][] newCells = new Cell[cols][rows];
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- if (i < cols && j < rows) {
- newCells[i][j] = cells[i][j];
- }
- }
- }
- cells = newCells;
- }
-
- nonRenderedWidgets = (HashMap<Widget, ChildComponentContainer>) widgetToComponentContainer
- .clone();
-
- final int[] alignments = uidl.getIntArrayAttribute("alignments");
- int alignmentIndex = 0;
-
- LinkedList<Cell> pendingCells = new LinkedList<Cell>();
-
- LinkedList<Cell> relativeHeighted = new LinkedList<Cell>();
-
- for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
- final UIDL r = (UIDL) i.next();
- if ("gr".equals(r.getTag())) {
- for (final Iterator<?> j = r.getChildIterator(); j.hasNext();) {
- final UIDL c = (UIDL) j.next();
- if ("gc".equals(c.getTag())) {
- Cell cell = getCell(c);
- if (cell.hasContent()) {
- boolean rendered = cell.renderIfNoRelativeWidth();
- cell.alignment = alignments[alignmentIndex++];
- if (!rendered) {
- pendingCells.add(cell);
- }
-
- if (cell.colspan > 1) {
- storeColSpannedCell(cell);
- } else if (rendered) {
- // strore non-colspanned widths to columnWidth
- // array
- if (columnWidths[cell.col] < cell.getWidth()) {
- columnWidths[cell.col] = cell.getWidth();
- }
- }
- if (cell.hasRelativeHeight()) {
- relativeHeighted.add(cell);
- }
- }
- }
- }
- }
- }
-
- colExpandRatioArray = uidl.getIntArrayAttribute("colExpand");
- rowExpandRatioArray = uidl.getIntArrayAttribute("rowExpand");
- distributeColSpanWidths();
-
- minColumnWidths = cloneArray(columnWidths);
- expandColumns();
-
- renderRemainingComponentsWithNoRelativeHeight(pendingCells);
-
- detectRowHeights();
-
- expandRows();
-
- renderRemainingComponents(pendingCells);
-
- for (Cell cell : relativeHeighted) {
- // rendering done above so cell.cc should not be null
- Widget widget2 = cell.cc.getWidget();
- client.handleComponentRelativeSize(widget2);
- cell.cc.updateWidgetSize();
- }
-
- layoutCells();
-
- // clean non rendered components
- for (Widget w : nonRenderedWidgets.keySet()) {
- ChildComponentContainer childComponentContainer = widgetToComponentContainer
- .get(w);
- paintableToCell.remove(w);
- widgetToComponentContainer.remove(w);
- childComponentContainer.removeFromParent();
- client.unregisterPaintable((Paintable) w);
- }
- nonRenderedWidgets = null;
-
- rendering = false;
- sizeChangedDuringRendering = false;
-
- }
-
- private static int[] cloneArray(int[] toBeCloned) {
- int[] clone = new int[toBeCloned.length];
- for (int i = 0; i < clone.length; i++) {
- clone[i] = toBeCloned[i] * 1;
- }
- return clone;
- }
-
- private void expandRows() {
- if (!"".equals(height)) {
- int usedSpace = minRowHeights[0];
- for (int i = 1; i < minRowHeights.length; i++) {
- usedSpace += spacingPixelsVertical + minRowHeights[i];
- }
- int availableSpace = getOffsetHeight() - marginTopAndBottom;
- int excessSpace = availableSpace - usedSpace;
- int distributed = 0;
- if (excessSpace > 0) {
- for (int i = 0; i < rowHeights.length; i++) {
- int ew = excessSpace * rowExpandRatioArray[i] / 1000;
- rowHeights[i] = minRowHeights[i] + ew;
- distributed += ew;
- }
- excessSpace -= distributed;
- int c = 0;
- while (excessSpace > 0) {
- rowHeights[c % rowHeights.length]++;
- excessSpace--;
- c++;
- }
- }
- }
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- if (!height.equals(this.height)) {
- this.height = height;
- if (rendering) {
- sizeChangedDuringRendering = true;
- } else {
- expandRows();
- layoutCells();
- for (Paintable c : paintableToCell.keySet()) {
- client.handleComponentRelativeSize((Widget) c);
- }
- }
- }
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- if (!width.equals(this.width)) {
- this.width = width;
- if (rendering) {
- sizeChangedDuringRendering = true;
- } else {
- int[] oldWidths = cloneArray(columnWidths);
- expandColumns();
- boolean heightChanged = false;
- HashSet<Integer> dirtyRows = null;
- for (int i = 0; i < oldWidths.length; i++) {
- if (columnWidths[i] != oldWidths[i]) {
- Cell[] column = cells[i];
- for (int j = 0; j < column.length; j++) {
- Cell c = column[j];
- if (c != null && c.cc != null
- && c.widthCanAffectHeight()) {
- c.cc.setContainerSize(c.getAvailableWidth(),
- c.getAvailableHeight());
- client.handleComponentRelativeSize(c.cc
- .getWidget());
- c.cc.updateWidgetSize();
- int newHeight = c.getHeight();
- if (columnWidths[i] < oldWidths[i]
- && newHeight > minRowHeights[j]
- && c.rowspan == 1) {
- /*
- * The width of this column was reduced and
- * this affected the height. The height is
- * now greater than the previously
- * calculated minHeight for the row.
- */
- minRowHeights[j] = newHeight;
- if (newHeight > rowHeights[j]) {
- /*
- * The new height is greater than the
- * previously calculated rowHeight -> we
- * need to recalculate heights later on
- */
- rowHeights[j] = newHeight;
- heightChanged = true;
- }
- } else if (newHeight < minRowHeights[j]) {
- /*
- * The new height of the component is less
- * than the previously calculated min row
- * height. The min row height may be
- * affected and must thus be recalculated
- */
- if (dirtyRows == null) {
- dirtyRows = new HashSet<Integer>();
- }
- dirtyRows.add(j);
- }
- }
- }
- }
- }
- if (dirtyRows != null) {
- /* flag indicating that there is a potential row shrinking */
- boolean rowMayShrink = false;
- for (Integer rowIndex : dirtyRows) {
- int oldMinimum = minRowHeights[rowIndex];
- int newMinimum = 0;
- for (int colIndex = 0; colIndex < columnWidths.length; colIndex++) {
- Cell cell = cells[colIndex][rowIndex];
- if (cell != null && !cell.hasRelativeHeight()
- && cell.getHeight() > newMinimum) {
- newMinimum = cell.getHeight();
- }
- }
- if (newMinimum < oldMinimum) {
- minRowHeights[rowIndex] = rowHeights[rowIndex] = newMinimum;
- rowMayShrink = true;
- }
- }
- if (rowMayShrink) {
- distributeRowSpanHeights();
- minRowHeights = cloneArray(rowHeights);
- heightChanged = true;
- }
-
- }
- layoutCells();
- for (Paintable c : paintableToCell.keySet()) {
- client.handleComponentRelativeSize((Widget) c);
- }
- if (heightChanged && "".equals(height)) {
- Util.notifyParentOfSizeChange(this, false);
- }
- }
- }
- }
-
- private void expandColumns() {
- if (!"".equals(width)) {
- int usedSpace = minColumnWidths[0];
- for (int i = 1; i < minColumnWidths.length; i++) {
- usedSpace += spacingPixelsHorizontal + minColumnWidths[i];
- }
- canvas.setWidth("");
- int availableSpace = canvas.getOffsetWidth();
- int excessSpace = availableSpace - usedSpace;
- int distributed = 0;
- if (excessSpace > 0) {
- for (int i = 0; i < columnWidths.length; i++) {
- int ew = excessSpace * colExpandRatioArray[i] / 1000;
- columnWidths[i] = minColumnWidths[i] + ew;
- distributed += ew;
- }
- excessSpace -= distributed;
- int c = 0;
- while (excessSpace > 0) {
- columnWidths[c % columnWidths.length]++;
- excessSpace--;
- c++;
- }
- }
- }
- }
-
- private void layoutCells() {
- int x = 0;
- int y = 0;
- for (int i = 0; i < cells.length; i++) {
- y = 0;
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null) {
- cell.layout(x, y);
- }
- y += rowHeights[j] + spacingPixelsVertical;
- }
- x += columnWidths[i] + spacingPixelsHorizontal;
- }
-
- if (isUndefinedWidth()) {
- canvas.setWidth((x - spacingPixelsHorizontal) + "px");
- } else {
- // main element defines width
- canvas.setWidth("");
- }
-
- int canvasHeight;
- if (isUndefinedHeight()) {
- canvasHeight = y - spacingPixelsVertical;
- } else {
- canvasHeight = getOffsetHeight() - marginTopAndBottom;
- if (canvasHeight < 0) {
- canvasHeight = 0;
- }
- }
- canvas.setHeight(canvasHeight + "px");
- }
-
- private boolean isUndefinedHeight() {
- return "".equals(height);
- }
-
- private boolean isUndefinedWidth() {
- return "".equals(width);
- }
-
- private void renderRemainingComponents(LinkedList<Cell> pendingCells) {
- for (Cell cell : pendingCells) {
- cell.render();
- }
- }
-
- private void detectRowHeights() {
-
- // collect min rowheight from non-rowspanned cells
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null) {
- /*
- * Setting fixing container width may in some situations
- * affect height. Example: Label with wrapping text without
- * or with relative width.
- */
- if (cell.cc != null && cell.widthCanAffectHeight()) {
- cell.cc.setWidth(cell.getAvailableWidth() + "px");
- cell.cc.updateWidgetSize();
- }
- if (cell.rowspan == 1) {
- if (!cell.hasRelativeHeight()
- && rowHeights[j] < cell.getHeight()) {
- rowHeights[j] = cell.getHeight();
- }
- } else {
- storeRowSpannedCell(cell);
- }
- }
- }
- }
-
- distributeRowSpanHeights();
-
- minRowHeights = cloneArray(rowHeights);
- }
-
- private void storeRowSpannedCell(Cell cell) {
- SpanList l = null;
- for (SpanList list : rowSpans) {
- if (list.span < cell.rowspan) {
- continue;
- } else {
- // insert before this
- l = list;
- break;
- }
- }
- if (l == null) {
- l = new SpanList(cell.rowspan);
- rowSpans.add(l);
- } else if (l.span != cell.rowspan) {
- SpanList newL = new SpanList(cell.rowspan);
- rowSpans.add(rowSpans.indexOf(l), newL);
- l = newL;
- }
- l.cells.add(cell);
- }
-
- private void renderRemainingComponentsWithNoRelativeHeight(
- LinkedList<Cell> pendingCells) {
-
- for (Iterator<Cell> iterator = pendingCells.iterator(); iterator
- .hasNext();) {
- Cell cell = iterator.next();
- if (!cell.hasRelativeHeight()) {
- cell.render();
- iterator.remove();
- }
- }
-
- }
-
- /**
- * Iterates colspanned cells, ensures cols have enough space to accommodate
- * them
- */
- private void distributeColSpanWidths() {
- for (SpanList list : colSpans) {
- for (Cell cell : list.cells) {
- // cells with relative content may return non 0 here if on
- // subsequent renders
- int width = cell.hasRelativeWidth() ? 0 : cell.getWidth();
- distributeSpanSize(columnWidths, cell.col, cell.colspan,
- spacingPixelsHorizontal, width, colExpandRatioArray);
- }
- }
- }
-
- /**
- * Iterates rowspanned cells, ensures rows have enough space to accommodate
- * them
- */
- private void distributeRowSpanHeights() {
- for (SpanList list : rowSpans) {
- for (Cell cell : list.cells) {
- // cells with relative content may return non 0 here if on
- // subsequent renders
- int height = cell.hasRelativeHeight() ? 0 : cell.getHeight();
- distributeSpanSize(rowHeights, cell.row, cell.rowspan,
- spacingPixelsVertical, height, rowExpandRatioArray);
- }
- }
- }
-
- private static void distributeSpanSize(int[] dimensions,
- int spanStartIndex, int spanSize, int spacingSize, int size,
- int[] expansionRatios) {
- int allocated = dimensions[spanStartIndex];
- for (int i = 1; i < spanSize; i++) {
- allocated += spacingSize + dimensions[spanStartIndex + i];
- }
- if (allocated < size) {
- // dimensions needs to be expanded due spanned cell
- int neededExtraSpace = size - allocated;
- int allocatedExtraSpace = 0;
-
- // Divide space according to expansion ratios if any span has a
- // ratio
- int totalExpansion = 0;
- for (int i = 0; i < spanSize; i++) {
- int itemIndex = spanStartIndex + i;
- totalExpansion += expansionRatios[itemIndex];
- }
-
- for (int i = 0; i < spanSize; i++) {
- int itemIndex = spanStartIndex + i;
- int expansion;
- if (totalExpansion == 0) {
- // Divide equally among all cells if there are no
- // expansion ratios
- expansion = neededExtraSpace / spanSize;
- } else {
- expansion = neededExtraSpace * expansionRatios[itemIndex]
- / totalExpansion;
- }
- dimensions[itemIndex] += expansion;
- allocatedExtraSpace += expansion;
- }
-
- // We might still miss a couple of pixels because of
- // rounding errors...
- if (neededExtraSpace > allocatedExtraSpace) {
- for (int i = 0; i < spanSize; i++) {
- // Add one pixel to every cell until we have
- // compensated for any rounding error
- int itemIndex = spanStartIndex + i;
- dimensions[itemIndex] += 1;
- allocatedExtraSpace += 1;
- if (neededExtraSpace == allocatedExtraSpace) {
- break;
- }
- }
- }
- }
- }
-
- private LinkedList<SpanList> colSpans = new LinkedList<SpanList>();
- private LinkedList<SpanList> rowSpans = new LinkedList<SpanList>();
-
- private int marginTopAndBottom;
-
- private class SpanList {
- final int span;
- List<Cell> cells = new LinkedList<Cell>();
-
- public SpanList(int span) {
- this.span = span;
- }
- }
-
- private void storeColSpannedCell(Cell cell) {
- SpanList l = null;
- for (SpanList list : colSpans) {
- if (list.span < cell.colspan) {
- continue;
- } else {
- // insert before this
- l = list;
- break;
- }
- }
- if (l == null) {
- l = new SpanList(cell.colspan);
- colSpans.add(l);
- } else if (l.span != cell.colspan) {
-
- SpanList newL = new SpanList(cell.colspan);
- colSpans.add(colSpans.indexOf(l), newL);
- l = newL;
- }
- l.cells.add(cell);
- }
-
- private void detectSpacing(UIDL uidl) {
- DivElement spacingmeter = Document.get().createDivElement();
- spacingmeter.setClassName(CLASSNAME + "-" + "spacing-"
- + (uidl.getBooleanAttribute("spacing") ? "on" : "off"));
- spacingmeter.getStyle().setProperty("width", "0");
- spacingmeter.getStyle().setProperty("height", "0");
- canvas.getElement().appendChild(spacingmeter);
- spacingPixelsHorizontal = spacingmeter.getOffsetWidth();
- spacingPixelsVertical = spacingmeter.getOffsetHeight();
- canvas.getElement().removeChild(spacingmeter);
- }
-
- private void handleMargins(UIDL uidl) {
- final VMarginInfo margins = new VMarginInfo(
- uidl.getIntAttribute("margins"));
-
- String styles = CLASSNAME + "-margin";
- if (margins.hasTop()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_TOP;
- }
- if (margins.hasRight()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT;
- }
- if (margins.hasBottom()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM;
- }
- if (margins.hasLeft()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_LEFT;
- }
- margin.setClassName(styles);
-
- marginTopAndBottom = margin.getOffsetHeight()
- - canvas.getOffsetHeight();
- }
-
- public boolean hasChildComponent(Widget component) {
- return paintableToCell.containsKey(component);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- ChildComponentContainer componentContainer = widgetToComponentContainer
- .remove(oldComponent);
- if (componentContainer == null) {
- return;
- }
-
- componentContainer.setWidget(newComponent);
- widgetToComponentContainer.put(newComponent, componentContainer);
-
- paintableToCell.put((Paintable) newComponent,
- paintableToCell.get(oldComponent));
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- ChildComponentContainer cc = widgetToComponentContainer.get(component);
- if (cc != null) {
- cc.updateCaption(uidl, client);
- }
- if (!rendering) {
- // ensure rel size details are updated
- paintableToCell.get(component).updateRelSizeStatus(uidl);
- /*
- * This was a component-only update and the possible size change
- * must be propagated to the layout
- */
- client.captionSizeUpdated(component);
- }
- }
-
- public boolean requestLayout(final Set<Paintable> changedChildren) {
- boolean needsLayout = false;
- boolean reDistributeColSpanWidths = false;
- boolean reDistributeRowSpanHeights = false;
- int offsetHeight = canvas.getOffsetHeight();
- int offsetWidth = canvas.getOffsetWidth();
- if ("".equals(width) || "".equals(height)) {
- needsLayout = true;
- }
- ArrayList<Integer> dirtyColumns = new ArrayList<Integer>();
- ArrayList<Integer> dirtyRows = new ArrayList<Integer>();
- for (Paintable paintable : changedChildren) {
-
- Cell cell = paintableToCell.get(paintable);
- if (!cell.hasRelativeHeight() || !cell.hasRelativeWidth()) {
- // cell sizes will only stay still if only relatively
- // sized components
- // check if changed child affects min col widths
- assert cell.cc != null;
- cell.cc.setWidth("");
- cell.cc.setHeight("");
-
- cell.cc.updateWidgetSize();
-
- /*
- * If this is the result of an caption icon onload event the
- * caption size may have changed
- */
- cell.cc.updateCaptionSize();
-
- int width = cell.getWidth();
- int allocated = columnWidths[cell.col];
- for (int i = 1; i < cell.colspan; i++) {
- allocated += spacingPixelsHorizontal
- + columnWidths[cell.col + i];
- }
- if (allocated < width) {
- needsLayout = true;
- if (cell.colspan == 1) {
- // do simple column width expansion
- columnWidths[cell.col] = minColumnWidths[cell.col] = width;
- } else {
- // mark that col span expansion is needed
- reDistributeColSpanWidths = true;
- }
- } else if (allocated != width) {
- // size is smaller thant allocated, column might
- // shrink
- dirtyColumns.add(cell.col);
- }
-
- int height = cell.getHeight();
-
- allocated = rowHeights[cell.row];
- for (int i = 1; i < cell.rowspan; i++) {
- allocated += spacingPixelsVertical
- + rowHeights[cell.row + i];
- }
- if (allocated < height) {
- needsLayout = true;
- if (cell.rowspan == 1) {
- // do simple row expansion
- rowHeights[cell.row] = minRowHeights[cell.row] = height;
- } else {
- // mark that row span expansion is needed
- reDistributeRowSpanHeights = true;
- }
- } else if (allocated != height) {
- // size is smaller than allocated, row might shrink
- dirtyRows.add(cell.row);
- }
- }
- }
-
- if (dirtyColumns.size() > 0) {
- for (Integer colIndex : dirtyColumns) {
- int colW = 0;
- for (int i = 0; i < rowHeights.length; i++) {
- Cell cell = cells[colIndex][i];
- if (cell != null && cell.getChildUIDL() != null
- && !cell.hasRelativeWidth() && cell.colspan == 1) {
- int width = cell.getWidth();
- if (width > colW) {
- colW = width;
- }
- }
- }
- minColumnWidths[colIndex] = colW;
- }
- needsLayout = true;
- // ensure colspanned columns have enough space
- columnWidths = cloneArray(minColumnWidths);
- distributeColSpanWidths();
- reDistributeColSpanWidths = false;
- }
-
- if (reDistributeColSpanWidths) {
- distributeColSpanWidths();
- }
-
- if (dirtyRows.size() > 0) {
- needsLayout = true;
- for (Integer rowIndex : dirtyRows) {
- // recalculate min row height
- int rowH = minRowHeights[rowIndex] = 0;
- // loop all columns on row rowIndex
- for (int i = 0; i < columnWidths.length; i++) {
- Cell cell = cells[i][rowIndex];
- if (cell != null && cell.getChildUIDL() != null
- && !cell.hasRelativeHeight() && cell.rowspan == 1) {
- int h = cell.getHeight();
- if (h > rowH) {
- rowH = h;
- }
- }
- }
- minRowHeights[rowIndex] = rowH;
- }
- // TODO could check only some row spans
- rowHeights = cloneArray(minRowHeights);
- distributeRowSpanHeights();
- reDistributeRowSpanHeights = false;
- }
-
- if (reDistributeRowSpanHeights) {
- distributeRowSpanHeights();
- }
-
- if (needsLayout) {
- expandColumns();
- expandRows();
- layoutCells();
- // loop all relative sized components and update their size
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null
- && cell.cc != null
- && (cell.hasRelativeHeight() || cell
- .hasRelativeWidth())) {
- client.handleComponentRelativeSize(cell.cc.getWidget());
- }
- }
- }
- }
- if (canvas.getOffsetHeight() != offsetHeight
- || canvas.getOffsetWidth() != offsetWidth) {
- return false;
- } else {
- return true;
- }
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- Cell cell = paintableToCell.get(child);
- assert cell != null;
- return cell.getAllocatedSpace();
- }
-
- private Cell[][] cells;
-
- /**
- * Private helper class.
- */
- private class Cell {
- private boolean relHeight = false;
- private boolean relWidth = false;
- private boolean widthCanAffectHeight = false;
-
- public Cell(UIDL c) {
- row = c.getIntAttribute("y");
- col = c.getIntAttribute("x");
- setUidl(c);
- }
-
- public boolean widthCanAffectHeight() {
- return widthCanAffectHeight;
- }
-
- public boolean hasRelativeHeight() {
- return relHeight;
- }
-
- public RenderSpace getAllocatedSpace() {
- return new RenderSpace(getAvailableWidth()
- - cc.getCaptionWidthAfterComponent(), getAvailableHeight()
- - cc.getCaptionHeightAboveComponent());
- }
-
- public boolean hasContent() {
- return childUidl != null;
- }
-
- /**
- * @return total of spanned cols
- */
- private int getAvailableWidth() {
- int width = columnWidths[col];
- for (int i = 1; i < colspan; i++) {
- width += spacingPixelsHorizontal + columnWidths[col + i];
- }
- return width;
- }
-
- /**
- * @return total of spanned rows
- */
- private int getAvailableHeight() {
- int height = rowHeights[row];
- for (int i = 1; i < rowspan; i++) {
- height += spacingPixelsVertical + rowHeights[row + i];
- }
- return height;
- }
-
- public void layout(int x, int y) {
- if (cc != null && cc.isAttached()) {
- canvas.setWidgetPosition(cc, x, y);
- cc.setContainerSize(getAvailableWidth(), getAvailableHeight());
- cc.setAlignment(new AlignmentInfo(alignment));
- cc.updateAlignments(getAvailableWidth(), getAvailableHeight());
- }
- }
-
- public int getWidth() {
- if (cc != null) {
- int w = cc.getWidgetSize().getWidth()
- + cc.getCaptionWidthAfterComponent();
- return w;
- } else {
- return 0;
- }
- }
-
- public int getHeight() {
- if (cc != null) {
- return cc.getWidgetSize().getHeight()
- + cc.getCaptionHeightAboveComponent();
- } else {
- return 0;
- }
- }
-
- public boolean renderIfNoRelativeWidth() {
- if (childUidl == null) {
- return false;
- }
- if (!hasRelativeWidth()) {
- render();
- return true;
- } else {
- return false;
- }
- }
-
- protected boolean hasRelativeWidth() {
- return relWidth;
- }
-
- protected void render() {
- assert childUidl != null;
-
- Paintable paintable = client.getPaintable(childUidl);
- assert paintable != null;
- if (cc == null || cc.getWidget() != paintable) {
- if (widgetToComponentContainer.containsKey(paintable)) {
- // Component moving from one place to another
- cc = widgetToComponentContainer.get(paintable);
- cc.setWidth("");
- cc.setHeight("");
- /*
- * Widget might not be set if moving from another component
- * and this layout has been hidden when moving out, see
- * #5372
- */
- cc.setWidget((Widget) paintable);
- } else {
- // A new component
- cc = new ChildComponentContainer((Widget) paintable,
- CellBasedLayout.ORIENTATION_VERTICAL);
- widgetToComponentContainer.put((Widget) paintable, cc);
- cc.setWidth("");
- canvas.add(cc, 0, 0);
- }
- paintableToCell.put(paintable, this);
- }
- cc.renderChild(childUidl, client, -1);
- if (sizeChangedDuringRendering && Util.isCached(childUidl)) {
- client.handleComponentRelativeSize(cc.getWidget());
- }
- cc.updateWidgetSize();
- nonRenderedWidgets.remove(paintable);
- }
-
- public UIDL getChildUIDL() {
- return childUidl;
- }
-
- final int row;
- final int col;
- int colspan = 1;
- int rowspan = 1;
- UIDL childUidl;
- int alignment;
- // may be null after setUidl() if content has vanished or changed, set
- // in render()
- ChildComponentContainer cc;
-
- public void setUidl(UIDL c) {
- // Set cell width
- colspan = c.hasAttribute("w") ? c.getIntAttribute("w") : 1;
- // Set cell height
- rowspan = c.hasAttribute("h") ? c.getIntAttribute("h") : 1;
- // ensure we will lose reference to old cells, now overlapped by
- // this cell
- for (int i = 0; i < colspan; i++) {
- for (int j = 0; j < rowspan; j++) {
- if (i > 0 || j > 0) {
- cells[col + i][row + j] = null;
- }
- }
- }
-
- c = c.getChildUIDL(0); // we are interested about childUidl
- if (childUidl != null) {
- if (c == null) {
- // content has vanished, old content will be removed from
- // canvas later during the render phase
- cc = null;
- } else if (cc != null
- && cc.getWidget() != client.getPaintable(c)) {
- // content has changed
- cc = null;
- Paintable paintable = client.getPaintable(c);
- if (widgetToComponentContainer.containsKey(paintable)) {
- // cc exist for this component (moved) use that for this
- // cell
- cc = widgetToComponentContainer.get(paintable);
- cc.setWidth("");
- cc.setHeight("");
- paintableToCell.put(paintable, this);
- }
- }
- }
- childUidl = c;
- updateRelSizeStatus(c);
- }
-
- protected void updateRelSizeStatus(UIDL uidl) {
- if (uidl != null && !uidl.getBooleanAttribute("cached")) {
- if (uidl.hasAttribute("height")
- && uidl.getStringAttribute("height").contains("%")) {
- relHeight = true;
- } else {
- relHeight = false;
- }
- if (uidl.hasAttribute("width")) {
- widthCanAffectHeight = relWidth = uidl.getStringAttribute(
- "width").contains("%");
- if (uidl.hasAttribute("height")) {
- widthCanAffectHeight = false;
- }
- } else {
- widthCanAffectHeight = !uidl.hasAttribute("height");
- relWidth = false;
- }
- }
- }
- }
-
- private Cell getCell(UIDL c) {
- int row = c.getIntAttribute("y");
- int col = c.getIntAttribute("x");
- Cell cell = cells[col][row];
- if (cell == null) {
- cell = new Cell(c);
- cells[col][row] = cell;
- } else {
- cell.setUidl(c);
- }
- return cell;
- }
-
- /**
- * 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.
- */
- private Paintable getComponent(Element element) {
- return Util.getPaintableForElement(client, this, element);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java
deleted file mode 100644
index b3a036f748..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-public class VHorizontalLayout extends VOrderedLayout {
-
- public static final String CLASSNAME = "v-horizontallayout";
-
- public VHorizontalLayout() {
- super(CLASSNAME, ORIENTATION_HORIZONTAL);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VLabel.java b/src/com/vaadin/terminal/gwt/client/ui/VLabel.java
deleted file mode 100644
index 341e9f3484..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VLabel.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.PreElement;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.HTML;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-public class VLabel extends HTML implements Paintable {
-
- public static final String CLASSNAME = "v-label";
- private static final String CLASSNAME_UNDEFINED_WIDTH = "v-label-undef-w";
-
- private ApplicationConnection client;
- private int verticalPaddingBorder = 0;
- private int horizontalPaddingBorder = 0;
-
- public VLabel() {
- super();
- setStyleName(CLASSNAME);
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- }
-
- public VLabel(String text) {
- super(text);
- setStyleName(CLASSNAME);
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (event.getTypeInt() == Event.ONLOAD) {
- Util.notifyParentOfSizeChange(this, true);
- event.cancelBubble(true);
- return;
- }
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- this.client = client;
-
- boolean sinkOnloads = false;
-
- final String mode = uidl.getStringAttribute("mode");
- if (mode == null || "text".equals(mode)) {
- setText(uidl.getChildString(0));
- } else if ("pre".equals(mode)) {
- PreElement preElement = Document.get().createPreElement();
- preElement.setInnerText(uidl.getChildUIDL(0).getChildString(0));
- // clear existing content
- setHTML("");
- // add preformatted text to dom
- getElement().appendChild(preElement);
- } else if ("uidl".equals(mode)) {
- setHTML(uidl.getChildrenAsXML());
- } else if ("xhtml".equals(mode)) {
- UIDL content = uidl.getChildUIDL(0).getChildUIDL(0);
- if (content.getChildCount() > 0) {
- setHTML(content.getChildString(0));
- } else {
- setHTML("");
- }
- sinkOnloads = true;
- } else if ("xml".equals(mode)) {
- setHTML(uidl.getChildUIDL(0).getChildString(0));
- } else if ("raw".equals(mode)) {
- setHTML(uidl.getChildUIDL(0).getChildString(0));
- sinkOnloads = true;
- } else {
- setText("");
- }
- if (sinkOnloads) {
- sinkOnloadsForContainedImgs();
- }
- }
-
- private void sinkOnloadsForContainedImgs() {
- NodeList<Element> images = getElement().getElementsByTagName("img");
- for (int i = 0; i < images.getLength(); i++) {
- Element img = images.getItem(i);
- DOM.sinkEvents((com.google.gwt.user.client.Element) img,
- Event.ONLOAD);
- }
-
- }
-
- @Override
- public void setHeight(String height) {
- verticalPaddingBorder = Util.setHeightExcludingPaddingAndBorder(this,
- height, verticalPaddingBorder);
- }
-
- @Override
- public void setWidth(String width) {
- horizontalPaddingBorder = Util.setWidthExcludingPaddingAndBorder(this,
- width, horizontalPaddingBorder);
- if (width == null || width.equals("")) {
- setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, true);
- } else {
- setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, false);
- }
- }
-
- @Override
- public void setText(String text) {
- if (BrowserInfo.get().isIE() && BrowserInfo.get().getIEVersion() < 9) {
- // #3983 - IE6-IE8 incorrectly replaces \n with <br> so we do the
- // escaping manually and set as HTML
- super.setHTML(Util.escapeHTML(text));
- } else {
- super.setText(text);
- }
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VLink.java b/src/com/vaadin/terminal/gwt/client/ui/VLink.java
deleted file mode 100644
index b8030de421..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VLink.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-public class VLink extends HTML implements Paintable, ClickHandler {
-
- public static final String CLASSNAME = "v-link";
-
- private static final int BORDER_STYLE_DEFAULT = 0;
- private static final int BORDER_STYLE_MINIMAL = 1;
- private static final int BORDER_STYLE_NONE = 2;
-
- private String src;
-
- private String target;
-
- private int borderStyle = BORDER_STYLE_DEFAULT;
-
- private boolean enabled;
-
- private boolean readonly;
-
- private int targetWidth;
-
- private int targetHeight;
-
- private Element errorIndicatorElement;
-
- private final Element anchor = DOM.createAnchor();
-
- private final Element captionElement = DOM.createSpan();
-
- private Icon icon;
-
- private ApplicationConnection client;
-
- public VLink() {
- super();
- getElement().appendChild(anchor);
- anchor.appendChild(captionElement);
- addClickHandler(this);
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- setStyleName(CLASSNAME);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- // Ensure correct implementation,
- // but don't let container manage caption etc.
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
-
- this.client = client;
-
- enabled = uidl.hasAttribute("disabled") ? false : true;
- readonly = uidl.hasAttribute("readonly") ? true : false;
-
- if (uidl.hasAttribute("name")) {
- target = uidl.getStringAttribute("name");
- anchor.setAttribute("target", target);
- }
- if (uidl.hasAttribute("src")) {
- src = client.translateVaadinUri(uidl.getStringAttribute("src"));
- anchor.setAttribute("href", src);
- }
-
- if (uidl.hasAttribute("border")) {
- if ("none".equals(uidl.getStringAttribute("border"))) {
- borderStyle = BORDER_STYLE_NONE;
- } else {
- borderStyle = BORDER_STYLE_MINIMAL;
- }
- } else {
- borderStyle = BORDER_STYLE_DEFAULT;
- }
-
- targetHeight = uidl.hasAttribute("targetHeight") ? uidl
- .getIntAttribute("targetHeight") : -1;
- targetWidth = uidl.hasAttribute("targetWidth") ? uidl
- .getIntAttribute("targetWidth") : -1;
-
- // Set link caption
- captionElement.setInnerText(uidl.getStringAttribute("caption"));
-
- // handle error
- if (uidl.hasAttribute("error")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createDiv();
- DOM.setElementProperty(errorIndicatorElement, "className",
- "v-errorindicator");
- }
- DOM.insertChild(getElement(), errorIndicatorElement, 0);
- } else if (errorIndicatorElement != null) {
- DOM.setStyleAttribute(errorIndicatorElement, "display", "none");
- }
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
- anchor.insertBefore(icon.getElement(), captionElement);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- }
-
- }
-
- public void onClick(ClickEvent event) {
- if (enabled && !readonly) {
- if (target == null) {
- target = "_self";
- }
- String features;
- switch (borderStyle) {
- case BORDER_STYLE_NONE:
- features = "menubar=no,location=no,status=no";
- break;
- case BORDER_STYLE_MINIMAL:
- features = "menubar=yes,location=no,status=no";
- break;
- default:
- features = "";
- break;
- }
-
- if (targetWidth > 0) {
- features += (features.length() > 0 ? "," : "") + "width="
- + targetWidth;
- }
- if (targetHeight > 0) {
- features += (features.length() > 0 ? "," : "") + "height="
- + targetHeight;
- }
-
- if (features.length() > 0) {
- // if 'special features' are set, use window.open(), unless
- // a modifier key is held (ctrl to open in new tab etc)
- Event e = DOM.eventGetCurrentEvent();
- if (!e.getCtrlKey() && !e.getAltKey() && !e.getShiftKey()
- && !e.getMetaKey()) {
- Window.open(src, target, features);
- e.preventDefault();
- }
- }
- }
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- final Element target = DOM.eventGetTarget(event);
- if (event.getTypeInt() == Event.ONLOAD) {
- Util.notifyParentOfSizeChange(this, true);
- }
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- if (target == captionElement || target == anchor
- || (icon != null && target == icon.getElement())) {
- super.onBrowserEvent(event);
- }
- if (!enabled) {
- event.preventDefault();
- }
-
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java b/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java
index 53638917b2..6c5fbc2ef0 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java
@@ -8,25 +8,10 @@ import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.MediaElement;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-public abstract class VMediaBase extends Widget implements Paintable {
- public static final String ATTR_PAUSE = "pause";
- public static final String ATTR_PLAY = "play";
- public static final String ATTR_MUTED = "muted";
- public static final String ATTR_CONTROLS = "ctrl";
- public static final String ATTR_AUTOPLAY = "auto";
- public static final String TAG_SOURCE = "src";
- public static final String ATTR_RESOURCE = "res";
- public static final String ATTR_RESOURCE_TYPE = "type";
- public static final String ATTR_HTML = "html";
- public static final String ATTR_ALT_TEXT = "alt";
+public abstract class VMediaBase extends Widget {
private MediaElement media;
- protected ApplicationConnection client;
/**
* Sets the MediaElement that is to receive all commands and properties.
@@ -38,96 +23,40 @@ public abstract class VMediaBase extends Widget implements Paintable {
media = element;
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- this.client = client;
-
- media.setControls(shouldShowControls(uidl));
- media.setAutoplay(shouldAutoplay(uidl));
- media.setMuted(isMediaMuted(uidl));
-
- // Add all sources
- for (int ix = 0; ix < uidl.getChildCount(); ix++) {
- UIDL child = uidl.getChildUIDL(ix);
- if (TAG_SOURCE.equals(child.getTag())) {
- Element src = Document.get().createElement("source").cast();
- src.setAttribute("src", getSourceUrl(child));
- src.setAttribute("type", getSourceType(child));
- media.appendChild(src);
- }
- }
- setAltText(uidl);
-
- evalPauseCommand(uidl);
- evalPlayCommand(uidl);
- }
-
- protected boolean shouldShowControls(UIDL uidl) {
- return uidl.getBooleanAttribute(ATTR_CONTROLS);
- }
-
- private boolean shouldAutoplay(UIDL uidl) {
- return uidl.getBooleanAttribute(ATTR_AUTOPLAY);
- }
-
- private boolean isMediaMuted(UIDL uidl) {
- return uidl.getBooleanAttribute(ATTR_MUTED);
- }
-
/**
- * @param uidl
- * @return the URL of a resource to be used as a source for the media
+ * @return the default HTML to show users with browsers that do not support
+ * HTML5 media markup.
*/
- private String getSourceUrl(UIDL uidl) {
- String url = client.translateVaadinUri(uidl
- .getStringAttribute(ATTR_RESOURCE));
- if (url == null) {
- return "";
- }
- return url;
- }
+ protected abstract String getDefaultAltHtml();
- /**
- * @param uidl
- * @return the mime type of the media
- */
- private String getSourceType(UIDL uidl) {
- return uidl.getStringAttribute(ATTR_RESOURCE_TYPE);
+ public void play() {
+ media.play();
}
- private void setAltText(UIDL uidl) {
- String alt = uidl.getStringAttribute(ATTR_ALT_TEXT);
+ public void pause() {
+ media.pause();
+ }
- if (alt == null || "".equals(alt)) {
- alt = getDefaultAltHtml();
- } else if (!allowHtmlContent(uidl)) {
- alt = Util.escapeHTML(alt);
- }
+ public void setAltText(String alt) {
media.appendChild(Document.get().createTextNode(alt));
}
- private boolean allowHtmlContent(UIDL uidl) {
- return uidl.getBooleanAttribute(ATTR_HTML);
+ public void setControls(boolean shouldShowControls) {
+ media.setControls(shouldShowControls);
}
- private void evalPlayCommand(UIDL uidl) {
- if (uidl.hasAttribute(ATTR_PLAY)) {
- media.play();
- }
+ public void setAutoplay(boolean shouldAutoplay) {
+ media.setAutoplay(shouldAutoplay);
}
- private void evalPauseCommand(UIDL uidl) {
- if (uidl.hasAttribute(ATTR_PAUSE)) {
- media.pause();
- }
+ public void setMuted(boolean mediaMuted) {
+ media.setMuted(mediaMuted);
}
- /**
- * @return the default HTML to show users with browsers that do not support
- * HTML5 media markup.
- */
- protected abstract String getDefaultAltHtml();
+ public void addSource(String sourceUrl, String sourceType) {
+ Element src = Document.get().createElement("source").cast();
+ src.setAttribute("src", sourceUrl);
+ src.setAttribute("type", sourceType);
+ media.appendChild(src);
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java
deleted file mode 100644
index 98d3b505ae..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.core.client.Scheduler;
-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;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-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.Event;
-import com.google.gwt.user.client.ui.Button;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.EventHelper;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VTooltip;
-
-public class VNativeButton extends Button implements Paintable, ClickHandler,
- FocusHandler, BlurHandler {
-
- public static final String CLASSNAME = "v-nativebutton";
-
- protected String width = null;
-
- protected String id;
-
- protected ApplicationConnection client;
-
- protected Element errorIndicatorElement;
-
- protected final Element captionElement = DOM.createSpan();
-
- protected Icon icon;
-
- /**
- * Helper flag to handle special-case where the button is moved from under
- * mouse while clicking it. In this case mouse leaves the button without
- * moving.
- */
- private boolean clickPending;
-
- private HandlerRegistration focusHandlerRegistration;
- private HandlerRegistration blurHandlerRegistration;
-
- private boolean disableOnClick = false;
-
- public VNativeButton() {
- setStyleName(CLASSNAME);
-
- getElement().appendChild(captionElement);
- captionElement.setClassName(getStyleName() + "-caption");
-
- addClickHandler(this);
-
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- sinkEvents(Event.ONMOUSEDOWN);
- sinkEvents(Event.ONMOUSEUP);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- // Ensure correct implementation,
- // but don't let container manage caption etc.
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
-
- disableOnClick = uidl.hasAttribute(VButton.ATTR_DISABLE_ON_CLICK);
-
- focusHandlerRegistration = EventHelper.updateFocusHandler(this, client,
- focusHandlerRegistration);
- blurHandlerRegistration = EventHelper.updateBlurHandler(this, client,
- blurHandlerRegistration);
-
- // Save details
- this.client = client;
- id = uidl.getId();
-
- // Set text
- setText(uidl.getStringAttribute("caption"));
-
- // handle error
- if (uidl.hasAttribute("error")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createSpan();
- errorIndicatorElement.setClassName("v-errorindicator");
- }
- getElement().insertBefore(errorIndicatorElement, captionElement);
-
- // Fix for IE6, IE7
- if (BrowserInfo.get().isIE()) {
- errorIndicatorElement.setInnerText(" ");
- }
-
- } else if (errorIndicatorElement != null) {
- getElement().removeChild(errorIndicatorElement);
- errorIndicatorElement = null;
- }
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
- getElement().insertBefore(icon.getElement(), captionElement);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- } else {
- if (icon != null) {
- getElement().removeChild(icon.getElement());
- icon = null;
- }
- }
-
- if (BrowserInfo.get().isIE7()) {
- /*
- * Workaround for IE7 size calculation issues. Deferred because of
- * issues with a button with an icon using the reindeer theme
- */
- if (width.equals("")) {
- Scheduler.get().scheduleDeferred(new Command() {
-
- public void execute() {
- setWidth("");
- setWidth(getOffsetWidth() + "px");
- }
- });
- }
- }
- }
-
- @Override
- public void setText(String text) {
- captionElement.setInnerText(text);
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
-
- if (DOM.eventGetType(event) == Event.ONLOAD) {
- Util.notifyParentOfSizeChange(this, true);
-
- } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN
- && event.getButton() == Event.BUTTON_LEFT) {
- clickPending = true;
- } else if (DOM.eventGetType(event) == Event.ONMOUSEMOVE) {
- clickPending = false;
- } else if (DOM.eventGetType(event) == Event.ONMOUSEOUT) {
- if (clickPending) {
- click();
- }
- clickPending = false;
- }
-
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- }
-
- @Override
- public void setWidth(String width) {
- /* Workaround for IE7 button size part 1 (#2014) */
- if (BrowserInfo.get().isIE7() && this.width != null) {
- if (this.width.equals(width)) {
- return;
- }
-
- if (width == null) {
- width = "";
- }
- }
-
- this.width = width;
- super.setWidth(width);
-
- /* Workaround for IE7 button size part 2 (#2014) */
- if (BrowserInfo.get().isIE7()) {
- super.setWidth(width);
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
- * .dom.client.ClickEvent)
- */
- public void onClick(ClickEvent event) {
- if (id == null || client == null) {
- return;
- }
-
- if (BrowserInfo.get().isSafari()) {
- VNativeButton.this.setFocus(true);
- }
- if (disableOnClick) {
- setEnabled(false);
- client.updateVariable(id, "disabledOnClick", true, false);
- }
-
- // Add mouse details
- MouseEventDetails details = new MouseEventDetails(
- event.getNativeEvent(), getElement());
- client.updateVariable(id, "mousedetails", details.serialize(), false);
-
- client.updateVariable(id, "state", true, true);
- clickPending = false;
- }
-
- public void onFocus(FocusEvent arg0) {
- client.updateVariable(id, EventId.FOCUS, "", true);
- }
-
- public void onBlur(BlurEvent arg0) {
- client.updateVariable(id, EventId.BLUR, "", true);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- if (isEnabled() != enabled) {
- super.setEnabled(enabled);
- setStyleName(ApplicationConnection.DISABLED_CLASSNAME, !enabled);
- }
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java
deleted file mode 100644
index ecdb171ec4..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java
+++ /dev/null
@@ -1,969 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Set;
-
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Style.Unit;
-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.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.ValueMap;
-import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout;
-import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer;
-
-public class VOrderedLayout extends CellBasedLayout {
-
- public static final String CLASSNAME = "v-orderedlayout";
-
- private int orientation;
-
- // Can be removed once OrderedLayout is removed
- private boolean allowOrientationUpdate = false;
-
- /**
- * Size of the layout excluding any margins.
- */
- private Size activeLayoutSize = new Size(0, 0);
-
- private boolean isRendering = false;
-
- private String width = "";
-
- private boolean sizeHasChangedDuringRendering = false;
-
- private ValueMap expandRatios;
-
- private double expandRatioSum;
-
- private double defaultExpandRatio;
-
- private ValueMap alignments;
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this, EventId.LAYOUT_CLICK) {
-
- @Override
- protected Paintable getChildComponent(Element element) {
- return getComponent(element);
- }
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- public VOrderedLayout() {
- this(CLASSNAME, ORIENTATION_VERTICAL);
- allowOrientationUpdate = true;
- }
-
- protected VOrderedLayout(String className, int orientation) {
- setStyleName(className);
- this.orientation = orientation;
-
- STYLENAME_SPACING = className + "-spacing";
- STYLENAME_MARGIN_TOP = className + "-margin-top";
- STYLENAME_MARGIN_RIGHT = className + "-margin-right";
- STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom";
- STYLENAME_MARGIN_LEFT = className + "-margin-left";
- }
-
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- isRendering = true;
- super.updateFromUIDL(uidl, client);
-
- // Only non-cached, visible UIDL:s can introduce changes
- if (uidl.getBooleanAttribute("cached")
- || uidl.getBooleanAttribute("invisible")) {
- isRendering = false;
- return;
- }
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- if (allowOrientationUpdate) {
- handleOrientationUpdate(uidl);
- }
-
- // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL");
-
- ArrayList<Widget> uidlWidgets = new ArrayList<Widget>(
- uidl.getChildCount());
- ArrayList<ChildComponentContainer> relativeSizeComponents = new ArrayList<ChildComponentContainer>();
- ArrayList<UIDL> relativeSizeComponentUIDL = new ArrayList<UIDL>();
-
- int pos = 0;
- for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) {
- final UIDL childUIDL = (UIDL) it.next();
- final Paintable child = client.getPaintable(childUIDL);
- Widget widget = (Widget) child;
-
- // Create container for component
- ChildComponentContainer childComponentContainer = getComponentContainer(widget);
-
- if (childComponentContainer == null) {
- // This is a new component
- childComponentContainer = createChildContainer(widget);
- } else {
- /*
- * The widget may be null if the same paintable has been
- * rendered in a different component container while this has
- * been invisible. Ensure the childComponentContainer has the
- * widget attached. See e.g. #5372
- */
- childComponentContainer.setWidget(widget);
- }
-
- addOrMoveChild(childComponentContainer, pos++);
-
- /*
- * Components which are to be expanded in the same orientation as
- * the layout are rendered later when it is clear how much space
- * they can use
- */
- if (!Util.isCached(childUIDL)) {
- FloatSize relativeSize = Util.parseRelativeSize(childUIDL);
- childComponentContainer.setRelativeSize(relativeSize);
- }
-
- if (childComponentContainer.isComponentRelativeSized(orientation)) {
- relativeSizeComponents.add(childComponentContainer);
- relativeSizeComponentUIDL.add(childUIDL);
- } else {
- if (isDynamicWidth()) {
- childComponentContainer.renderChild(childUIDL, client, -1);
- } else {
- childComponentContainer.renderChild(childUIDL, client,
- activeLayoutSize.getWidth());
- }
- if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) {
- // notify cached relative sized component about size
- // chance
- client.handleComponentRelativeSize(childComponentContainer
- .getWidget());
- }
- }
-
- uidlWidgets.add(widget);
-
- }
-
- // w.mark("Rendering of "
- // + (uidlWidgets.size() - relativeSizeComponents.size())
- // + " absolute size components done");
-
- /*
- * Remove any children after pos. These are the ones that previously
- * were in the layout but have now been removed
- */
- removeChildrenAfter(pos);
-
- // w.mark("Old children removed");
-
- /* Fetch alignments and expand ratio from UIDL */
- updateAlignmentsAndExpandRatios(uidl, uidlWidgets);
- // w.mark("Alignments and expand ratios updated");
-
- /* Fetch widget sizes from rendered components */
- updateWidgetSizes();
- // w.mark("Widget sizes updated");
-
- recalculateLayout();
- // w.mark("Layout size calculated (" + activeLayoutSize +
- // ") offsetSize: "
- // + getOffsetWidth() + "," + getOffsetHeight());
-
- /* Render relative size components */
- for (int i = 0; i < relativeSizeComponents.size(); i++) {
- ChildComponentContainer childComponentContainer = relativeSizeComponents
- .get(i);
- UIDL childUIDL = relativeSizeComponentUIDL.get(i);
-
- if (isDynamicWidth()) {
- childComponentContainer.renderChild(childUIDL, client, -1);
- } else {
- childComponentContainer.renderChild(childUIDL, client,
- activeLayoutSize.getWidth());
- }
-
- if (Util.isCached(childUIDL)) {
- /*
- * We must update the size of the relative sized component if
- * the expand ratio or something else in the layout changes
- * which affects the size of a relative sized component
- */
- client.handleComponentRelativeSize(childComponentContainer
- .getWidget());
- }
-
- // childComponentContainer.updateWidgetSize();
- }
-
- // w.mark("Rendering of " + (relativeSizeComponents.size())
- // + " relative size components done");
-
- /* Fetch widget sizes for relative size components */
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
-
- /* Update widget size from DOM */
- childComponentContainer.updateWidgetSize();
- }
-
- // w.mark("Widget sizes updated");
-
- /*
- * Components with relative size in main direction may affect the layout
- * size in the other direction
- */
- if ((isHorizontal() && isDynamicHeight())
- || (isVertical() && isDynamicWidth())) {
- layoutSizeMightHaveChanged();
- }
- // w.mark("Layout dimensions updated");
-
- /* Update component spacing */
- updateContainerMargins();
-
- /*
- * Update component sizes for components with relative size in non-main
- * direction
- */
- if (updateRelativeSizesInNonMainDirection()) {
- // Sizes updated - might affect the other dimension so we need to
- // recheck the widget sizes and recalculate layout dimensions
- updateWidgetSizes();
- layoutSizeMightHaveChanged();
- }
- calculateAlignments();
- // w.mark("recalculateComponentSizesAndAlignments done");
-
- setRootSize();
-
- if (BrowserInfo.get().isIE()) {
- /*
- * This should fix the issue with padding not always taken into
- * account for the containers leading to no spacing between
- * elements.
- */
- root.getStyle().setProperty("zoom", "1");
- }
-
- // w.mark("runDescendentsLayout done");
- isRendering = false;
- sizeHasChangedDuringRendering = false;
- }
-
- private void layoutSizeMightHaveChanged() {
- Size oldSize = new Size(activeLayoutSize.getWidth(),
- activeLayoutSize.getHeight());
- calculateLayoutDimensions();
-
- /*
- * If layout dimension changes we must also update container sizes
- */
- if (!oldSize.equals(activeLayoutSize)) {
- calculateContainerSize();
- }
- }
-
- private void updateWidgetSizes() {
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
-
- /*
- * Update widget size from DOM
- */
- childComponentContainer.updateWidgetSize();
- }
- }
-
- private void recalculateLayout() {
-
- /* Calculate space for relative size components */
- int spaceForExpansion = calculateLayoutDimensions();
-
- if (!widgetToComponentContainer.isEmpty()) {
- /* Divide expansion space between component containers */
- expandComponentContainers(spaceForExpansion);
-
- /* Update container sizes */
- calculateContainerSize();
- }
-
- }
-
- private void expandComponentContainers(int spaceForExpansion) {
- int remaining = spaceForExpansion;
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
- remaining -= childComponentContainer.expand(orientation,
- spaceForExpansion);
- }
-
- if (remaining > 0) {
-
- // Some left-over pixels due to rounding errors
-
- // Add one pixel to each container until there are no pixels left
- // FIXME extra pixels should be divided among expanded widgets if
- // such a widgets exists
-
- Iterator<Widget> widgetIterator = iterator();
- while (widgetIterator.hasNext() && remaining-- > 0) {
- ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
- .next();
- childComponentContainer.expandExtra(orientation, 1);
- }
- }
-
- }
-
- private void handleOrientationUpdate(UIDL uidl) {
- int newOrientation = ORIENTATION_VERTICAL;
- if ("horizontal".equals(uidl.getStringAttribute("orientation"))) {
- newOrientation = ORIENTATION_HORIZONTAL;
- }
-
- if (orientation != newOrientation) {
- orientation = newOrientation;
-
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
- childComponentContainer.setOrientation(orientation);
- }
- }
-
- }
-
- /**
- * Updated components with relative height in horizontal layouts and
- * components with relative width in vertical layouts. This is only needed
- * if the height (horizontal layout) or width (vertical layout) has not been
- * specified.
- */
- private boolean updateRelativeSizesInNonMainDirection() {
- int updateDirection = 1 - orientation;
- if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth())
- || (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) {
- return false;
- }
-
- boolean updated = false;
- for (ChildComponentContainer componentContainer : widgetToComponentContainer
- .values()) {
- if (componentContainer.isComponentRelativeSized(updateDirection)) {
- client.handleComponentRelativeSize(componentContainer
- .getWidget());
- }
-
- updated = true;
- }
-
- return updated;
- }
-
- private int calculateLayoutDimensions() {
- int summedWidgetWidth = 0;
- int summedWidgetHeight = 0;
-
- int maxWidgetWidth = 0;
- int maxWidgetHeight = 0;
-
- // Calculate layout dimensions from component dimensions
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
-
- int widgetHeight = 0;
- int widgetWidth = 0;
- if (childComponentContainer.isComponentRelativeSized(orientation)) {
- if (orientation == ORIENTATION_HORIZONTAL) {
- widgetHeight = getWidgetHeight(childComponentContainer);
- } else {
- widgetWidth = getWidgetWidth(childComponentContainer);
- }
- } else {
- widgetWidth = getWidgetWidth(childComponentContainer);
- widgetHeight = getWidgetHeight(childComponentContainer);
- }
-
- summedWidgetWidth += widgetWidth;
- summedWidgetHeight += widgetHeight;
-
- maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight);
- maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth);
- }
-
- if (isHorizontal()) {
- summedWidgetWidth += activeSpacing.hSpacing
- * (widgetToComponentContainer.size() - 1);
- } else {
- summedWidgetHeight += activeSpacing.vSpacing
- * (widgetToComponentContainer.size() - 1);
- }
-
- Size layoutSize = updateLayoutDimensions(summedWidgetWidth,
- summedWidgetHeight, maxWidgetWidth, maxWidgetHeight);
-
- int remainingSpace;
- if (isHorizontal()) {
- remainingSpace = layoutSize.getWidth() - summedWidgetWidth;
- } else {
- remainingSpace = layoutSize.getHeight() - summedWidgetHeight;
- }
- if (remainingSpace < 0) {
- remainingSpace = 0;
- }
-
- // ApplicationConnection.getConsole().log(
- // "Layout size: " + activeLayoutSize);
- return remainingSpace;
- }
-
- private int getWidgetHeight(ChildComponentContainer childComponentContainer) {
- Size s = childComponentContainer.getWidgetSize();
- return s.getHeight()
- + childComponentContainer.getCaptionHeightAboveComponent();
- }
-
- private int getWidgetWidth(ChildComponentContainer childComponentContainer) {
- Size s = childComponentContainer.getWidgetSize();
- int widgetWidth = s.getWidth()
- + childComponentContainer.getCaptionWidthAfterComponent();
-
- /*
- * If the component does not have a specified size in the main direction
- * the caption may determine the space used by the component
- */
- if (!childComponentContainer.widgetHasSizeSpecified(orientation)) {
- int captionWidth = childComponentContainer
- .getCaptionRequiredWidth();
-
- if (captionWidth > widgetWidth) {
- widgetWidth = captionWidth;
- }
- }
-
- return widgetWidth;
- }
-
- private void calculateAlignments() {
- int w = 0;
- int h = 0;
-
- if (isHorizontal()) {
- // HORIZONTAL
- h = activeLayoutSize.getHeight();
- if (!isDynamicWidth()) {
- w = -1;
- }
-
- } else {
- // VERTICAL
- w = activeLayoutSize.getWidth();
- if (!isDynamicHeight()) {
- h = -1;
- }
- }
-
- for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
- .values()) {
- childComponentContainer.updateAlignments(w, h);
- }
-
- }
-
- private void calculateContainerSize() {
-
- /*
- * Container size here means the size the container gets from the
- * component. The expansion size is not include in this but taken
- * separately into account.
- */
- int height = 0, width = 0;
- Iterator<Widget> widgetIterator = iterator();
- if (isHorizontal()) {
- height = activeLayoutSize.getHeight();
- int availableWidth = activeLayoutSize.getWidth();
- boolean first = true;
- while (widgetIterator.hasNext()) {
- ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
- .next();
- if (!childComponentContainer
- .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) {
- /*
- * Only components with non-relative size in the main
- * direction has a container size
- */
- width = childComponentContainer.getWidgetSize().getWidth()
- + childComponentContainer
- .getCaptionWidthAfterComponent();
-
- /*
- * If the component does not have a specified size in the
- * main direction the caption may determine the space used
- * by the component
- */
- if (!childComponentContainer
- .widgetHasSizeSpecified(orientation)) {
- int captionWidth = childComponentContainer
- .getCaptionRequiredWidth();
- // ApplicationConnection.getConsole().log(
- // "Component width: " + width
- // + ", caption width: " + captionWidth);
- if (captionWidth > width) {
- width = captionWidth;
- }
- }
- } else {
- width = 0;
- }
-
- if (!isDynamicWidth()) {
- if (availableWidth == 0) {
- /*
- * Let the overflowing components overflow. IE has
- * problems with zero sizes.
- */
- // width = 0;
- // height = 0;
- } else if (width > availableWidth) {
- width = availableWidth;
-
- if (!first) {
- width -= activeSpacing.hSpacing;
- }
- availableWidth = 0;
- } else {
- availableWidth -= width;
- if (!first) {
- availableWidth -= activeSpacing.hSpacing;
- }
- }
-
- first = false;
- }
-
- childComponentContainer.setContainerSize(width, height);
- }
- } else {
- width = activeLayoutSize.getWidth();
- while (widgetIterator.hasNext()) {
- ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
- .next();
-
- if (!childComponentContainer
- .isComponentRelativeSized(ORIENTATION_VERTICAL)) {
- /*
- * Only components with non-relative size in the main
- * direction has a container size
- */
- height = childComponentContainer.getWidgetSize()
- .getHeight()
- + childComponentContainer
- .getCaptionHeightAboveComponent();
- } else {
- height = 0;
- }
-
- childComponentContainer.setContainerSize(width, height);
- }
-
- }
-
- }
-
- private Size updateLayoutDimensions(int totalComponentWidth,
- int totalComponentHeight, int maxComponentWidth,
- int maxComponentHeight) {
-
- /* Only need to calculate dynamic dimensions */
- if (!isDynamicHeight() && !isDynamicWidth()) {
- return activeLayoutSize;
- }
-
- int activeLayoutWidth = 0;
- int activeLayoutHeight = 0;
-
- // Update layout dimensions
- if (isHorizontal()) {
- // Horizontal
- if (isDynamicWidth()) {
- activeLayoutWidth = totalComponentWidth;
- }
-
- if (isDynamicHeight()) {
- activeLayoutHeight = maxComponentHeight;
- }
-
- } else {
- // Vertical
- if (isDynamicWidth()) {
- activeLayoutWidth = maxComponentWidth;
- }
-
- if (isDynamicHeight()) {
- activeLayoutHeight = totalComponentHeight;
- }
- }
-
- if (isDynamicWidth()) {
- setActiveLayoutWidth(activeLayoutWidth);
- setOuterLayoutWidth(activeLayoutSize.getWidth());
- }
-
- if (isDynamicHeight()) {
- setActiveLayoutHeight(activeLayoutHeight);
- setOuterLayoutHeight(activeLayoutSize.getHeight());
- }
-
- return activeLayoutSize;
- }
-
- private void setActiveLayoutWidth(int activeLayoutWidth) {
- if (activeLayoutWidth < 0) {
- activeLayoutWidth = 0;
- }
- activeLayoutSize.setWidth(activeLayoutWidth);
- }
-
- private void setActiveLayoutHeight(int activeLayoutHeight) {
- if (activeLayoutHeight < 0) {
- activeLayoutHeight = 0;
- }
- activeLayoutSize.setHeight(activeLayoutHeight);
-
- }
-
- private void setOuterLayoutWidth(int activeLayoutWidth) {
- // Don't call setWidth to avoid triggering all kinds of recalculations
- // Also don't call super.setWidth to avoid messing with the
- // dynamicWidth property
- int newPixelWidth = (activeLayoutWidth + activeMargins.getHorizontal());
- getElement().getStyle().setWidth(newPixelWidth, Unit.PX);
- }
-
- private void setOuterLayoutHeight(int activeLayoutHeight) {
- // Don't call setHeight to avoid triggering all kinds of recalculations
- // Also don't call super.setHeight to avoid messing with the
- // dynamicHeight property
- int newPixelHeight = (activeLayoutHeight + activeMargins.getVertical());
- getElement().getStyle().setHeight(newPixelHeight, Unit.PX);
- }
-
- /**
- * Updates the spacing between components. Needs to be done only when
- * components are added/removed.
- */
- private void updateContainerMargins() {
- ChildComponentContainer firstChildComponent = getFirstChildComponentContainer();
- if (firstChildComponent != null) {
- firstChildComponent.setMarginLeft(0);
- firstChildComponent.setMarginTop(0);
-
- for (ChildComponentContainer childComponent : widgetToComponentContainer
- .values()) {
- if (childComponent == firstChildComponent) {
- continue;
- }
-
- if (isHorizontal()) {
- childComponent.setMarginLeft(activeSpacing.hSpacing);
- } else {
- childComponent.setMarginTop(activeSpacing.vSpacing);
- }
- }
- }
- }
-
- private boolean isHorizontal() {
- return orientation == ORIENTATION_HORIZONTAL;
- }
-
- private boolean isVertical() {
- return orientation == ORIENTATION_VERTICAL;
- }
-
- private ChildComponentContainer createChildContainer(Widget child) {
-
- // Create a container DIV for the child
- ChildComponentContainer childComponent = new ChildComponentContainer(
- child, orientation);
-
- return childComponent;
-
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- int width = 0;
- int height = 0;
- ChildComponentContainer childComponentContainer = getComponentContainer(child);
- // WIDTH CALCULATION
- if (isVertical()) {
- width = activeLayoutSize.getWidth();
- width -= childComponentContainer.getCaptionWidthAfterComponent();
- } else if (!isDynamicWidth()) {
- // HORIZONTAL
- width = childComponentContainer.getContSize().getWidth();
- width -= childComponentContainer.getCaptionWidthAfterComponent();
- }
-
- // HEIGHT CALCULATION
- if (isHorizontal()) {
- height = activeLayoutSize.getHeight();
- height -= childComponentContainer.getCaptionHeightAboveComponent();
- } else if (!isDynamicHeight()) {
- // VERTICAL
- height = childComponentContainer.getContSize().getHeight();
- height -= childComponentContainer.getCaptionHeightAboveComponent();
- }
-
- // ApplicationConnection.getConsole().log(
- // "allocatedSpace for " + Util.getSimpleName(child) + ": "
- // + width + "," + height);
- RenderSpace space = new RenderSpace(width, height);
- return space;
- }
-
- private void recalculateLayoutAndComponentSizes() {
- recalculateLayout();
-
- if (!(isDynamicHeight() && isDynamicWidth())) {
- /* First update relative sized components */
- for (ChildComponentContainer componentContainer : widgetToComponentContainer
- .values()) {
- client.handleComponentRelativeSize(componentContainer
- .getWidget());
-
- // Update widget size from DOM
- componentContainer.updateWidgetSize();
- }
- }
-
- if (isDynamicHeight()) {
- /*
- * Height is not necessarily correct anymore as the height of
- * components might have changed if the width has changed.
- */
-
- /*
- * Get the new widget sizes from DOM and calculate new container
- * sizes
- */
- updateWidgetSizes();
-
- /* Update layout dimensions based on widget sizes */
- recalculateLayout();
- }
-
- updateRelativeSizesInNonMainDirection();
- calculateAlignments();
-
- setRootSize();
- }
-
- private void setRootSize() {
- root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth());
- root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight());
- }
-
- public boolean requestLayout(Set<Paintable> children) {
- for (Paintable p : children) {
- /* Update widget size from DOM */
- ChildComponentContainer componentContainer = getComponentContainer((Widget) p);
- // This should no longer be needed (after #2563)
- // if (isDynamicWidth()) {
- // componentContainer.setUnlimitedContainerWidth();
- // } else {
- // componentContainer.setLimitedContainerWidth(activeLayoutSize
- // .getWidth());
- // }
-
- componentContainer.updateWidgetSize();
-
- /*
- * If this is the result of an caption icon onload event the caption
- * size may have changed
- */
- componentContainer.updateCaptionSize();
- }
-
- Size sizeBefore = new Size(activeLayoutSize.getWidth(),
- activeLayoutSize.getHeight());
-
- recalculateLayoutAndComponentSizes();
- boolean sameSize = (sizeBefore.equals(activeLayoutSize));
- if (!sameSize) {
- /* Must inform child components about possible size updates */
- client.runDescendentsLayout(this);
- }
-
- /* Automatically propagated upwards if the size has changed */
-
- return sameSize;
- }
-
- @Override
- public void setHeight(String height) {
- Size sizeBefore = new Size(activeLayoutSize.getWidth(),
- activeLayoutSize.getHeight());
-
- super.setHeight(height);
-
- if (height != null && !height.equals("")) {
- setActiveLayoutHeight(getOffsetHeight()
- - activeMargins.getVertical());
- }
-
- if (isRendering) {
- sizeHasChangedDuringRendering = true;
- } else {
- recalculateLayoutAndComponentSizes();
- boolean sameSize = (sizeBefore.equals(activeLayoutSize));
- if (!sameSize) {
- /* Must inform child components about possible size updates */
- client.runDescendentsLayout(this);
- }
- }
- }
-
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width) || !isVisible()) {
- return;
- }
- Size sizeBefore = new Size(activeLayoutSize.getWidth(),
- activeLayoutSize.getHeight());
-
- super.setWidth(width);
- this.width = width;
- if (width != null && !width.equals("")) {
- setActiveLayoutWidth(getOffsetWidth()
- - activeMargins.getHorizontal());
- }
-
- if (isRendering) {
- sizeHasChangedDuringRendering = true;
- } else {
- recalculateLayoutAndComponentSizes();
- boolean sameSize = (sizeBefore.equals(activeLayoutSize));
- if (!sameSize) {
- /* Must inform child components about possible size updates */
- client.runDescendentsLayout(this);
- }
- /*
- * If the height changes as a consequence of this we must inform the
- * parent also
- */
- if (isDynamicHeight()
- && sizeBefore.getHeight() != activeLayoutSize.getHeight()) {
- Util.notifyParentOfSizeChange(this, false);
- }
-
- }
- }
-
- protected void updateAlignmentsAndExpandRatios(UIDL uidl,
- ArrayList<Widget> renderedWidgets) {
-
- /*
- */
- alignments = uidl.getMapAttribute("alignments");
-
- /*
- * UIDL contains a map of paintable ids to expand ratios
- */
-
- expandRatios = uidl.getMapAttribute("expandRatios");
- expandRatioSum = -1.0;
-
- for (int i = 0; i < renderedWidgets.size(); i++) {
- Widget widget = renderedWidgets.get(i);
- String pid = client.getPid(widget.getElement());
-
- ChildComponentContainer container = getComponentContainer(widget);
-
- // Calculate alignment info
- container.setAlignment(getAlignment(pid));
-
- // Update expand ratio
- container.setNormalizedExpandRatio(getExpandRatio(pid));
- }
- }
-
- private AlignmentInfo getAlignment(String pid) {
- if (alignments.containsKey(pid)) {
- return new AlignmentInfo(alignments.getInt(pid));
- } else {
- return AlignmentInfo.TOP_LEFT;
- }
- }
-
- private double getExpandRatio(String pid) {
- if (expandRatioSum < 0) {
- expandRatioSum = 0;
- JsArrayString keyArray = expandRatios.getKeyArray();
- int length = keyArray.length();
- for (int i = 0; i < length; i++) {
- expandRatioSum += expandRatios.getRawNumber(keyArray.get(i));
- }
- if (expandRatioSum == 0) {
- // by default split equally among components
- defaultExpandRatio = 1.0 / widgetToComponentContainer.size();
- } else {
- defaultExpandRatio = 0;
- }
- }
- if (expandRatios.containsKey(pid)) {
- return expandRatios.getRawNumber(pid) / expandRatioSum;
- } else {
- return defaultExpandRatio;
- }
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- ChildComponentContainer componentContainer = getComponentContainer((Widget) component);
- componentContainer.updateCaption(uidl, client);
- if (!isRendering) {
- /*
- * This was a component-only update and the possible size change
- * must be propagated to the layout
- */
- client.captionSizeUpdated(component);
- }
- }
-
- /**
- * 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.
- */
- private Paintable getComponent(Element element) {
- return Util.getPaintableForElement(client, this, element);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java b/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java
index 413f30ad06..df655ef959 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VOverlay.java
@@ -15,7 +15,6 @@ import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Util;
/**
* In Vaadin UI this Overlay should always be used for all elements that
@@ -28,7 +27,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
* The z-index value from where all overlays live. This can be overridden in
* any extending class.
*/
- protected static int Z_INDEX = 20000;
+ public static int Z_INDEX = 20000;
private static int leftFix = -1;
@@ -162,32 +161,18 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
private static int adjustByRelativeTopBodyMargin() {
if (topFix == -1) {
- boolean ie6OrIe7 = BrowserInfo.get().isIE()
- && BrowserInfo.get().getIEVersion() <= 7;
- topFix = detectRelativeBodyFixes("top", ie6OrIe7);
+ topFix = detectRelativeBodyFixes("top");
}
return topFix;
}
- private native static int detectRelativeBodyFixes(String axis,
- boolean removeClientLeftOrTop)
+ private native static int detectRelativeBodyFixes(String axis)
/*-{
try {
var b = $wnd.document.body;
var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
if(cstyle && cstyle.position == 'relative') {
- var offset = b.getBoundingClientRect()[axis];
- if (removeClientLeftOrTop) {
- // IE6 and IE7 include the top left border of the client area into the boundingClientRect
- var clientTopOrLeft = 0;
- if (axis == "top")
- clientTopOrLeft = $wnd.document.documentElement.clientTop;
- else
- clientTopOrLeft = $wnd.document.documentElement.clientLeft;
-
- offset -= clientTopOrLeft;
- }
- return offset;
+ return b.getBoundingClientRect()[axis];
}
} catch(e){}
return 0;
@@ -195,9 +180,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
private static int adjustByRelativeLeftBodyMargin() {
if (leftFix == -1) {
- boolean ie6OrIe7 = BrowserInfo.get().isIE()
- && BrowserInfo.get().getIEVersion() <= 7;
- leftFix = detectRelativeBodyFixes("left", ie6OrIe7);
+ leftFix = detectRelativeBodyFixes("left");
}
return leftFix;
@@ -214,13 +197,6 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
updateShadowSizeAndPosition(1.0);
}
}
- Util.runIE7ZeroSizedBodyFix();
- }
-
- @Override
- public void hide(boolean autoClosed) {
- super.hide(autoClosed);
- Util.runIE7ZeroSizedBodyFix();
}
@Override
@@ -273,7 +249,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
* size of overlay without using normal 'setWidth(String)' and
* 'setHeight(String)' methods (if not calling super.setWidth/Height).
*/
- protected void updateShadowSizeAndPosition() {
+ public void updateShadowSizeAndPosition() {
updateShadowSizeAndPosition(1.0);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java
deleted file mode 100644
index 036f4f0600..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.Set;
-
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.event.dom.client.DomEvent.Type;
-import com.google.gwt.event.dom.client.TouchStartEvent;
-import com.google.gwt.event.dom.client.TouchStartHandler;
-import com.google.gwt.event.shared.EventHandler;
-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.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
-
-public class VPanel extends SimplePanel implements Container,
- ShortcutActionHandlerOwner, Focusable {
-
- public static final String CLICK_EVENT_IDENTIFIER = "click";
- public static final String CLASSNAME = "v-panel";
-
- ApplicationConnection client;
-
- String id;
-
- private final Element captionNode = DOM.createDiv();
-
- private final Element captionText = DOM.createSpan();
-
- private Icon icon;
-
- private final Element bottomDecoration = DOM.createDiv();
-
- private final Element contentNode = DOM.createDiv();
-
- private Element errorIndicatorElement;
-
- private String height;
-
- private Paintable layout;
-
- ShortcutActionHandler shortcutHandler;
-
- private String width = "";
-
- private Element geckoCaptionMeter;
-
- private int scrollTop;
-
- private int scrollLeft;
-
- private RenderInformation renderInformation = new RenderInformation();
-
- private int borderPaddingHorizontal = -1;
-
- private int borderPaddingVertical = -1;
-
- private int captionPaddingHorizontal = -1;
-
- private int captionMarginLeft = -1;
-
- private boolean rendering;
-
- private int contentMarginLeft = -1;
-
- private String previousStyleName;
-
- private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
- CLICK_EVENT_IDENTIFIER) {
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
- private TouchScrollDelegate touchScrollDelegate;
-
- public VPanel() {
- super();
- DivElement captionWrap = Document.get().createDivElement();
- captionWrap.appendChild(captionNode);
- captionNode.appendChild(captionText);
-
- captionWrap.setClassName(CLASSNAME + "-captionwrap");
- captionNode.setClassName(CLASSNAME + "-caption");
- contentNode.setClassName(CLASSNAME + "-content");
- bottomDecoration.setClassName(CLASSNAME + "-deco");
-
- getElement().appendChild(captionWrap);
-
- /*
- * Make contentNode focusable only by using the setFocus() method. This
- * behaviour can be changed by invoking setTabIndex() in the serverside
- * implementation
- */
- contentNode.setTabIndex(-1);
-
- getElement().appendChild(contentNode);
-
- getElement().appendChild(bottomDecoration);
- setStyleName(CLASSNAME);
- DOM.sinkEvents(getElement(), Event.ONKEYDOWN);
- DOM.sinkEvents(contentNode, Event.ONSCROLL | Event.TOUCHEVENTS);
- contentNode.getStyle().setProperty("position", "relative");
- getElement().getStyle().setProperty("overflow", "hidden");
- addHandler(new TouchStartHandler() {
- public void onTouchStart(TouchStartEvent event) {
- getTouchScrollDelegate().onTouchStart(event);
- }
- }, TouchStartEvent.getType());
- }
-
- /**
- * Sets the keyboard focus on the Panel
- *
- * @param focus
- * Should the panel have focus or not.
- */
- public void setFocus(boolean focus) {
- if (focus) {
- getContainerElement().focus();
- } else {
- getContainerElement().blur();
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.gwt.client.Focusable#focus()
- */
- public void focus() {
- setFocus(true);
-
- }
-
- @Override
- protected Element getContainerElement() {
- return contentNode;
- }
-
- private void setCaption(String text) {
- DOM.setInnerHTML(captionText, text);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- if (!uidl.hasAttribute("cached")) {
-
- // Handle caption displaying and style names, prior generics.
- // Affects size
- // calculations
-
- // Restore default stylenames
- contentNode.setClassName(CLASSNAME + "-content");
- bottomDecoration.setClassName(CLASSNAME + "-deco");
- captionNode.setClassName(CLASSNAME + "-caption");
- boolean hasCaption = false;
- if (uidl.hasAttribute("caption")
- && !uidl.getStringAttribute("caption").equals("")) {
- setCaption(uidl.getStringAttribute("caption"));
- hasCaption = true;
- } else {
- setCaption("");
- captionNode.setClassName(CLASSNAME + "-nocaption");
- }
-
- // Add proper stylenames for all elements. This way we can prevent
- // unwanted CSS selector inheritance.
- if (uidl.hasAttribute("style")) {
- final String[] styles = uidl.getStringAttribute("style").split(
- " ");
- final String captionBaseClass = CLASSNAME
- + (hasCaption ? "-caption" : "-nocaption");
- final String contentBaseClass = CLASSNAME + "-content";
- final String decoBaseClass = CLASSNAME + "-deco";
- String captionClass = captionBaseClass;
- String contentClass = contentBaseClass;
- String decoClass = decoBaseClass;
- for (int i = 0; i < styles.length; i++) {
- captionClass += " " + captionBaseClass + "-" + styles[i];
- contentClass += " " + contentBaseClass + "-" + styles[i];
- decoClass += " " + decoBaseClass + "-" + styles[i];
- }
- captionNode.setClassName(captionClass);
- contentNode.setClassName(contentClass);
- bottomDecoration.setClassName(decoClass);
-
- }
- }
- // Ensure correct implementation
- if (client.updateComponent(this, uidl, false)) {
- rendering = false;
- return;
- }
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- this.client = client;
- id = uidl.getId();
-
- setIconUri(uidl, client);
-
- handleError(uidl);
-
- // Render content
- final UIDL layoutUidl = uidl.getChildUIDL(0);
- final Paintable newLayout = client.getPaintable(layoutUidl);
- if (newLayout != layout) {
- if (layout != null) {
- client.unregisterPaintable(layout);
- }
- setWidget((Widget) newLayout);
- layout = newLayout;
- }
- layout.updateFromUIDL(layoutUidl, client);
-
- // We may have actions attached to this panel
- if (uidl.getChildCount() > 1) {
- final int cnt = uidl.getChildCount();
- for (int i = 1; i < cnt; i++) {
- UIDL childUidl = uidl.getChildUIDL(i);
- if (childUidl.getTag().equals("actions")) {
- if (shortcutHandler == null) {
- shortcutHandler = new ShortcutActionHandler(id, client);
- }
- shortcutHandler.updateActionMap(childUidl);
- }
- }
- }
-
- if (uidl.hasVariable("scrollTop")
- && uidl.getIntVariable("scrollTop") != scrollTop) {
- scrollTop = uidl.getIntVariable("scrollTop");
- contentNode.setScrollTop(scrollTop);
- // re-read the actual scrollTop in case invalid value was set
- // (scrollTop != 0 when no scrollbar exists, other values would be
- // caught by scroll listener), see #3784
- scrollTop = contentNode.getScrollTop();
- }
-
- if (uidl.hasVariable("scrollLeft")
- && uidl.getIntVariable("scrollLeft") != scrollLeft) {
- scrollLeft = uidl.getIntVariable("scrollLeft");
- contentNode.setScrollLeft(scrollLeft);
- // re-read the actual scrollTop in case invalid value was set
- // (scrollTop != 0 when no scrollbar exists, other values would be
- // caught by scroll listener), see #3784
- scrollLeft = contentNode.getScrollLeft();
- }
-
- // Must be run after scrollTop is set as Webkit overflow fix re-sets the
- // scrollTop
- runHacks(false);
-
- // And apply tab index
- if (uidl.hasVariable("tabindex")) {
- contentNode.setTabIndex(uidl.getIntVariable("tabindex"));
- }
-
- rendering = false;
-
- }
-
- @Override
- public void setStyleName(String style) {
- if (!style.equals(previousStyleName)) {
- super.setStyleName(style);
- detectContainerBorders();
- previousStyleName = style;
- }
- }
-
- private void handleError(UIDL uidl) {
- if (uidl.hasAttribute("error")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createSpan();
- DOM.setElementProperty(errorIndicatorElement, "className",
- "v-errorindicator");
- DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS);
- sinkEvents(Event.MOUSEEVENTS);
- }
- DOM.insertBefore(captionNode, errorIndicatorElement, captionText);
- } else if (errorIndicatorElement != null) {
- DOM.removeChild(captionNode, errorIndicatorElement);
- errorIndicatorElement = null;
- }
- }
-
- private void setIconUri(UIDL uidl, ApplicationConnection client) {
- final String iconUri = uidl.hasAttribute("icon") ? uidl
- .getStringAttribute("icon") : null;
- 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);
- }
- }
-
- public void runHacks(boolean runGeckoFix) {
- if (BrowserInfo.get().isIE6() && width != null && !width.equals("")) {
- /*
- * IE6 requires overflow-hidden elements to have a width specified
- * so we calculate the width of the content and caption nodes when
- * no width has been specified.
- */
- /*
- * Fixes #1923 VPanel: Horizontal scrollbar does not appear in IE6
- * with wide content
- */
-
- /*
- * Caption must be shrunk for parent measurements to return correct
- * result in IE6
- */
- DOM.setStyleAttribute(captionNode, "width", "1px");
-
- int parentPadding = Util.measureHorizontalPaddingAndBorder(
- getElement(), 0);
-
- int parentWidthExcludingPadding = getElement().getOffsetWidth()
- - parentPadding;
-
- Util.setWidthExcludingPaddingAndBorder(captionNode,
- parentWidthExcludingPadding - getCaptionMarginLeft(), 26,
- false);
-
- int contentMarginLeft = getContentMarginLeft();
-
- Util.setWidthExcludingPaddingAndBorder(contentNode,
- parentWidthExcludingPadding - contentMarginLeft, 2, false);
-
- }
-
- if ((BrowserInfo.get().isIE() || BrowserInfo.get().isFF2())
- && (width == null || width.equals(""))) {
- /*
- * IE and FF2 needs width to be specified for the root DIV so we
- * calculate that from the sizes of the caption and layout
- */
- int captionWidth = captionText.getOffsetWidth()
- + getCaptionMarginLeft() + getCaptionPaddingHorizontal();
- int layoutWidth = ((Widget) layout).getOffsetWidth()
- + getContainerBorderWidth();
- int width = layoutWidth;
- if (captionWidth > width) {
- width = captionWidth;
- }
-
- if (BrowserInfo.get().isIE7()) {
- Util.setWidthExcludingPaddingAndBorder(captionNode, width
- - getCaptionMarginLeft(), 26, false);
- }
-
- super.setWidth(width + "px");
- }
-
- if (runGeckoFix && BrowserInfo.get().isGecko()) {
- // workaround for #1764
- if (width == null || width.equals("")) {
- if (geckoCaptionMeter == null) {
- geckoCaptionMeter = DOM.createDiv();
- DOM.appendChild(captionNode, geckoCaptionMeter);
- }
- int captionWidth = DOM.getElementPropertyInt(captionText,
- "offsetWidth");
- int availWidth = DOM.getElementPropertyInt(geckoCaptionMeter,
- "offsetWidth");
- if (captionWidth == availWidth) {
- /*
- * Caption width defines panel width -> Gecko based browsers
- * somehow fails to float things right, without the
- * "noncode" below
- */
- setWidth(getOffsetWidth() + "px");
- } else {
- DOM.setStyleAttribute(captionNode, "width", "");
- }
- }
- }
-
- client.runDescendentsLayout(this);
-
- Util.runWebkitOverflowAutoFix(contentNode);
-
- }
-
- @Override
- 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);
- return;
- }
- if (type == Event.ONSCROLL) {
- int newscrollTop = DOM.getElementPropertyInt(contentNode,
- "scrollTop");
- int newscrollLeft = DOM.getElementPropertyInt(contentNode,
- "scrollLeft");
- if (client != null
- && (newscrollLeft != scrollLeft || newscrollTop != scrollTop)) {
- scrollLeft = newscrollLeft;
- scrollTop = newscrollTop;
- client.updateVariable(id, "scrollTop", scrollTop, false);
- client.updateVariable(id, "scrollLeft", scrollLeft, false);
- }
- } else if (captionNode.isOrHasChild(target)) {
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- }
- }
-
- protected TouchScrollDelegate getTouchScrollDelegate() {
- if (touchScrollDelegate == null) {
- touchScrollDelegate = new TouchScrollDelegate(contentNode);
- }
- return touchScrollDelegate;
-
- }
-
- @Override
- public void setHeight(String height) {
- this.height = height;
- super.setHeight(height);
- if (height != null && !"".equals(height)) {
- final int targetHeight = getOffsetHeight();
- int containerHeight = targetHeight
- - captionNode.getParentElement().getOffsetHeight()
- - bottomDecoration.getOffsetHeight()
- - getContainerBorderHeight();
- if (containerHeight < 0) {
- containerHeight = 0;
- }
- DOM.setStyleAttribute(contentNode, "height", containerHeight + "px");
- } else {
- DOM.setStyleAttribute(contentNode, "height", "");
- }
- if (!rendering) {
- runHacks(true);
- }
- }
-
- private int getCaptionMarginLeft() {
- if (captionMarginLeft < 0) {
- detectContainerBorders();
- }
- return captionMarginLeft;
- }
-
- private int getContentMarginLeft() {
- if (contentMarginLeft < 0) {
- detectContainerBorders();
- }
- return contentMarginLeft;
- }
-
- private int getCaptionPaddingHorizontal() {
- if (captionPaddingHorizontal < 0) {
- detectContainerBorders();
- }
- return captionPaddingHorizontal;
- }
-
- private int getContainerBorderHeight() {
- if (borderPaddingVertical < 0) {
- detectContainerBorders();
- }
- return borderPaddingVertical;
- }
-
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
-
- this.width = width;
- super.setWidth(width);
- if (!rendering) {
- runHacks(true);
-
- if (height.equals("")) {
- // Width change may affect height
- Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this);
- }
-
- }
- }
-
- private int getContainerBorderWidth() {
- if (borderPaddingHorizontal < 0) {
- detectContainerBorders();
- }
- return borderPaddingHorizontal;
- }
-
- private void detectContainerBorders() {
- DOM.setStyleAttribute(contentNode, "overflow", "hidden");
-
- borderPaddingHorizontal = Util.measureHorizontalBorder(contentNode);
- borderPaddingVertical = Util.measureVerticalBorder(contentNode);
-
- DOM.setStyleAttribute(contentNode, "overflow", "auto");
-
- captionPaddingHorizontal = Util.measureHorizontalPaddingAndBorder(
- captionNode, 26);
-
- captionMarginLeft = Util.measureMarginLeft(captionNode);
- contentMarginLeft = Util.measureMarginLeft(contentNode);
-
- }
-
- public boolean hasChildComponent(Widget component) {
- if (component != null && component == layout) {
- return true;
- } else {
- return false;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- // TODO This is untested as no layouts require this
- if (oldComponent != layout) {
- return;
- }
-
- setWidget(newComponent);
- layout = (Paintable) newComponent;
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- int w = 0;
- int h = 0;
-
- if (width != null && !width.equals("")) {
- w = getOffsetWidth() - getContainerBorderWidth();
- if (w < 0) {
- w = 0;
- }
- }
-
- if (height != null && !height.equals("")) {
- h = contentNode.getOffsetHeight() - getContainerBorderHeight();
- if (h < 0) {
- h = 0;
- }
- }
-
- return new RenderSpace(w, h, true);
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- // content size change might cause change to its available space
- // (scrollbars)
- client.handleComponentRelativeSize((Widget) layout);
- if (height != null && height != "" && width != null && width != "") {
- /*
- * If the height and width has been specified the child components
- * cannot make the size of the layout change
- */
- return true;
- }
- runHacks(false);
- return !renderInformation.updateSize(getElement());
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP: layouts caption, errors etc not rendered in Panel
- }
-
- @Override
- protected void onAttach() {
- super.onAttach();
- detectContainerBorders();
- }
-
- public ShortcutActionHandler getShortcutActionHandler() {
- return shortcutHandler;
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java
deleted file mode 100644
index ff1e2e6b78..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-public class VSplitPanelHorizontal extends VSplitPanel {
-
- public VSplitPanelHorizontal() {
- super(VSplitPanel.ORIENTATION_HORIZONTAL);
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java
deleted file mode 100644
index dcf7622e50..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-public class VSplitPanelVertical extends VSplitPanel {
-
- public VSplitPanelVertical() {
- super(VSplitPanel.ORIENTATION_VERTICAL);
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java b/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java
deleted file mode 100644
index d3cb3130b0..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Set;
-
-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.Event;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Label;
-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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-
-/**
- * TODO make this work (just an early prototype). We may want to have paging
- * style table which will be much lighter than VScrollTable is.
- */
-public class VTablePaging extends Composite implements Table, Paintable,
- ClickHandler {
-
- private final Grid tBody = new Grid();
- private final Button nextPage = new Button("&gt;");
- private final Button prevPage = new Button("&lt;");
- private final Button firstPage = new Button("&lt;&lt;");
- private final Button lastPage = new Button("&gt;&gt;");
-
- private int pageLength = 15;
-
- private boolean rowHeaders = false;
-
- private ApplicationConnection client;
- private String id;
-
- private boolean immediate = false;
-
- private int selectMode = Table.SELECT_MODE_NONE;
-
- private final ArrayList<String> selectedRowKeys = new ArrayList<String>();
-
- private int totalRows;
-
- private final HashMap<?, ?> visibleColumns = new HashMap<Object, Object>();
-
- private int rows;
-
- private int firstRow;
- private boolean sortAscending = true;
- private final HorizontalPanel pager;
-
- public HashMap<String, TableRow> rowKeysToTableRows = new HashMap<String, TableRow>();
-
- public VTablePaging() {
-
- tBody.setStyleName("itable-tbody");
-
- final VerticalPanel panel = new VerticalPanel();
-
- pager = new HorizontalPanel();
- pager.add(firstPage);
- firstPage.addClickHandler(this);
- pager.add(prevPage);
- prevPage.addClickHandler(this);
- pager.add(nextPage);
- nextPage.addClickHandler(this);
- pager.add(lastPage);
- lastPage.addClickHandler(this);
-
- panel.add(pager);
- panel.add(tBody);
-
- initWidget(panel);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- this.client = client;
- id = uidl.getStringAttribute("id");
- immediate = uidl.getBooleanAttribute("immediate");
- totalRows = uidl.getIntAttribute("totalrows");
- pageLength = uidl.getIntAttribute("pagelength");
- firstRow = uidl.getIntAttribute("firstrow");
- rows = uidl.getIntAttribute("rows");
-
- if (uidl.hasAttribute("selectmode")) {
- if (uidl.getStringAttribute("selectmode").equals("multi")) {
- selectMode = Table.SELECT_MODE_MULTI;
- } else {
- selectMode = Table.SELECT_MODE_SINGLE;
- }
-
- if (uidl.hasAttribute("selected")) {
- final Set<String> selectedKeys = uidl
- .getStringArrayVariableAsSet("selected");
- selectedRowKeys.clear();
- for (final Iterator<String> it = selectedKeys.iterator(); it
- .hasNext();) {
- selectedRowKeys.add(it.next());
- }
- }
- }
-
- if (uidl.hasVariable("sortascending")) {
- sortAscending = uidl.getBooleanVariable("sortascending");
- }
-
- if (uidl.hasAttribute("rowheaders")) {
- rowHeaders = true;
- }
-
- UIDL rowData = null;
- UIDL visibleColumns = null;
- for (final Iterator<?> it = uidl.getChildIterator(); it.hasNext();) {
- final UIDL c = (UIDL) it.next();
- if (c.getTag().equals("rows")) {
- rowData = c;
- } else if (c.getTag().equals("actions")) {
- updateActionMap(c);
- } else if (c.getTag().equals("visiblecolumns")) {
- visibleColumns = c;
- }
- }
- tBody.resize(rows + 1, uidl.getIntAttribute("cols")
- + (rowHeaders ? 1 : 0));
- updateHeader(visibleColumns);
- updateBody(rowData);
-
- updatePager();
- }
-
- private void updateHeader(UIDL c) {
- final Iterator<?> it = c.getChildIterator();
- visibleColumns.clear();
- int colIndex = (rowHeaders ? 1 : 0);
- while (it.hasNext()) {
- final UIDL col = (UIDL) it.next();
- final String cid = col.getStringAttribute("cid");
- if (!col.hasAttribute("collapsed")) {
- tBody.setWidget(0, colIndex,
- new HeaderCell(cid, col.getStringAttribute("caption")));
-
- }
- colIndex++;
- }
- }
-
- private void updateActionMap(UIDL c) {
- // TODO Auto-generated method stub
-
- }
-
- /**
- * Updates row data from uidl. UpdateFromUIDL delegates updating tBody to
- * this method.
- *
- * Updates may be to different part of tBody, depending on update type. It
- * can be initial row data, scroll up, scroll down...
- *
- * @param uidl
- * which contains row data
- */
- private void updateBody(UIDL uidl) {
- final Iterator<?> it = uidl.getChildIterator();
-
- int curRowIndex = 1;
- while (it.hasNext()) {
- final UIDL rowUidl = (UIDL) it.next();
- final TableRow row = new TableRow(curRowIndex,
- String.valueOf(rowUidl.getIntAttribute("key")),
- rowUidl.hasAttribute("selected"));
- int colIndex = 0;
- if (rowHeaders) {
- tBody.setWidget(curRowIndex, colIndex, new BodyCell(row,
- rowUidl.getStringAttribute("caption")));
- colIndex++;
- }
- final Iterator<?> cells = rowUidl.getChildIterator();
- while (cells.hasNext()) {
- final Object cell = cells.next();
- if (cell instanceof String) {
- tBody.setWidget(curRowIndex, colIndex, new BodyCell(row,
- (String) cell));
- } else {
- final Paintable cellContent = client
- .getPaintable((UIDL) cell);
- final BodyCell bodyCell = new BodyCell(row);
- bodyCell.setWidget((Widget) cellContent);
- tBody.setWidget(curRowIndex, colIndex, bodyCell);
- }
- colIndex++;
- }
- curRowIndex++;
- }
- }
-
- private void updatePager() {
- if (pageLength == 0) {
- pager.setVisible(false);
- return;
- }
- if (isFirstPage()) {
- firstPage.setEnabled(false);
- prevPage.setEnabled(false);
- } else {
- firstPage.setEnabled(true);
- prevPage.setEnabled(true);
- }
- if (hasNextPage()) {
- nextPage.setEnabled(true);
- lastPage.setEnabled(true);
- } else {
- nextPage.setEnabled(false);
- lastPage.setEnabled(false);
-
- }
- }
-
- private boolean hasNextPage() {
- if (firstRow + rows + 1 > totalRows) {
- return false;
- }
- return true;
- }
-
- private boolean isFirstPage() {
- if (firstRow == 0) {
- return true;
- }
- return false;
- }
-
- public void onClick(ClickEvent event) {
- Object sender = event.getSource();
- if (sender instanceof Button) {
- if (sender == firstPage) {
- client.updateVariable(id, "firstvisible", 0, true);
- } else if (sender == nextPage) {
- client.updateVariable(id, "firstvisible",
- firstRow + pageLength, true);
- } else if (sender == prevPage) {
- int newFirst = firstRow - pageLength;
- if (newFirst < 0) {
- newFirst = 0;
- }
- client.updateVariable(id, "firstvisible", newFirst, true);
- } else if (sender == lastPage) {
- client.updateVariable(id, "firstvisible", totalRows
- - pageLength, true);
- }
- }
- if (sender instanceof HeaderCell) {
- final HeaderCell hCell = (HeaderCell) sender;
- client.updateVariable(id, "sortcolumn", hCell.getCid(), false);
- client.updateVariable(id, "sortascending", (sortAscending ? false
- : true), true);
- }
- }
-
- private class HeaderCell extends HTML {
-
- private String cid;
-
- public String getCid() {
- return cid;
- }
-
- public void setCid(String pid) {
- cid = pid;
- }
-
- HeaderCell(String pid, String caption) {
- super();
- cid = pid;
- addClickHandler(VTablePaging.this);
- setText(caption);
- // TODO remove debug color
- DOM.setStyleAttribute(getElement(), "color", "brown");
- DOM.setStyleAttribute(getElement(), "font-weight", "bold");
- }
- }
-
- /**
- * Abstraction of table cell content. In needs to know on which row it is in
- * case of context click.
- *
- * @author mattitahvonen
- */
- public class BodyCell extends SimplePanel {
- private final TableRow row;
-
- public BodyCell(TableRow row) {
- super();
- sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT);
- this.row = row;
- }
-
- public BodyCell(TableRow row2, String textContent) {
- super();
- sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT);
- row = row2;
- setWidget(new Label(textContent));
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- System.out.println("CEll event: " + event.toString());
- switch (DOM.eventGetType(event)) {
- case Event.BUTTON_RIGHT:
- row.showContextMenu(event);
- Window.alert("context menu un-implemented");
- DOM.eventCancelBubble(event, true);
- break;
- case Event.BUTTON_LEFT:
- if (selectMode > Table.SELECT_MODE_NONE) {
- row.toggleSelected();
- }
- break;
- default:
- break;
- }
- super.onBrowserEvent(event);
- }
- }
-
- private class TableRow {
-
- private final String key;
- private final int rowIndex;
- private boolean selected = false;
-
- public TableRow(int rowIndex, String rowKey, boolean selected) {
- rowKeysToTableRows.put(rowKey, this);
- this.rowIndex = rowIndex;
- key = rowKey;
- setSelected(selected);
- }
-
- /**
- * This method is used to set row status. Does not change value on
- * server.
- *
- * @param selected
- */
- public void setSelected(boolean sel) {
- selected = sel;
- if (selected) {
- selectedRowKeys.add(key);
- DOM.setStyleAttribute(
- tBody.getRowFormatter().getElement(rowIndex),
- "background", "yellow");
-
- } else {
- selectedRowKeys.remove(key);
- DOM.setStyleAttribute(
- tBody.getRowFormatter().getElement(rowIndex),
- "background", "transparent");
- }
- }
-
- public void setContextMenuOptions(HashMap<?, ?> options) {
-
- }
-
- /**
- * Toggles rows select state. Also updates state to server according to
- * tables immediate flag.
- *
- */
- public void toggleSelected() {
- if (selected) {
- setSelected(false);
- } else {
- if (selectMode == Table.SELECT_MODE_SINGLE) {
- deselectAll();
- }
- setSelected(true);
- }
- client.updateVariable(
- id,
- "selected",
- selectedRowKeys.toArray(new String[selectedRowKeys.size()]),
- immediate);
- }
-
- /**
- * Shows context menu for this row.
- *
- * @param event
- * Event which triggered context menu. Correct place for
- * context menu can be determined with it.
- */
- public void showContextMenu(Event event) {
- System.out.println("TODO: Show context menu");
- }
- }
-
- public void deselectAll() {
- final Object[] keys = selectedRowKeys.toArray();
- for (int i = 0; i < keys.length; i++) {
- final TableRow tableRow = rowKeysToTableRows.get(keys[i]);
- if (tableRow != null) {
- tableRow.setSelected(false);
- }
- }
- // still ensure all selects are removed from
- selectedRowKeys.clear();
- }
-
- public void add(Widget w) {
- // TODO Auto-generated method stub
-
- }
-
- public void clear() {
- // TODO Auto-generated method stub
-
- }
-
- public Iterator<Widget> iterator() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public boolean remove(Widget w) {
- // TODO Auto-generated method stub
- return false;
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java
deleted file mode 100644
index 7304f62f41..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-
-abstract class VTabsheetBase extends ComplexPanel implements Container {
-
- String id;
- ApplicationConnection client;
-
- protected final ArrayList<String> tabKeys = new ArrayList<String>();
- protected int activeTabIndex = 0;
- protected boolean disabled;
- protected boolean readonly;
- protected Set<String> disabledTabKeys = new HashSet<String>();
- protected boolean cachedUpdate = false;
-
- public VTabsheetBase(String classname) {
- setElement(DOM.createDiv());
- setStyleName(classname);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
-
- // Ensure correct implementation
- cachedUpdate = client.updateComponent(this, uidl, true);
- if (cachedUpdate) {
- return;
- }
-
- // Update member references
- id = uidl.getId();
- disabled = uidl.hasAttribute("disabled");
-
- // Render content
- final UIDL tabs = uidl.getChildUIDL(0);
-
- // Paintables in the TabSheet before update
- ArrayList<Object> oldPaintables = new ArrayList<Object>();
- for (Iterator<Object> iterator = getPaintableIterator(); iterator
- .hasNext();) {
- oldPaintables.add(iterator.next());
- }
-
- // Clear previous values
- tabKeys.clear();
- disabledTabKeys.clear();
-
- 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("disabled")) {
- disabledTabKeys.add(key);
- }
-
- tabKeys.add(key);
-
- if (selected) {
- activeTabIndex = index;
- }
- renderTab(tab, index, selected, hidden);
- index++;
- }
-
- int tabCount = getTabCount();
- while (tabCount-- > index) {
- removeTab(index);
- }
-
- for (int i = 0; i < getTabCount(); i++) {
- Paintable p = getTab(i);
- oldPaintables.remove(p);
- }
-
- // Perform unregister for any paintables removed during update
- for (Iterator<Object> iterator = oldPaintables.iterator(); iterator
- .hasNext();) {
- Object oldPaintable = iterator.next();
- if (oldPaintable instanceof Paintable) {
- Widget w = (Widget) oldPaintable;
- if (w.isAttached()) {
- w.removeFromParent();
- }
- client.unregisterPaintable((Paintable) oldPaintable);
- }
- }
-
- }
-
- /**
- * @return a list of currently shown Paintables
- *
- * Apparently can be something else than Paintable as
- * {@link #updateFromUIDL(UIDL, ApplicationConnection)} checks if
- * instanceof Paintable. Therefore set to <Object>
- */
- abstract protected Iterator<Object> getPaintableIterator();
-
- /**
- * Clears current tabs and contents
- */
- abstract protected void clearPaintables();
-
- /**
- * Implement in extending classes. This method should render needed elements
- * and set the visibility of the tab according to the 'selected' parameter.
- */
- protected abstract void renderTab(final UIDL tabUidl, int index,
- boolean selected, boolean hidden);
-
- /**
- * Implement in extending classes. This method should render any previously
- * non-cached content and set the activeTabIndex property to the specified
- * index.
- */
- protected abstract void selectTab(int index, final UIDL contentUidl);
-
- /**
- * Implement in extending classes. This method should return the number of
- * tabs currently rendered.
- */
- protected abstract int getTabCount();
-
- /**
- * Implement in extending classes. This method should return the Paintable
- * corresponding to the given index.
- */
- protected abstract Paintable getTab(int index);
-
- /**
- * Implement in extending classes. This method should remove the rendered
- * tab with the specified index.
- */
- protected abstract void removeTab(int index);
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java b/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java
index c7442e4436..7bcdcec660 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java
@@ -6,18 +6,13 @@ package com.vaadin.terminal.gwt.client.ui;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.VerticalPanel;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.SimpleTree;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.VUIDLBrowser;
-public class VUnknownComponent extends Composite implements Paintable {
+public class VUnknownComponent extends Composite {
com.google.gwt.user.client.ui.Label caption = new com.google.gwt.user.client.ui.Label();;
SimpleTree uidlTree;
- private VerticalPanel panel;
- private String serverClassName = "unkwnown";
+ protected VerticalPanel panel;
public VUnknownComponent() {
panel = new VerticalPanel();
@@ -27,32 +22,6 @@ public class VUnknownComponent extends Composite implements Paintable {
caption.setStyleName("vaadin-unknown-caption");
}
- public void setServerSideClassName(String serverClassName) {
- this.serverClassName = serverClassName;
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
- setCaption("Widgetset does not contain implementation for "
- + serverClassName
- + ". Check its @ClientWidget mapping, widgetsets "
- + "GWT module description file and re-compile your"
- + " widgetset. In case you have downloaded a vaadin"
- + " add-on package, you might want to refer to "
- + "<a href='http://vaadin.com/using-addons'>add-on "
- + "instructions</a>. Unrendered UIDL:");
- if (uidlTree != null) {
- uidlTree.removeFromParent();
- }
-
- uidlTree = new VUIDLBrowser(uidl, client.getConfiguration());
- uidlTree.open(true);
- uidlTree.setText("Unrendered UIDL");
- panel.add(uidlTree);
- }
-
public void setCaption(String c) {
caption.getElement().setInnerHTML(c);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java b/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java
deleted file mode 100644
index db01699383..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-
-/**
- * Client side implementation for UriFragmentUtility. Uses GWT's History object
- * as an implementation.
- *
- */
-public class VUriFragmentUtility extends Widget implements Paintable,
- ValueChangeHandler<String> {
-
- private String fragment;
- private ApplicationConnection client;
- private String paintableId;
- private boolean immediate;
- private HandlerRegistration historyValueHandlerRegistration;
-
- public VUriFragmentUtility() {
- setElement(Document.get().createDivElement());
- if (BrowserInfo.get().isIE6()) {
- getElement().getStyle().setProperty("overflow", "hidden");
- getElement().getStyle().setProperty("height", "0");
- }
- }
-
- @Override
- protected void onAttach() {
- super.onAttach();
- historyValueHandlerRegistration = History.addValueChangeHandler(this);
- History.fireCurrentHistoryState();
- }
-
- @Override
- protected void onDetach() {
- super.onDetach();
- historyValueHandlerRegistration.removeHandler();
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
- String uidlFragment = uidl.getStringVariable("fragment");
- immediate = uidl.getBooleanAttribute("immediate");
- if (this.client == null) {
- // initial paint has some special logic
- this.client = client;
- paintableId = uidl.getId();
- if (!fragment.equals(uidlFragment)) {
- // initial server side fragment (from link/bookmark/typed) does
- // not equal the one on
- // server, send initial fragment to server
- History.fireCurrentHistoryState();
- }
- } else {
- if (uidlFragment != null && !uidlFragment.equals(fragment)) {
- fragment = uidlFragment;
- // normal fragment change from server, add new history item
- History.newItem(uidlFragment, false);
- }
- }
- }
-
- public void onValueChange(ValueChangeEvent<String> event) {
- String historyToken = event.getValue();
- fragment = historyToken;
- if (client != null) {
- client.updateVariable(paintableId, "fragment", fragment, immediate);
- }
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java
deleted file mode 100644
index fe5749ec8b..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui;
-
-public class VVerticalLayout extends VOrderedLayout {
-
- public static final String CLASSNAME = "v-verticallayout";
-
- public VVerticalLayout() {
- super(CLASSNAME, ORIENTATION_VERTICAL);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java
deleted file mode 100644
index 07ade6a8b1..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/VView.java
+++ /dev/null
@@ -1,779 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.terminal.gwt.client.ui;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.event.dom.client.DomEvent.Type;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.shared.EventHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
-
-/**
- *
- */
-public class VView extends SimplePanel implements Container, ResizeHandler,
- Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable {
-
- private static final String CLASSNAME = "v-view";
-
- public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain";
-
- private String theme;
-
- private Paintable layout;
-
- private final LinkedHashSet<VWindow> subWindows = new LinkedHashSet<VWindow>();
-
- private String id;
-
- private ShortcutActionHandler actionHandler;
-
- /** stored width for IE resize optimization */
- private int width;
-
- /** stored height for IE resize optimization */
- private int height;
-
- private ApplicationConnection connection;
-
- /**
- * We are postponing resize process with IE. IE bugs with scrollbars in some
- * situations, that causes false onWindowResized calls. With Timer we will
- * give IE some time to decide if it really wants to keep current size
- * (scrollbars).
- */
- private Timer resizeTimer;
-
- private int scrollTop;
-
- private int scrollLeft;
-
- private boolean rendering;
-
- private boolean scrollable;
-
- private boolean immediate;
-
- private boolean resizeLazy = false;
-
- public static final String RESIZE_LAZY = "rL";
- /**
- * Reference to the parent frame/iframe. Null if there is no parent (i)frame
- * or if the application and parent frame are in different domains.
- */
- private Element parentFrame;
-
- private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
- VPanel.CLICK_EVENT_IDENTIFIER) {
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
- new ScheduledCommand() {
- public void execute() {
- windowSizeMaybeChanged(Window.getClientWidth(),
- Window.getClientHeight());
- }
-
- });
-
- public VView() {
- super();
- setStyleName(CLASSNAME);
-
- // Allow focusing the view by using the focus() method, the view
- // should not be in the document focus flow
- getElement().setTabIndex(-1);
- }
-
- /**
- * Called when the window might have been resized.
- *
- * @param newWidth
- * The new width of the window
- * @param newHeight
- * The new height of the window
- */
- protected void windowSizeMaybeChanged(int newWidth, int newHeight) {
- boolean changed = false;
- if (width != newWidth) {
- width = newWidth;
- changed = true;
- VConsole.log("New window width: " + width);
- }
- if (height != newHeight) {
- height = newHeight;
- changed = true;
- VConsole.log("New window height: " + height);
- }
- if (changed) {
- VConsole.log("Running layout functions due to window resize");
- connection.runDescendentsLayout(VView.this);
- Util.runWebkitOverflowAutoFix(getElement());
-
- sendClientResized();
- }
- }
-
- public String getTheme() {
- return theme;
- }
-
- /**
- * Used to reload host page on theme changes.
- */
- private static native void reloadHostPage()
- /*-{
- $wnd.location.reload();
- }-*/;
-
- /**
- * Evaluate the given script in the browser document.
- *
- * @param script
- * Script to be executed.
- */
- private static native void eval(String script)
- /*-{
- try {
- if (script == null) return;
- $wnd.eval(script);
- } catch (e) {
- }
- }-*/;
-
- /**
- * Returns true if the body is NOT generated, i.e if someone else has made
- * the page that we're running in. Otherwise we're in charge of the whole
- * page.
- *
- * @return true if we're running embedded
- */
- public boolean isEmbedded() {
- return !getElement().getOwnerDocument().getBody().getClassName()
- .contains(ApplicationConnection.GENERATED_BODY_CLASSNAME);
- }
-
- public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
- rendering = true;
-
- id = uidl.getId();
- boolean firstPaint = connection == null;
- connection = client;
-
- immediate = uidl.hasAttribute("immediate");
- resizeLazy = uidl.hasAttribute(RESIZE_LAZY);
- String newTheme = uidl.getStringAttribute("theme");
- if (theme != null && !newTheme.equals(theme)) {
- // Complete page refresh is needed due css can affect layout
- // calculations etc
- reloadHostPage();
- } else {
- theme = newTheme;
- }
- if (uidl.hasAttribute("style")) {
- setStyleName(getStylePrimaryName() + " "
- + uidl.getStringAttribute("style"));
- }
-
- if (uidl.hasAttribute("name")) {
- client.setWindowName(uidl.getStringAttribute("name"));
- }
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- if (!isEmbedded()) {
- // only change window title if we're in charge of the whole page
- com.google.gwt.user.client.Window.setTitle(uidl
- .getStringAttribute("caption"));
- }
-
- // Process children
- int childIndex = 0;
-
- // Open URL:s
- boolean isClosed = false; // was this window closed?
- while (childIndex < uidl.getChildCount()
- && "open".equals(uidl.getChildUIDL(childIndex).getTag())) {
- final UIDL open = uidl.getChildUIDL(childIndex);
- final String url = client.translateVaadinUri(open
- .getStringAttribute("src"));
- final String target = open.getStringAttribute("name");
- if (target == null) {
- // source will be opened to this browser window, but we may have
- // to finish rendering this window in case this is a download
- // (and window stays open).
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- goTo(url);
- }
- });
- } else if ("_self".equals(target)) {
- // This window is closing (for sure). Only other opens are
- // relevant in this change. See #3558, #2144
- isClosed = true;
- goTo(url);
- } else {
- String options;
- if (open.hasAttribute("border")) {
- if (open.getStringAttribute("border").equals("minimal")) {
- options = "menubar=yes,location=no,status=no";
- } else {
- options = "menubar=no,location=no,status=no";
- }
-
- } else {
- options = "resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes";
- }
-
- if (open.hasAttribute("width")) {
- int w = open.getIntAttribute("width");
- options += ",width=" + w;
- }
- if (open.hasAttribute("height")) {
- int h = open.getIntAttribute("height");
- options += ",height=" + h;
- }
-
- Window.open(url, target, options);
- }
- childIndex++;
- }
- if (isClosed) {
- // don't render the content, something else will be opened to this
- // browser view
- rendering = false;
- return;
- }
-
- // Draw this application level window
- UIDL childUidl = uidl.getChildUIDL(childIndex);
- final Paintable lo = client.getPaintable(childUidl);
-
- if (layout != null) {
- if (layout != lo) {
- // remove old
- client.unregisterPaintable(layout);
- // add new
- setWidget((Widget) lo);
- layout = lo;
- }
- } else {
- setWidget((Widget) lo);
- layout = lo;
- }
-
- layout.updateFromUIDL(childUidl, client);
- if (!childUidl.getBooleanAttribute("cached")) {
- updateParentFrameSize();
- }
-
- // Save currently open subwindows to track which will need to be closed
- final HashSet<VWindow> removedSubWindows = new HashSet<VWindow>(
- subWindows);
-
- // Handle other UIDL children
- while ((childUidl = uidl.getChildUIDL(++childIndex)) != null) {
- String tag = childUidl.getTag().intern();
- if (tag == "actions") {
- if (actionHandler == null) {
- actionHandler = new ShortcutActionHandler(id, client);
- }
- actionHandler.updateActionMap(childUidl);
- } else if (tag == "execJS") {
- String script = childUidl.getStringAttribute("script");
- eval(script);
- } else if (tag == "notifications") {
- for (final Iterator<?> it = childUidl.getChildIterator(); it
- .hasNext();) {
- final UIDL notification = (UIDL) it.next();
- VNotification.showNotification(client, notification);
- }
- } else {
- // subwindows
- final Paintable w = client.getPaintable(childUidl);
- if (subWindows.contains(w)) {
- removedSubWindows.remove(w);
- } else {
- subWindows.add((VWindow) w);
- }
- w.updateFromUIDL(childUidl, client);
- }
- }
-
- // Close old windows which where not in UIDL anymore
- for (final Iterator<VWindow> rem = removedSubWindows.iterator(); rem
- .hasNext();) {
- final VWindow w = rem.next();
- client.unregisterPaintable(w);
- subWindows.remove(w);
- w.hide();
- }
-
- if (uidl.hasAttribute("focused")) {
- // set focused component when render phase is finished
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- final Paintable toBeFocused = uidl.getPaintableAttribute(
- "focused", connection);
-
- /*
- * Two types of Widgets can be focused, either implementing
- * GWT HasFocus of a thinner Vaadin specific Focusable
- * interface.
- */
- if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) {
- final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused;
- toBeFocusedWidget.setFocus(true);
- } else if (toBeFocused instanceof Focusable) {
- ((Focusable) toBeFocused).focus();
- } else {
- VConsole.log("Could not focus component");
- }
- }
- });
- }
-
- // Add window listeners on first paint, to prevent premature
- // variablechanges
- if (firstPaint) {
- Window.addWindowClosingHandler(this);
- Window.addResizeHandler(this);
- }
-
- onResize();
-
- // finally set scroll position from UIDL
- if (uidl.hasVariable("scrollTop")) {
- scrollable = true;
- scrollTop = uidl.getIntVariable("scrollTop");
- DOM.setElementPropertyInt(getElement(), "scrollTop", scrollTop);
- scrollLeft = uidl.getIntVariable("scrollLeft");
- DOM.setElementPropertyInt(getElement(), "scrollLeft", scrollLeft);
- } else {
- scrollable = false;
- }
-
- // Safari workaround must be run after scrollTop is updated as it sets
- // scrollTop using a deferred command.
- if (BrowserInfo.get().isSafari()) {
- Util.runWebkitOverflowAutoFix(getElement());
- }
-
- scrollIntoView(uidl);
-
- rendering = false;
- }
-
- /**
- * Tries to scroll paintable referenced from given UIDL snippet to be
- * visible.
- *
- * @param uidl
- */
- void scrollIntoView(final UIDL uidl) {
- if (uidl.hasAttribute("scrollTo")) {
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- final Paintable paintable = uidl.getPaintableAttribute(
- "scrollTo", connection);
- ((Widget) paintable).getElement().scrollIntoView();
- }
- });
- }
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- int type = DOM.eventGetType(event);
- if (type == Event.ONKEYDOWN && actionHandler != null) {
- actionHandler.handleKeyboardEvent(event);
- return;
- } else if (scrollable && type == Event.ONSCROLL) {
- updateScrollPosition();
- }
- }
-
- /**
- * Updates scroll position from DOM and saves variables to server.
- */
- private void updateScrollPosition() {
- int oldTop = scrollTop;
- int oldLeft = scrollLeft;
- scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop");
- scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft");
- if (connection != null && !rendering) {
- if (oldTop != scrollTop) {
- connection.updateVariable(id, "scrollTop", scrollTop, false);
- }
- if (oldLeft != scrollLeft) {
- connection.updateVariable(id, "scrollLeft", scrollLeft, false);
- }
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
- * .gwt.event.logical.shared.ResizeEvent)
- */
- public void onResize(ResizeEvent event) {
- onResize();
- }
-
- /**
- * Called when a resize event is received.
- */
- private void onResize() {
- /*
- * IE (pre IE9 at least) will give us some false resize events due to
- * problems with scrollbars. Firefox 3 might also produce some extra
- * events. We postpone both the re-layouting and the server side event
- * for a while to deal with these issues.
- *
- * We may also postpone these events to avoid slowness when resizing the
- * browser window. Constantly recalculating the layout causes the resize
- * operation to be really slow with complex layouts.
- */
- boolean lazy = resizeLazy
- || (BrowserInfo.get().isIE() && BrowserInfo.get()
- .getIEVersion() <= 8) || BrowserInfo.get().isFF3();
-
- if (lazy) {
- delayedResizeExecutor.trigger();
- } else {
- windowSizeMaybeChanged(Window.getClientWidth(),
- Window.getClientHeight());
- }
- }
-
- /**
- * Send new dimensions to the server.
- */
- private void sendClientResized() {
- connection.updateVariable(id, "height", height, false);
- connection.updateVariable(id, "width", width, immediate);
- }
-
- public native static void goTo(String url)
- /*-{
- $wnd.location = url;
- }-*/;
-
- public void onWindowClosing(Window.ClosingEvent event) {
- // Change focus on this window in order to ensure that all state is
- // collected from textfields
- // TODO this is a naive hack, that only works with text fields and may
- // cause some odd issues. Should be replaced with a decent solution, see
- // also related BeforeShortcutActionListener interface. Same interface
- // might be usable here.
- VTextField.flushChangesFromFocusedTextField();
-
- // Send the closing state to server
- connection.updateVariable(id, "close", true, false);
- connection.sendPendingVariableChangesSync();
- }
-
- private final RenderSpace myRenderSpace = new RenderSpace() {
- private int excessHeight = -1;
- private int excessWidth = -1;
-
- @Override
- public int getHeight() {
- return getElement().getOffsetHeight() - getExcessHeight();
- }
-
- private int getExcessHeight() {
- if (excessHeight < 0) {
- detectExcessSize();
- }
- return excessHeight;
- }
-
- private void detectExcessSize() {
- // TODO define that iview cannot be themed and decorations should
- // get to parent element, then get rid of this expensive and error
- // prone function
- final String overflow = getElement().getStyle().getProperty(
- "overflow");
- getElement().getStyle().setProperty("overflow", "hidden");
- if (BrowserInfo.get().isIE()
- && getElement().getPropertyInt("clientWidth") == 0) {
- // can't detect possibly themed border/padding width in some
- // situations (with some layout configurations), use empty div
- // to measure width properly
- DivElement div = Document.get().createDivElement();
- div.setInnerHTML("&nbsp;");
- div.getStyle().setProperty("overflow", "hidden");
- div.getStyle().setProperty("height", "1px");
- getElement().appendChild(div);
- excessWidth = getElement().getOffsetWidth()
- - div.getOffsetWidth();
- getElement().removeChild(div);
- } else {
- excessWidth = getElement().getOffsetWidth()
- - getElement().getPropertyInt("clientWidth");
- }
- excessHeight = getElement().getOffsetHeight()
- - getElement().getPropertyInt("clientHeight");
-
- getElement().getStyle().setProperty("overflow", overflow);
- }
-
- @Override
- public int getWidth() {
- int w = getRealWidth();
- if (w < 10 && BrowserInfo.get().isIE7()) {
- // Overcome an IE7 bug #3295
- Util.shakeBodyElement();
- w = getRealWidth();
- }
- return w;
- }
-
- private int getRealWidth() {
- if (connection.getConfiguration().isStandalone()) {
- return getElement().getOffsetWidth() - getExcessWidth();
- }
-
- // If not running standalone, there might be multiple Vaadin apps
- // that won't shrink with the browser window as the components have
- // calculated widths (#3125)
-
- // Find all Vaadin applications on the page
- ArrayList<String> vaadinApps = new ArrayList<String>();
- loadAppIdListFromDOM(vaadinApps);
-
- // Store original styles here so they can be restored
- ArrayList<String> originalDisplays = new ArrayList<String>(
- vaadinApps.size());
-
- String ownAppId = connection.getConfiguration().getRootPanelId();
-
- // Hiding elements causes browser to forget scroll position -> must
- // save values and restore when the elements are visible again #7976
- int originalScrollTop = Window.getScrollTop();
- int originalScrollLeft = Window.getScrollLeft();
-
- // Set display: none for all Vaadin apps
- for (int i = 0; i < vaadinApps.size(); i++) {
- String appId = vaadinApps.get(i);
- Element targetElement;
- if (appId.equals(ownAppId)) {
- // Only hide the contents of current application
- targetElement = ((Widget) layout).getElement();
- } else {
- // Hide everything for other applications
- targetElement = Document.get().getElementById(appId);
- }
- Style layoutStyle = targetElement.getStyle();
-
- originalDisplays.add(i, layoutStyle.getDisplay());
- layoutStyle.setDisplay(Display.NONE);
- }
-
- int w = getElement().getOffsetWidth() - getExcessWidth();
-
- // Then restore the old display style before returning
- for (int i = 0; i < vaadinApps.size(); i++) {
- String appId = vaadinApps.get(i);
- Element targetElement;
- if (appId.equals(ownAppId)) {
- targetElement = ((Widget) layout).getElement();
- } else {
- targetElement = Document.get().getElementById(appId);
- }
- Style layoutStyle = targetElement.getStyle();
- String originalDisplay = originalDisplays.get(i);
-
- if (originalDisplay.length() == 0) {
- layoutStyle.clearDisplay();
- } else {
- layoutStyle.setProperty("display", originalDisplay);
- }
- }
-
- // Scroll back to original location
- Window.scrollTo(originalScrollLeft, originalScrollTop);
-
- return w;
- }
-
- private int getExcessWidth() {
- if (excessWidth < 0) {
- detectExcessSize();
- }
- return excessWidth;
- }
-
- @Override
- public int getScrollbarSize() {
- return Util.getNativeScrollbarSize();
- }
- };
-
- private native static void loadAppIdListFromDOM(ArrayList<String> list)
- /*-{
- var j;
- for(j in $wnd.vaadin.vaadinConfigurations) {
- list.@java.util.Collection::add(Ljava/lang/Object;)(j);
- }
- }-*/;
-
- public RenderSpace getAllocatedSpace(Widget child) {
- return myRenderSpace;
- }
-
- public boolean hasChildComponent(Widget component) {
- return (component != null && component == layout);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- // TODO This is untested as no layouts require this
- if (oldComponent != layout) {
- return;
- }
-
- setWidget(newComponent);
- layout = (Paintable) newComponent;
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- /*
- * Can never propagate further and we do not want need to re-layout the
- * layout which has caused this request.
- */
- updateParentFrameSize();
-
- // layout size change may affect its available space (scrollbars)
- connection.handleComponentRelativeSize((Widget) layout);
-
- return true;
-
- }
-
- private void updateParentFrameSize() {
- if (parentFrame == null) {
- return;
- }
-
- int childHeight = Util.getRequiredHeight(getWidget().getElement());
- int childWidth = Util.getRequiredWidth(getWidget().getElement());
-
- parentFrame.getStyle().setPropertyPx("width", childWidth);
- parentFrame.getStyle().setPropertyPx("height", childHeight);
- }
-
- private static native Element getParentFrame()
- /*-{
- try {
- var frameElement = $wnd.frameElement;
- if (frameElement == null) {
- return null;
- }
- if (frameElement.getAttribute("autoResize") == "true") {
- return frameElement;
- }
- } catch (e) {
- }
- return null;
- }-*/;
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP Subwindows never draw caption for their first child (layout)
- }
-
- /**
- * Return an iterator for current subwindows. This method is meant for
- * testing purposes only.
- *
- * @return
- */
- public ArrayList<VWindow> getSubWindowList() {
- ArrayList<VWindow> windows = new ArrayList<VWindow>(subWindows.size());
- for (VWindow widget : subWindows) {
- windows.add(widget);
- }
- return windows;
- }
-
- public void init(String rootPanelId,
- ApplicationConnection applicationConnection) {
- DOM.sinkEvents(getElement(), Event.ONKEYDOWN | Event.ONSCROLL);
-
- // iview is focused when created so element needs tabIndex
- // 1 due 0 is at the end of natural tabbing order
- DOM.setElementProperty(getElement(), "tabIndex", "1");
-
- RootPanel root = RootPanel.get(rootPanelId);
-
- // Remove the v-app-loading or any splash screen added inside the div by
- // the user
- root.getElement().setInnerHTML("");
- // For backwards compatibility with static index pages only.
- // No longer added by AbstractApplicationServlet/Portlet
- root.removeStyleName("v-app-loading");
-
- root.add(this);
-
- if (applicationConnection.getConfiguration().isStandalone()) {
- // set focus to iview element by default to listen possible keyboard
- // shortcuts. For embedded applications this is unacceptable as we
- // don't want to steal focus from the main page nor we don't want
- // side-effects from focusing (scrollIntoView).
- getElement().focus();
- }
-
- parentFrame = getParentFrame();
- }
-
- public ShortcutActionHandler getShortcutActionHandler() {
- return actionHandler;
- }
-
- public void focus() {
- getElement().focus();
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/Vaadin6Connector.java b/src/com/vaadin/terminal/gwt/client/ui/Vaadin6Connector.java
new file mode 100644
index 0000000000..7fccdafd2a
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/Vaadin6Connector.java
@@ -0,0 +1,17 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+
+public abstract class Vaadin6Connector extends AbstractComponentConnector
+ implements Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ ((Paintable) getWidget()).updateFromUIDL(uidl, client);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java
new file mode 100644
index 0000000000..80b6254e02
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java
@@ -0,0 +1,219 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.absolutelayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style;
+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.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+import com.vaadin.terminal.gwt.client.ui.absolutelayout.VAbsoluteLayout.AbsoluteWrapper;
+import com.vaadin.ui.AbsoluteLayout;
+
+@Connect(AbsoluteLayout.class)
+public class AbsoluteLayoutConnector extends
+ AbstractComponentContainerConnector implements DirectionalManagedLayout {
+
+ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
+ this) {
+
+ @Override
+ protected ComponentConnector getChildComponent(Element element) {
+ return getConnectorForElement(element);
+ }
+
+ @Override
+ protected LayoutClickRpc getLayoutClickRPC() {
+ return rpc;
+ };
+
+ };
+
+ private AbsoluteLayoutServerRpc rpc;
+
+ private Map<String, AbsoluteWrapper> connectorIdToComponentWrapper = new HashMap<String, AbsoluteWrapper>();
+
+ @Override
+ protected void init() {
+ super.init();
+ rpc = RpcProxy.create(AbsoluteLayoutServerRpc.class, this);
+ }
+
+ /**
+ * 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.
+ */
+ protected ComponentConnector getConnectorForElement(Element element) {
+ return Util.getConnectorForElement(getConnection(), getWidget(),
+ element);
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ VAbsoluteLayout absoluteLayoutWidget = getWidget();
+ AbsoluteWrapper componentWrapper = getWrapper(component);
+
+ boolean captionIsNeeded = VCaption.isNeeded(component.getState());
+
+ VCaption caption = componentWrapper.getCaption();
+
+ if (captionIsNeeded) {
+ if (caption == null) {
+ caption = new VCaption(component, getConnection());
+ absoluteLayoutWidget.add(caption);
+ componentWrapper.setCaption(caption);
+ }
+ caption.updateCaption();
+ componentWrapper.updateCaptionPosition();
+ } else {
+ if (caption != null) {
+ caption.removeFromParent();
+ }
+ }
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VAbsoluteLayout.class);
+ }
+
+ @Override
+ public VAbsoluteLayout getWidget() {
+ return (VAbsoluteLayout) super.getWidget();
+ }
+
+ @Override
+ public AbsoluteLayoutState getState() {
+ return (AbsoluteLayoutState) super.getState();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ clickEventHandler.handleEventHandlerRegistration();
+
+ // TODO Margin handling
+
+ for (ComponentConnector child : getChildren()) {
+ getWrapper(child).setPosition(
+ getState().getConnectorPosition(child));
+ }
+ };
+
+ private AbsoluteWrapper getWrapper(ComponentConnector child) {
+ String childId = child.getConnectorId();
+ AbsoluteWrapper wrapper = connectorIdToComponentWrapper.get(childId);
+ if (wrapper != null) {
+ return wrapper;
+ }
+
+ wrapper = new AbsoluteWrapper(child.getWidget());
+ connectorIdToComponentWrapper.put(childId, wrapper);
+ getWidget().add(wrapper);
+ return wrapper;
+
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ for (ComponentConnector child : getChildren()) {
+ getWrapper(child);
+ }
+
+ for (ComponentConnector oldChild : event.getOldChildren()) {
+ if (oldChild.getParent() != this) {
+ String connectorId = oldChild.getConnectorId();
+ AbsoluteWrapper absoluteWrapper = connectorIdToComponentWrapper
+ .remove(connectorId);
+ absoluteWrapper.destroy();
+ }
+ }
+ }
+
+ public void layoutVertically() {
+ VAbsoluteLayout layout = getWidget();
+ for (ComponentConnector paintable : getChildren()) {
+ Widget widget = paintable.getWidget();
+ AbsoluteWrapper wrapper = (AbsoluteWrapper) widget.getParent();
+ Style wrapperStyle = wrapper.getElement().getStyle();
+
+ if (paintable.isRelativeHeight()) {
+ int h;
+ if (wrapper.top != null && wrapper.bottom != null) {
+ h = wrapper.getOffsetHeight();
+ } else if (wrapper.bottom != null) {
+ // top not defined, available space 0... bottom of
+ // wrapper
+ h = wrapper.getElement().getOffsetTop()
+ + wrapper.getOffsetHeight();
+ } else {
+ // top defined or both undefined, available space ==
+ // canvas - top
+ h = layout.canvas.getOffsetHeight()
+ - wrapper.getElement().getOffsetTop();
+ }
+ wrapperStyle.setHeight(h, Unit.PX);
+ getLayoutManager().reportHeightAssignedToRelative(paintable, h);
+ } else {
+ wrapperStyle.clearHeight();
+ }
+
+ wrapper.updateCaptionPosition();
+ }
+ }
+
+ public void layoutHorizontally() {
+ VAbsoluteLayout layout = getWidget();
+ for (ComponentConnector paintable : getChildren()) {
+ AbsoluteWrapper wrapper = getWrapper(paintable);
+ Style wrapperStyle = wrapper.getElement().getStyle();
+
+ if (paintable.isRelativeWidth()) {
+ int w;
+ if (wrapper.left != null && wrapper.right != null) {
+ w = wrapper.getOffsetWidth();
+ } else if (wrapper.right != null) {
+ // left == null
+ // available width == right edge == offsetleft + width
+ w = wrapper.getOffsetWidth()
+ + wrapper.getElement().getOffsetLeft();
+ } else {
+ // left != null && right == null || left == null &&
+ // right == null
+ // available width == canvas width - offset left
+ w = layout.canvas.getOffsetWidth()
+ - wrapper.getElement().getOffsetLeft();
+ }
+ wrapperStyle.setWidth(w, Unit.PX);
+ getLayoutManager().reportWidthAssignedToRelative(paintable, w);
+ } else {
+ wrapperStyle.clearWidth();
+ }
+
+ wrapper.updateCaptionPosition();
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java
new file mode 100644
index 0000000000..d626eb5b6c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.absolutelayout;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+
+public interface AbsoluteLayoutServerRpc extends LayoutClickRpc, ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutState.java
new file mode 100644
index 0000000000..4e1a43dd8b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutState.java
@@ -0,0 +1,29 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.absolutelayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
+
+public class AbsoluteLayoutState extends AbstractLayoutState {
+ // Maps each component to a position
+ private Map<String, String> connectorToCssPosition = new HashMap<String, String>();
+
+ public String getConnectorPosition(Connector connector) {
+ return connectorToCssPosition.get(connector.getConnectorId());
+ }
+
+ public Map<String, String> getConnectorToCssPosition() {
+ return connectorToCssPosition;
+ }
+
+ public void setConnectorToCssPosition(
+ Map<String, String> componentToCssPosition) {
+ connectorToCssPosition = componentToCssPosition;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/VAbsoluteLayout.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/VAbsoluteLayout.java
new file mode 100644
index 0000000000..e2cb629d68
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/VAbsoluteLayout.java
@@ -0,0 +1,134 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.absolutelayout;
+
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+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;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.VCaption;
+
+public class VAbsoluteLayout extends ComplexPanel {
+
+ /** Tag name for widget creation */
+ public static final String TAGNAME = "absolutelayout";
+
+ /** Class name, prefix in styling */
+ public static final String CLASSNAME = "v-absolutelayout";
+
+ private DivElement marginElement;
+
+ protected final Element canvas = DOM.createDiv();
+
+ private Object previousStyleName;
+
+ protected ApplicationConnection client;
+
+ public VAbsoluteLayout() {
+ setElement(Document.get().createDivElement());
+ setStyleName(CLASSNAME);
+ marginElement = Document.get().createDivElement();
+ canvas.getStyle().setProperty("position", "relative");
+ canvas.getStyle().setProperty("overflow", "hidden");
+ marginElement.appendChild(canvas);
+ getElement().appendChild(marginElement);
+
+ canvas.setClassName(CLASSNAME + "-canvas");
+ canvas.setClassName(CLASSNAME + "-margin");
+ }
+
+ @Override
+ public void add(Widget child) {
+ super.add(child, canvas);
+ }
+
+ public static class AbsoluteWrapper extends SimplePanel {
+ private String css;
+ String left;
+ String top;
+ String right;
+ String bottom;
+ private String zIndex;
+
+ private VCaption caption;
+
+ public AbsoluteWrapper(Widget child) {
+ setWidget(child);
+ setStyleName(CLASSNAME + "-wrapper");
+ }
+
+ public VCaption getCaption() {
+ return caption;
+ }
+
+ public void setCaption(VCaption caption) {
+ this.caption = caption;
+ }
+
+ public void destroy() {
+ if (caption != null) {
+ caption.removeFromParent();
+ }
+ removeFromParent();
+ }
+
+ public void setPosition(String stringAttribute) {
+ if (css == null || !css.equals(stringAttribute)) {
+ css = stringAttribute;
+ top = right = bottom = left = zIndex = null;
+ if (!css.equals("")) {
+ String[] properties = css.split(";");
+ for (int i = 0; i < properties.length; i++) {
+ String[] keyValue = properties[i].split(":");
+ if (keyValue[0].equals("left")) {
+ left = keyValue[1];
+ } else if (keyValue[0].equals("top")) {
+ top = keyValue[1];
+ } else if (keyValue[0].equals("right")) {
+ right = keyValue[1];
+ } else if (keyValue[0].equals("bottom")) {
+ bottom = keyValue[1];
+ } else if (keyValue[0].equals("z-index")) {
+ zIndex = keyValue[1];
+ }
+ }
+ }
+ // ensure ne values
+ Style style = getElement().getStyle();
+ /*
+ * IE8 dies when nulling zIndex, even in IE7 mode. All other css
+ * properties (and even in older IE's) accept null values just
+ * fine. Assign empty string instead of null.
+ */
+ if (zIndex != null) {
+ style.setProperty("zIndex", zIndex);
+ } else {
+ style.setProperty("zIndex", "");
+ }
+ style.setProperty("top", top);
+ style.setProperty("left", left);
+ style.setProperty("right", right);
+ style.setProperty("bottom", bottom);
+
+ }
+ updateCaptionPosition();
+ }
+
+ void updateCaptionPosition() {
+ if (caption != null) {
+ Style style = caption.getElement().getStyle();
+ style.setProperty("position", "absolute");
+ style.setPropertyPx("left", getElement().getOffsetLeft());
+ style.setPropertyPx("top", getElement().getOffsetTop()
+ - caption.getHeight());
+ }
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java b/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java
new file mode 100644
index 0000000000..a03fa37214
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java
@@ -0,0 +1,83 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.accordion;
+
+import java.util.Iterator;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.accordion.VAccordion.StackItem;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector;
+import com.vaadin.ui.Accordion;
+
+@Connect(Accordion.class)
+public class AccordionConnector extends TabsheetBaseConnector implements
+ SimpleManagedLayout, MayScrollChildren {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().selectedUIDLItemIndex = -1;
+ super.updateFromUIDL(uidl, client);
+ /*
+ * Render content after all tabs have been created and we know how large
+ * the content area is
+ */
+ if (getWidget().selectedUIDLItemIndex >= 0) {
+ StackItem selectedItem = getWidget().getStackItem(
+ getWidget().selectedUIDLItemIndex);
+ UIDL selectedTabUIDL = getWidget().lazyUpdateMap
+ .remove(selectedItem);
+ getWidget().open(getWidget().selectedUIDLItemIndex);
+
+ selectedItem.setContent(selectedTabUIDL);
+ } else if (isRealUpdate(uidl) && getWidget().openTab != null) {
+ getWidget().close(getWidget().openTab);
+ }
+
+ getWidget().iLayout();
+ // 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));
+ }
+ getWidget().lazyUpdateMap.clear();
+ }
+
+ }
+
+ @Override
+ public VAccordion getWidget() {
+ return (VAccordion) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VAccordion.class);
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ /* Accordion does not render its children's captions */
+ }
+
+ public void layout() {
+ VAccordion accordion = getWidget();
+
+ accordion.updateOpenTabSize();
+
+ if (isUndefinedHeight()) {
+ accordion.openTab.setHeightFromWidget();
+ }
+ accordion.iLayout();
+
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAccordion.java b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java
index 4d0776a5f9..dcd520bbb3 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VAccordion.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java
@@ -1,7 +1,7 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.accordion;
import java.util.HashMap;
import java.util.HashSet;
@@ -15,80 +15,28 @@ 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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.ContainerResizedListener;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderSpace;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheetBase;
-public class VAccordion extends VTabsheetBase implements
- ContainerResizedListener {
+public class VAccordion extends VTabsheetBase {
public static final String CLASSNAME = "v-accordion";
- private Set<Paintable> paintables = new HashSet<Paintable>();
+ private Set<Widget> widgets = new HashSet<Widget>();
- private String height;
+ HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
- private String width = "";
+ StackItem openTab = null;
- private HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
-
- private RenderSpace renderSpace = new RenderSpace(0, 0, true);
-
- private StackItem openTab = null;
-
- private boolean rendering = false;
-
- private int selectedUIDLItemIndex = -1;
-
- private RenderInformation renderInformation = new RenderInformation();
+ int selectedUIDLItemIndex = -1;
public VAccordion() {
super(CLASSNAME);
- // IE6 needs this to calculate offsetHeight correctly
- if (BrowserInfo.get().isIE6()) {
- DOM.setStyleAttribute(getElement(), "zoom", "1");
- }
- }
-
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- selectedUIDLItemIndex = -1;
- super.updateFromUIDL(uidl, client);
- /*
- * Render content after all tabs have been created and we know how large
- * the content area is
- */
- if (selectedUIDLItemIndex >= 0) {
- StackItem selectedItem = getStackItem(selectedUIDLItemIndex);
- UIDL selectedTabUIDL = lazyUpdateMap.remove(selectedItem);
- open(selectedUIDLItemIndex);
-
- selectedItem.setContent(selectedTabUIDL);
- } else if (!uidl.getBooleanAttribute("cached") && openTab != null) {
- close(openTab);
- }
-
- iLayout();
- // finally render possible hidden tabs
- if (lazyUpdateMap.size() > 0) {
- for (Iterator iterator = lazyUpdateMap.keySet().iterator(); iterator
- .hasNext();) {
- StackItem item = (StackItem) iterator.next();
- item.setContent(lazyUpdateMap.get(item));
- }
- lazyUpdateMap.clear();
- }
-
- renderInformation.updateSize(getElement());
-
- rendering = false;
}
@Override
@@ -137,7 +85,7 @@ public class VAccordion extends VTabsheetBase implements
private StackItem moveStackItemIfNeeded(StackItem item, int newIndex,
UIDL tabUidl) {
UIDL tabContentUIDL = null;
- Paintable tabContent = null;
+ ComponentConnector tabContent = null;
if (tabUidl.getChildCount() > 0) {
tabContentUIDL = tabUidl.getChildUIDL(0);
tabContent = client.getPaintable(tabContentUIDL);
@@ -179,14 +127,13 @@ public class VAccordion extends VTabsheetBase implements
// StackItem
Widget oldWidget = item.getComponent();
if (oldWidget != null) {
- item = new StackItem(tabUidl);
- insert(item, getElement(), newIndex, true);
+ oldWidget.removeFromParent();
}
}
return item;
}
- private void open(int itemIndex) {
+ void open(int itemIndex) {
StackItem item = (StackItem) getWidget(itemIndex);
boolean alreadyOpen = false;
if (openTab != null) {
@@ -209,7 +156,7 @@ public class VAccordion extends VTabsheetBase implements
updateOpenTabSize();
}
- private void close(StackItem item) {
+ void close(StackItem item) {
if (!item.isOpen()) {
return;
}
@@ -242,58 +189,19 @@ public class VAccordion extends VTabsheetBase implements
}
}
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
-
- Util.setWidthExcludingPaddingAndBorder(this, width, 2);
- this.width = width;
- if (!rendering) {
- updateOpenTabSize();
-
- if (isDynamicHeight()) {
- Util.updateRelativeChildrenAndSendSizeUpdateEvent(client,
- openTab, this);
- updateOpenTabSize();
- }
-
- if (isDynamicHeight()) {
- openTab.setHeightFromWidget();
- }
- iLayout();
- }
- }
-
- @Override
- public void setHeight(String height) {
- Util.setHeightExcludingPaddingAndBorder(this, height, 2);
- this.height = height;
-
- if (!rendering) {
- updateOpenTabSize();
- }
-
- }
-
/**
* Sets the size of the open tab
*/
- private void updateOpenTabSize() {
+ void updateOpenTabSize() {
if (openTab == null) {
- renderSpace.setHeight(0);
- renderSpace.setWidth(0);
return;
}
// WIDTH
if (!isDynamicWidth()) {
- int w = getOffsetWidth();
- openTab.setWidth(w);
- renderSpace.setWidth(w);
+ openTab.setWidth("100%");
} else {
- renderSpace.setWidth(0);
+ openTab.setWidth(null);
}
// HEIGHT
@@ -317,10 +225,8 @@ public class VAccordion extends VTabsheetBase implements
spaceForOpenItem = 0;
}
- renderSpace.setHeight(spaceForOpenItem);
openTab.setHeight(spaceForOpenItem);
} else {
- renderSpace.setHeight(0);
openTab.setHeightFromWidget();
}
@@ -348,13 +254,11 @@ public class VAccordion extends VTabsheetBase implements
super.setWidth(maxWidth + "px");
openTab.setWidth(maxWidth);
}
-
- Util.runWebkitOverflowAutoFix(openTab.getContainerElement());
-
}
/**
- *
+ * A StackItem has always two children, Child 0 is a VCaption, Child 1 is
+ * the actual child widget.
*/
protected class StackItem extends ComplexPanel implements ClickHandler {
@@ -383,12 +287,12 @@ public class VAccordion extends VTabsheetBase implements
}
public void setHeightFromWidget() {
- Widget paintable = getPaintable();
- if (paintable == null) {
+ Widget widget = getChildWidget();
+ if (widget == null) {
return;
}
- int paintableHeight = (paintable).getElement().getOffsetHeight();
+ int paintableHeight = widget.getElement().getOffsetHeight();
setHeight(paintableHeight);
}
@@ -432,12 +336,8 @@ public class VAccordion extends VTabsheetBase implements
public StackItem(UIDL tabUidl) {
setElement(DOM.createDiv());
- caption = new VCaption(null, client);
+ caption = new VCaption(client);
caption.addClickHandler(this);
- if (BrowserInfo.get().isIE6()) {
- DOM.setEventListener(captionNode, this);
- DOM.sinkEvents(captionNode, Event.BUTTON_LEFT);
- }
super.add(caption, captionNode);
DOM.appendChild(captionNode, caption.getElement());
DOM.appendChild(getElement(), captionNode);
@@ -459,7 +359,7 @@ public class VAccordion extends VTabsheetBase implements
return content;
}
- public Widget getPaintable() {
+ public Widget getChildWidget() {
if (getWidgetCount() > 1) {
return getWidget(1);
} else {
@@ -467,14 +367,17 @@ public class VAccordion extends VTabsheetBase implements
}
}
- public void replacePaintable(Paintable newPntbl) {
+ public void replaceWidget(Widget newWidget) {
if (getWidgetCount() > 1) {
- client.unregisterPaintable((Paintable) getWidget(1));
- paintables.remove(getWidget(1));
+ Widget oldWidget = getWidget(1);
+ ComponentConnector oldPaintable = ConnectorMap.get(client)
+ .getConnector(oldWidget);
+ ConnectorMap.get(client).unregisterConnector(oldPaintable);
+ widgets.remove(oldWidget);
remove(1);
}
- add((Widget) newPntbl, content);
- paintables.add(newPntbl);
+ add(newWidget, content);
+ widgets.add(newWidget);
}
public void open() {
@@ -496,10 +399,6 @@ public class VAccordion extends VTabsheetBase implements
removeStyleDependentName("open");
setHeight(-1);
setWidth("");
- if (BrowserInfo.get().isIE6()) {
- // Work around for IE6 layouting problem #3359
- getElement().getStyle().setProperty("zoom", "1");
- }
open = false;
}
@@ -508,20 +407,21 @@ public class VAccordion extends VTabsheetBase implements
}
public void setContent(UIDL contentUidl) {
- final Paintable newPntbl = client.getPaintable(contentUidl);
- if (getPaintable() == null) {
- add((Widget) newPntbl, content);
- paintables.add(newPntbl);
- } else if (getPaintable() != newPntbl) {
- replacePaintable(newPntbl);
+ final ComponentConnector newPntbl = client
+ .getPaintable(contentUidl);
+ Widget newWidget = newPntbl.getWidget();
+ if (getChildWidget() == null) {
+ add(newWidget, content);
+ widgets.add(newWidget);
+ } else if (getChildWidget() != newWidget) {
+ replaceWidget(newWidget);
}
- newPntbl.updateFromUIDL(contentUidl, client);
if (contentUidl.getBooleanAttribute("cached")) {
/*
* The size of a cached, relative sized component must be
* updated to report correct size.
*/
- client.handleComponentRelativeSize((Widget) newPntbl);
+ client.handleComponentRelativeSize(newPntbl.getWidget());
}
if (isOpen() && isDynamicHeight()) {
setHeightFromWidget();
@@ -533,15 +433,21 @@ public class VAccordion extends VTabsheetBase implements
}
public void updateCaption(UIDL uidl) {
- caption.updateCaption(uidl);
+ // TODO need to call this because the caption does not have an owner
+ caption.updateCaptionWithoutOwner(
+ uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_CAPTION),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DISABLED),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE),
+ uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ICON));
}
public int getWidgetWidth() {
return DOM.getFirstChild(content).getOffsetWidth();
}
- public boolean contains(Paintable p) {
- return (getPaintable() == p);
+ public boolean contains(ComponentConnector p) {
+ return (getChildWidget() == p.getWidget());
}
public boolean isCaptionVisible() {
@@ -555,80 +461,22 @@ public class VAccordion extends VTabsheetBase implements
clear();
}
- public boolean isDynamicHeight() {
- return height == null || height.equals("");
+ boolean isDynamicWidth() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ return paintable.isUndefinedWidth();
}
- public boolean isDynamicWidth() {
- return width == null || width.equals("");
+ boolean isDynamicHeight() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ return paintable.isUndefinedHeight();
}
@Override
@SuppressWarnings("unchecked")
- protected Iterator<Object> getPaintableIterator() {
- return (Iterator) paintables.iterator();
- }
-
- public boolean hasChildComponent(Widget component) {
- if (paintables.contains(component)) {
- return true;
- } else {
- return false;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- for (Widget w : getChildren()) {
- StackItem item = (StackItem) w;
- if (item.getPaintable() == oldComponent) {
- item.replacePaintable((Paintable) newComponent);
- return;
- }
- }
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- /* Accordion does not render its children's captions */
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- if (!isDynamicHeight() && !isDynamicWidth()) {
- /*
- * If the height and width has been specified for this container the
- * child components cannot make the size of the layout change
- */
- // layout size change may affect its available space (scrollbars)
- for (Paintable paintable : child) {
- client.handleComponentRelativeSize((Widget) paintable);
- }
-
- return true;
- }
-
- updateOpenTabSize();
-
- if (renderInformation.updateSize(getElement())) {
- /*
- * Size has changed so we let the child components know about the
- * new size.
- */
- iLayout();
- // TODO Check if this is needed
- client.runDescendentsLayout(this);
-
- return false;
- } else {
- /*
- * Size has not changed so we do not need to propagate the event
- * further
- */
- return true;
- }
-
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- return renderSpace;
+ protected Iterator<Widget> getWidgetIterator() {
+ return widgets.iterator();
}
@Override
@@ -643,15 +491,17 @@ public class VAccordion extends VTabsheetBase implements
}
@Override
- protected Paintable getTab(int index) {
+ protected ComponentConnector getTab(int index) {
if (index < getWidgetCount()) {
- return (Paintable) (getStackItem(index)).getPaintable();
+ Widget w = getStackItem(index);
+ return ConnectorMap.get(client).getConnector(w);
}
return null;
}
- private StackItem getStackItem(int index) {
+ StackItem getStackItem(int index) {
return (StackItem) getWidget(index);
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAudio.java b/src/com/vaadin/terminal/gwt/client/ui/audio/AudioConnector.java
index 1fdfaca831..d55e66dbd5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VAudio.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/audio/AudioConnector.java
@@ -1,35 +1,29 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
+package com.vaadin.terminal.gwt.client.ui.audio;
-package com.vaadin.terminal.gwt.client.ui;
-
-import com.google.gwt.dom.client.AudioElement;
-import com.google.gwt.dom.client.Document;
+import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector;
+import com.vaadin.ui.Audio;
-public class VAudio extends VMediaBase {
- private static String CLASSNAME = "v-audio";
-
- private AudioElement audio;
-
- public VAudio() {
- audio = Document.get().createAudioElement();
- setMediaElement(audio);
- setStyleName(CLASSNAME);
- }
+@Connect(Audio.class)
+public class AudioConnector extends MediaBaseConnector {
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
+ super.updateFromUIDL(uidl, client);
+ if (!isRealUpdate(uidl)) {
return;
}
- super.updateFromUIDL(uidl, client);
- Style style = audio.getStyle();
+ Style style = getWidget().getElement().getStyle();
// Make sure that the controls are not clipped if visible.
if (shouldShowControls(uidl)
@@ -43,7 +37,8 @@ public class VAudio extends VMediaBase {
}
@Override
- protected String getDefaultAltHtml() {
- return "Your browser does not support the <code>audio</code> element.";
+ protected Widget createWidget() {
+ return GWT.create(VAudio.class);
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/audio/VAudio.java b/src/com/vaadin/terminal/gwt/client/ui/audio/VAudio.java
new file mode 100644
index 0000000000..7d5d1fe034
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/audio/VAudio.java
@@ -0,0 +1,27 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.audio;
+
+import com.google.gwt.dom.client.AudioElement;
+import com.google.gwt.dom.client.Document;
+import com.vaadin.terminal.gwt.client.ui.VMediaBase;
+
+public class VAudio extends VMediaBase {
+ private static String CLASSNAME = "v-audio";
+
+ private AudioElement audio;
+
+ public VAudio() {
+ audio = Document.get().createAudioElement();
+ setMediaElement(audio);
+ setStyleName(CLASSNAME);
+ }
+
+ @Override
+ protected String getDefaultAltHtml() {
+ return "Your browser does not support the <code>audio</code> element.";
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java
new file mode 100644
index 0000000000..62a5e8ac8b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java
@@ -0,0 +1,134 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.button;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.EventHelper;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
+import com.vaadin.terminal.gwt.client.communication.FieldRpc.FocusAndBlurServerRpc;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.ui.Button;
+
+@Connect(value = Button.class, loadStyle = LoadStyle.EAGER)
+public class ButtonConnector extends AbstractComponentConnector implements
+ BlurHandler, FocusHandler, ClickHandler {
+
+ private ButtonServerRpc rpc = RpcProxy.create(ButtonServerRpc.class, this);
+ private FocusAndBlurServerRpc focusBlurProxy = RpcProxy.create(
+ FocusAndBlurServerRpc.class, this);
+
+ private HandlerRegistration focusHandlerRegistration = null;
+ private HandlerRegistration blurHandlerRegistration = null;
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ getWidget().addClickHandler(this);
+ getWidget().client = getConnection();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ focusHandlerRegistration = EventHelper.updateFocusHandler(this,
+ focusHandlerRegistration);
+ blurHandlerRegistration = EventHelper.updateBlurHandler(this,
+ blurHandlerRegistration);
+ // Set text
+ getWidget().setText(getState().getCaption());
+
+ // handle error
+ if (null != getState().getErrorMessage()) {
+ 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;
+ }
+
+ if (getState().getIcon() != null) {
+ if (getWidget().icon == null) {
+ getWidget().icon = new Icon(getConnection());
+ getWidget().wrapper.insertBefore(getWidget().icon.getElement(),
+ getWidget().captionElement);
+ }
+ getWidget().icon.setUri(getState().getIcon().getURL());
+ } else {
+ if (getWidget().icon != null) {
+ getWidget().wrapper.removeChild(getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ }
+
+ getWidget().clickShortcut = getState().getClickShortcutKeyCode();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VButton.class);
+ }
+
+ @Override
+ public VButton getWidget() {
+ return (VButton) super.getWidget();
+ }
+
+ @Override
+ public ButtonState getState() {
+ return (ButtonState) super.getState();
+ }
+
+ public void onFocus(FocusEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurProxy.focus();
+ }
+
+ public void onBlur(BlurEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurProxy.blur();
+ }
+
+ public void onClick(ClickEvent event) {
+ if (getState().isDisableOnClick()) {
+ getWidget().setEnabled(false);
+ rpc.disableOnClick();
+ }
+
+ // Add mouse details
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event.getNativeEvent(), getWidget()
+ .getElement());
+ rpc.click(details);
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonServerRpc.java
new file mode 100644
index 0000000000..4a379c9262
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonServerRpc.java
@@ -0,0 +1,28 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.button;
+
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+/**
+ * RPC interface for calls from client to server.
+ *
+ * @since 7.0
+ */
+public interface ButtonServerRpc extends ServerRpc {
+ /**
+ * Button click event.
+ *
+ * @param mouseEventDetails
+ * serialized mouse event details
+ */
+ public void click(MouseEventDetails mouseEventDetails);
+
+ /**
+ * Indicate to the server that the client has disabled the button as a
+ * result of a click.
+ */
+ public void disableOnClick();
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java
new file mode 100644
index 0000000000..f26cdae0c6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java
@@ -0,0 +1,65 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.button;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.ui.Button;
+
+/**
+ * Shared state for Button and NativeButton.
+ *
+ * @see ComponentState
+ *
+ * @since 7.0
+ */
+public class ButtonState extends ComponentState {
+ private boolean disableOnClick = false;
+ private int clickShortcutKeyCode = 0;
+
+ /**
+ * Checks whether the button should be disabled on the client side on next
+ * click.
+ *
+ * @return true if the button should be disabled on click
+ */
+ public boolean isDisableOnClick() {
+ return disableOnClick;
+ }
+
+ /**
+ * Sets whether the button should be disabled on the client side on next
+ * click.
+ *
+ * @param disableOnClick
+ * true if the button should be disabled on click
+ */
+ public void setDisableOnClick(boolean disableOnClick) {
+ this.disableOnClick = disableOnClick;
+ }
+
+ /**
+ * Returns the key code for activating the button via a keyboard shortcut.
+ *
+ * See {@link Button#setClickShortcut(int, int...)} for more information.
+ *
+ * @return key code or 0 for none
+ */
+ public int getClickShortcutKeyCode() {
+ return clickShortcutKeyCode;
+ }
+
+ /**
+ * Sets the key code for activating the button via a keyboard shortcut.
+ *
+ * See {@link Button#setClickShortcut(int, int...)} for more information.
+ *
+ * @param clickShortcutKeyCode
+ * key code or 0 for none
+ */
+ public void setClickShortcutKeyCode(int clickShortcutKeyCode) {
+ this.clickShortcutKeyCode = clickShortcutKeyCode;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VButton.java b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
index 9188f7406a..f7d73d3b5e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VButton.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
@@ -2,48 +2,34 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.button;
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.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Accessibility;
import com.google.gwt.user.client.ui.FocusWidget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.EventHelper;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Icon;
-public class VButton extends FocusWidget implements Paintable, ClickHandler,
- FocusHandler, BlurHandler {
+public class VButton extends FocusWidget implements ClickHandler {
public static final String CLASSNAME = "v-button";
private static final String CLASSNAME_PRESSED = "v-pressed";
- public static final String ATTR_DISABLE_ON_CLICK = "dc";
-
// mouse movement is checked before synthesizing click event on mouseout
protected static int MOVE_THRESHOLD = 3;
protected int mousedownX = 0;
protected int mousedownY = 0;
- protected String id;
-
protected ApplicationConnection client;
protected final Element wrapper = DOM.createSpan();
@@ -65,8 +51,6 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
private int tabIndex = 0;
- private boolean disableOnClick = false;
-
/*
* BELOW PRIVATE MEMBERS COPY-PASTED FROM GWT CustomButton
*/
@@ -88,10 +72,7 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
private boolean disallowNextClick = false;
private boolean isHovering;
- private HandlerRegistration focusHandlerRegistration;
- private HandlerRegistration blurHandlerRegistration;
-
- private int clickShortcut = 0;
+ protected int clickShortcut = 0;
public VButton() {
super(DOM.createDiv());
@@ -113,64 +94,6 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
addClickHandler(this);
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- // Ensure correct implementation,
- // but don't let container manage caption etc.
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
-
- focusHandlerRegistration = EventHelper.updateFocusHandler(this, client,
- focusHandlerRegistration);
- blurHandlerRegistration = EventHelper.updateBlurHandler(this, client,
- blurHandlerRegistration);
-
- // Save details
- this.client = client;
- id = uidl.getId();
-
- // Set text
- setText(uidl.getStringAttribute("caption"));
-
- disableOnClick = uidl.hasAttribute(ATTR_DISABLE_ON_CLICK);
-
- // handle error
- if (uidl.hasAttribute("error")) {
- if (errorIndicatorElement == null) {
- errorIndicatorElement = DOM.createSpan();
- errorIndicatorElement.setClassName("v-errorindicator");
- }
- wrapper.insertBefore(errorIndicatorElement, captionElement);
-
- // Fix for IE6, IE7
- if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE7()) {
- errorIndicatorElement.setInnerText(" ");
- }
-
- } else if (errorIndicatorElement != null) {
- wrapper.removeChild(errorIndicatorElement);
- errorIndicatorElement = null;
- }
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- icon = new Icon(client);
- wrapper.insertBefore(icon.getElement(), captionElement);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- } else {
- if (icon != null) {
- wrapper.removeChild(icon.getElement());
- icon = null;
- }
- }
-
- if (uidl.hasAttribute("keycode")) {
- clickShortcut = uidl.getIntAttribute("keycode");
- }
- }
-
public void setText(String text) {
captionElement.setInnerText(text);
}
@@ -250,10 +173,9 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
if (BrowserInfo.get().isIE() || BrowserInfo.get().isOpera()) {
removeStyleName(CLASSNAME_PRESSED);
}
- // Explicitly prevent IE 6 to 8 from propagating mouseup events
+ // Explicitly prevent IE 8 from propagating mouseup events
// upward (fixes #6753)
- if (BrowserInfo.get().isIE()
- && BrowserInfo.get().getIEVersion() < 9) {
+ if (BrowserInfo.get().isIE8()) {
event.stopPropagation();
}
}
@@ -359,23 +281,9 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
* .dom.client.ClickEvent)
*/
public void onClick(ClickEvent event) {
- if (id == null || client == null) {
- return;
- }
if (BrowserInfo.get().isSafari()) {
VButton.this.setFocus(true);
}
- if (disableOnClick) {
- setEnabled(false);
- client.updateVariable(id, "disabledOnClick", true, false);
- }
-
- client.updateVariable(id, "state", true, false);
-
- // Add mouse details
- MouseEventDetails details = new MouseEventDetails(
- event.getNativeEvent(), getElement());
- client.updateVariable(id, "mousedetails", details.serialize(), true);
clickPending = false;
}
@@ -457,25 +365,6 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
}
}
- @Override
- public void setWidth(String width) {
- if (BrowserInfo.get().isIE6() || BrowserInfo.get().isIE7()) {
- if (width != null && width.length() > 2) {
- // Assume pixel values are always sent from
- // ApplicationConnection
- int w = Integer
- .parseInt(width.substring(0, width.length() - 2));
- w -= getHorizontalBorderAndPaddingWidth(getElement());
- if (w < 0) {
- // validity check for IE
- w = 0;
- }
- width = w + "px";
- }
- }
- super.setWidth(width);
- }
-
private static native int getHorizontalBorderAndPaddingWidth(Element elem)
/*-{
// THIS METHOD IS ONLY USED FOR INTERNET EXPLORER, IT DOESN'T WORK WITH OTHERS
@@ -527,12 +416,4 @@ public class VButton extends FocusWidget implements Paintable, ClickHandler,
return ret;
}-*/;
- public void onFocus(FocusEvent arg0) {
- client.updateVariable(id, EventId.FOCUS, "", true);
- }
-
- public void onBlur(BlurEvent arg0) {
- client.updateVariable(id, EventId.BLUR, "", true);
- }
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxConnector.java b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxConnector.java
new file mode 100644
index 0000000000..731838371f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxConnector.java
@@ -0,0 +1,148 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.checkbox;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+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.terminal.gwt.client.EventHelper;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.communication.FieldRpc.FocusAndBlurServerRpc;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.ui.CheckBox;
+
+@Connect(CheckBox.class)
+public class CheckBoxConnector extends AbstractFieldConnector implements
+ FocusHandler, BlurHandler, ClickHandler {
+
+ private HandlerRegistration focusHandlerRegistration;
+ private HandlerRegistration blurHandlerRegistration;
+
+ private CheckBoxServerRpc rpc = RpcProxy.create(CheckBoxServerRpc.class,
+ this);
+ private FocusAndBlurServerRpc focusBlurRpc = RpcProxy.create(
+ FocusAndBlurServerRpc.class, this);
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ getWidget().addClickHandler(this);
+ getWidget().client = getConnection();
+ getWidget().id = getConnectorId();
+
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ focusHandlerRegistration = EventHelper.updateFocusHandler(this,
+ focusHandlerRegistration);
+ blurHandlerRegistration = EventHelper.updateBlurHandler(this,
+ blurHandlerRegistration);
+
+ if (null != getState().getErrorMessage()) {
+ if (getWidget().errorIndicatorElement == null) {
+ getWidget().errorIndicatorElement = DOM.createSpan();
+ getWidget().errorIndicatorElement.setInnerHTML("&nbsp;");
+ DOM.setElementProperty(getWidget().errorIndicatorElement,
+ "className", "v-errorindicator");
+ DOM.appendChild(getWidget().getElement(),
+ getWidget().errorIndicatorElement);
+ DOM.sinkEvents(getWidget().errorIndicatorElement,
+ VTooltip.TOOLTIP_EVENTS | Event.ONCLICK);
+ } else {
+ DOM.setStyleAttribute(getWidget().errorIndicatorElement,
+ "display", "");
+ }
+ } else if (getWidget().errorIndicatorElement != null) {
+ DOM.setStyleAttribute(getWidget().errorIndicatorElement, "display",
+ "none");
+ }
+
+ if (isReadOnly()) {
+ getWidget().setEnabled(false);
+ }
+
+ if (getState().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(getState().getIcon().getURL());
+ } else if (getWidget().icon != null) {
+ // detach icon
+ DOM.removeChild(getWidget().getElement(),
+ getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+
+ // Set text
+ getWidget().setText(getState().getCaption());
+ getWidget().setValue(getState().isChecked());
+ getWidget().immediate = getState().isImmediate();
+ }
+
+ @Override
+ public CheckBoxState getState() {
+ return (CheckBoxState) super.getState();
+ }
+
+ @Override
+ public VCheckBox getWidget() {
+ return (VCheckBox) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VCheckBox.class);
+ }
+
+ public void onFocus(FocusEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurRpc.focus();
+ }
+
+ public void onBlur(BlurEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurRpc.blur();
+ }
+
+ public void onClick(ClickEvent event) {
+ if (!isEnabled()) {
+ return;
+ }
+
+ // Add mouse details
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event.getNativeEvent(), getWidget()
+ .getElement());
+ rpc.setChecked(getWidget().getValue(), details);
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxServerRpc.java
new file mode 100644
index 0000000000..05091ff6ed
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.checkbox;
+
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public interface CheckBoxServerRpc extends ServerRpc {
+ public void setChecked(boolean checked, MouseEventDetails mouseEventDetails);
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxState.java b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxState.java
new file mode 100644
index 0000000000..d6d51cad36
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/checkbox/CheckBoxState.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.checkbox;
+
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
+
+public class CheckBoxState extends AbstractFieldState {
+ private boolean checked = false;
+
+ public boolean isChecked() {
+ return checked;
+ }
+
+ public void setChecked(boolean checked) {
+ this.checked = checked;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/checkbox/VCheckBox.java b/src/com/vaadin/terminal/gwt/client/ui/checkbox/VCheckBox.java
new file mode 100644
index 0000000000..fd90796ea5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/checkbox/VCheckBox.java
@@ -0,0 +1,61 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.checkbox;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+
+public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements
+ Field {
+
+ public static final String CLASSNAME = "v-checkbox";
+
+ String id;
+
+ boolean immediate;
+
+ ApplicationConnection client;
+
+ Element errorIndicatorElement;
+
+ Icon icon;
+
+ public VCheckBox() {
+ setStyleName(CLASSNAME);
+
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ Element el = DOM.getFirstChild(getElement());
+ while (el != null) {
+ DOM.sinkEvents(el,
+ (DOM.getEventsSunk(el) | VTooltip.TOOLTIP_EVENTS));
+ el = DOM.getNextSibling(el);
+ }
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ if (icon != null && (event.getTypeInt() == Event.ONCLICK)
+ && (DOM.eventGetTarget(event) == icon.getElement())) {
+ // Click on icon should do nothing if widget is disabled
+ if (isEnabled()) {
+ setValue(!getValue());
+ }
+ }
+ super.onBrowserEvent(event);
+ if (event.getTypeInt() == Event.ONLOAD) {
+ Util.notifyParentOfSizeChange(this, true);
+ }
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/combobox/ComboBoxConnector.java b/src/com/vaadin/terminal/gwt/client/ui/combobox/ComboBoxConnector.java
new file mode 100644
index 0000000000..ee16d90b12
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/combobox/ComboBoxConnector.java
@@ -0,0 +1,246 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.combobox;
+
+import java.util.Iterator;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.combobox.VFilterSelect.FilterSelectSuggestion;
+import com.vaadin.terminal.gwt.client.ui.menubar.MenuItem;
+import com.vaadin.ui.Select;
+
+@Connect(Select.class)
+public class ComboBoxConnector extends AbstractFieldConnector implements
+ Paintable, SimpleManagedLayout {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal
+ * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection)
+ */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // Save details
+ getWidget().client = client;
+ getWidget().paintableId = uidl.getId();
+
+ getWidget().readonly = isReadOnly();
+ getWidget().enabled = isEnabled();
+
+ getWidget().tb.setEnabled(getWidget().enabled);
+ getWidget().updateReadOnly();
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // Inverse logic here to make the default case (text input enabled)
+ // work without additional UIDL messages
+ boolean noTextInput = uidl
+ .hasAttribute(VFilterSelect.ATTR_NO_TEXT_INPUT)
+ && uidl.getBooleanAttribute(VFilterSelect.ATTR_NO_TEXT_INPUT);
+ getWidget().setTextInputEnabled(!noTextInput);
+
+ // not a FocusWidget -> needs own tabindex handling
+ if (uidl.hasAttribute("tabindex")) {
+ getWidget().tb.setTabIndex(uidl.getIntAttribute("tabindex"));
+ }
+
+ if (uidl.hasAttribute("filteringmode")) {
+ getWidget().filteringmode = uidl.getIntAttribute("filteringmode");
+ }
+
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().nullSelectionAllowed = uidl.hasAttribute("nullselect");
+
+ getWidget().nullSelectItem = uidl.hasAttribute("nullselectitem")
+ && uidl.getBooleanAttribute("nullselectitem");
+
+ getWidget().currentPage = uidl.getIntVariable("page");
+
+ if (uidl.hasAttribute("pagelength")) {
+ getWidget().pageLength = uidl.getIntAttribute("pagelength");
+ }
+
+ if (uidl.hasAttribute(VFilterSelect.ATTR_INPUTPROMPT)) {
+ // input prompt changed from server
+ getWidget().inputPrompt = uidl
+ .getStringAttribute(VFilterSelect.ATTR_INPUTPROMPT);
+ } else {
+ getWidget().inputPrompt = "";
+ }
+
+ getWidget().suggestionPopup.updateStyleNames(uidl, getState());
+
+ getWidget().allowNewItem = uidl.hasAttribute("allownewitem");
+ getWidget().lastNewItemString = null;
+
+ getWidget().currentSuggestions.clear();
+ if (!getWidget().waitingForFilteringResponse) {
+ /*
+ * Clear the current suggestions as the server response always
+ * includes the new ones. Exception is when filtering, then we need
+ * to retain the value if the user does not select any of the
+ * options matching the filter.
+ */
+ getWidget().currentSuggestion = null;
+ /*
+ * Also ensure no old items in menu. Unless cleared the old values
+ * may cause odd effects on blur events. Suggestions in menu might
+ * not necessary exist in select at all anymore.
+ */
+ getWidget().suggestionPopup.menu.clearItems();
+
+ }
+
+ final UIDL options = uidl.getChildUIDL(0);
+ if (uidl.hasAttribute("totalMatches")) {
+ getWidget().totalMatches = uidl.getIntAttribute("totalMatches");
+ } else {
+ getWidget().totalMatches = 0;
+ }
+
+ // used only to calculate minimum popup width
+ String captions = Util.escapeHTML(getWidget().inputPrompt);
+
+ for (final Iterator<?> i = options.getChildIterator(); i.hasNext();) {
+ final UIDL optionUidl = (UIDL) i.next();
+ final FilterSelectSuggestion suggestion = getWidget().new FilterSelectSuggestion(
+ optionUidl);
+ getWidget().currentSuggestions.add(suggestion);
+ if (optionUidl.hasAttribute("selected")) {
+ if (!getWidget().waitingForFilteringResponse
+ || getWidget().popupOpenerClicked) {
+ String newSelectedOptionKey = Integer.toString(suggestion
+ .getOptionKey());
+ if (!newSelectedOptionKey
+ .equals(getWidget().selectedOptionKey)
+ || suggestion.getReplacementString().equals(
+ getWidget().tb.getText())) {
+ // Update text field if we've got a new selection
+ // Also update if we've got the same text to retain old
+ // text selection behavior
+ getWidget().setPromptingOff(
+ suggestion.getReplacementString());
+ getWidget().selectedOptionKey = newSelectedOptionKey;
+ }
+ }
+ getWidget().currentSuggestion = suggestion;
+ getWidget().setSelectedItemIcon(suggestion.getIconUri());
+ }
+
+ // Collect captions so we can calculate minimum width for textarea
+ if (captions.length() > 0) {
+ captions += "|";
+ }
+ captions += Util.escapeHTML(suggestion.getReplacementString());
+ }
+
+ if ((!getWidget().waitingForFilteringResponse || getWidget().popupOpenerClicked)
+ && uidl.hasVariable("selected")
+ && uidl.getStringArrayVariable("selected").length == 0) {
+ // select nulled
+ if (!getWidget().waitingForFilteringResponse
+ || !getWidget().popupOpenerClicked) {
+ if (!getWidget().focused) {
+ /*
+ * client.updateComponent overwrites all styles so we must
+ * ALWAYS set the prompting style at this point, even though
+ * we think it has been set already...
+ */
+ getWidget().prompting = false;
+ getWidget().setPromptingOn();
+ } else {
+ // we have focus in field, prompting can't be set on,
+ // instead just clear the input
+ getWidget().tb.setValue("");
+ }
+ }
+ getWidget().setSelectedItemIcon(null);
+ getWidget().selectedOptionKey = null;
+ }
+
+ if (getWidget().waitingForFilteringResponse
+ && getWidget().lastFilter.toLowerCase().equals(
+ uidl.getStringVariable("filter"))) {
+ getWidget().suggestionPopup.showSuggestions(
+ getWidget().currentSuggestions, getWidget().currentPage,
+ getWidget().totalMatches);
+ getWidget().waitingForFilteringResponse = false;
+ if (!getWidget().popupOpenerClicked
+ && getWidget().selectPopupItemWhenResponseIsReceived != VFilterSelect.Select.NONE) {
+ // we're paging w/ arrows
+ if (getWidget().selectPopupItemWhenResponseIsReceived == VFilterSelect.Select.LAST) {
+ getWidget().suggestionPopup.menu.selectLastItem();
+ } else {
+ getWidget().suggestionPopup.menu.selectFirstItem();
+ }
+
+ // This is used for paging so we update the keyboard selection
+ // variable as well.
+ MenuItem activeMenuItem = getWidget().suggestionPopup.menu
+ .getSelectedItem();
+ getWidget().suggestionPopup.menu
+ .setKeyboardSelectedItem(activeMenuItem);
+
+ // Update text field to contain the correct text
+ getWidget().setTextboxText(activeMenuItem.getText());
+ getWidget().tb.setSelectionRange(
+ getWidget().lastFilter.length(),
+ activeMenuItem.getText().length()
+ - getWidget().lastFilter.length());
+
+ getWidget().selectPopupItemWhenResponseIsReceived = VFilterSelect.Select.NONE; // reset
+ }
+ if (getWidget().updateSelectionWhenReponseIsReceived) {
+ getWidget().suggestionPopup.menu
+ .doPostFilterSelectedItemAction();
+ }
+ }
+
+ // Calculate minumum textarea width
+ getWidget().suggestionPopupMinWidth = getWidget().minWidth(captions);
+
+ getWidget().popupOpenerClicked = false;
+
+ if (!getWidget().initDone) {
+ getWidget().updateRootWidth();
+ }
+
+ // Focus dependent style names are lost during the update, so we add
+ // them here back again
+ if (getWidget().focused) {
+ getWidget().addStyleDependentName("focus");
+ }
+
+ getWidget().initDone = true;
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VFilterSelect.class);
+ }
+
+ @Override
+ public VFilterSelect getWidget() {
+ return (VFilterSelect) super.getWidget();
+ }
+
+ public void layout() {
+ VFilterSelect widget = getWidget();
+ if (widget.initDone) {
+ widget.updateRootWidth();
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java
index 8362d6fbec..d29eda0d6a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/combobox/VFilterSelect.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.combobox;
import java.util.ArrayList;
import java.util.Collection;
@@ -12,9 +12,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.Style.Overflow;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -46,13 +46,21 @@ import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.TextBox;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.menubar.MenuBar;
+import com.vaadin.terminal.gwt.client.ui.menubar.MenuItem;
/**
* Client side implementation of the Select component.
@@ -60,9 +68,8 @@ import com.vaadin.terminal.gwt.client.VTooltip;
* TODO needs major refactoring (to be extensible etc)
*/
@SuppressWarnings("deprecation")
-public class VFilterSelect extends Composite implements Paintable, Field,
- KeyDownHandler, KeyUpHandler, ClickHandler, FocusHandler, BlurHandler,
- Focusable {
+public class VFilterSelect extends Composite implements Field, KeyDownHandler,
+ KeyUpHandler, ClickHandler, FocusHandler, BlurHandler, Focusable {
/**
* Represents a suggestion in the suggestion popup box
@@ -154,7 +161,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
private static final String Z_INDEX = "30000";
- private final SuggestionMenu menu;
+ protected final SuggestionMenu menu;
private final Element up = DOM.createDiv();
private final Element down = DOM.createDiv();
@@ -543,14 +550,19 @@ public class VFilterSelect extends Composite implements Paintable, Field,
/**
* Updates style names in suggestion popup to help theme building.
+ *
+ * @param uidl
+ * UIDL for the whole combo box
+ * @param componentState
+ * shared state of the combo box
*/
- public void updateStyleNames(UIDL uidl) {
- if (uidl.hasAttribute("style")) {
- setStyleName(CLASSNAME + "-suggestpopup");
- final String[] styles = uidl.getStringAttribute("style").split(
- " ");
- for (int i = 0; i < styles.length; i++) {
- addStyleDependentName(styles[i]);
+ public void updateStyleNames(UIDL uidl, ComponentState componentState) {
+ setStyleName(CLASSNAME + "-suggestpopup");
+ if (componentState.hasStyles()) {
+ for (String style : componentState.getStyles()) {
+ if (!"".equals(style)) {
+ addStyleDependentName(style);
+ }
}
}
}
@@ -762,12 +774,6 @@ public class VFilterSelect extends Composite implements Paintable, Field,
}
public void onLoad(LoadEvent event) {
- if (BrowserInfo.get().isIE6()) {
- // Ensure PNG transparency works in IE6
- Util.doIE6PngFix((Element) Element.as(event.getNativeEvent()
- .getEventTarget()));
- }
-
// Handle icon onload events to ensure shadow is resized
// correctly
delayedImageLoadExecutioner.trigger();
@@ -783,7 +789,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
return keyboardSelectedItem;
}
- private void setKeyboardSelectedItem(MenuItem firstItem) {
+ protected void setKeyboardSelectedItem(MenuItem firstItem) {
keyboardSelectedItem = firstItem;
}
@@ -810,7 +816,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
/**
* The text box where the filter is written
*/
- private final TextBox tb = new TextBox() {
+ protected final TextBox tb = new TextBox() {
/*
* (non-Javadoc)
*
@@ -837,7 +843,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
};
};
- private final SuggestionPopup suggestionPopup = new SuggestionPopup();
+ protected final SuggestionPopup suggestionPopup = new SuggestionPopup();
/**
* Used when measuring the width of the popup
@@ -868,74 +874,70 @@ public class VFilterSelect extends Composite implements Paintable, Field,
private final Image selectedItemIcon = new Image();
- private ApplicationConnection client;
+ protected ApplicationConnection client;
- private String paintableId;
+ protected String paintableId;
- private int currentPage;
+ protected int currentPage;
/**
* A collection of available suggestions (options) as received from the
* server.
*/
- private final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
+ protected final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
- private boolean immediate;
+ protected boolean immediate;
- private String selectedOptionKey;
+ protected String selectedOptionKey;
- private boolean waitingForFilteringResponse = false;
- private boolean updateSelectionWhenReponseIsReceived = false;
+ protected boolean waitingForFilteringResponse = false;
+ protected boolean updateSelectionWhenReponseIsReceived = false;
private boolean tabPressedWhenPopupOpen = false;
- private boolean initDone = false;
+ protected boolean initDone = false;
- private String lastFilter = "";
+ protected String lastFilter = "";
- private enum Select {
+ protected enum Select {
NONE, FIRST, LAST
};
- private Select selectPopupItemWhenResponseIsReceived = Select.NONE;
+ protected Select selectPopupItemWhenResponseIsReceived = Select.NONE;
/**
* The current suggestion selected from the dropdown. This is one of the
* values in currentSuggestions except when filtering, in this case
* currentSuggestion might not be in currentSuggestions.
*/
- private FilterSelectSuggestion currentSuggestion;
+ protected FilterSelectSuggestion currentSuggestion;
- private int totalMatches;
- private boolean allowNewItem;
- private boolean nullSelectionAllowed;
- private boolean nullSelectItem;
- private boolean enabled;
- private boolean readonly;
+ protected int totalMatches;
+ protected boolean allowNewItem;
+ protected boolean nullSelectionAllowed;
+ protected boolean nullSelectItem;
+ protected boolean enabled;
+ protected boolean readonly;
- private int filteringmode = FILTERINGMODE_OFF;
+ protected int filteringmode = FILTERINGMODE_OFF;
// shown in unfocused empty field, disappears on focus (e.g "Search here")
private static final String CLASSNAME_PROMPT = "prompt";
- private static final String ATTR_INPUTPROMPT = "prompt";
+ protected static final String ATTR_INPUTPROMPT = "prompt";
public static final String ATTR_NO_TEXT_INPUT = "noInput";
- private String inputPrompt = "";
- private boolean prompting = false;
+ protected String inputPrompt = "";
+ protected boolean prompting = false;
// Set true when popupopened has been clicked. Cleared on each UIDL-update.
// This handles the special case where are not filtering yet and the
// selected value has changed on the server-side. See #2119
- private boolean popupOpenerClicked;
- private String width = null;
- private int textboxPadding = -1;
- private int componentPadding = -1;
- private int suggestionPopupMinWidth = 0;
+ protected boolean popupOpenerClicked;
+ protected int suggestionPopupMinWidth = 0;
private int popupWidth = -1;
/*
* Stores the last new item string to avoid double submissions. Cleared on
* uidl updates
*/
- private String lastNewItemString;
- private boolean focused = false;
- private int horizPaddingAndBorder = 2;
+ protected String lastNewItemString;
+ protected boolean focused = false;
/**
* If set to false, the component should not allow entering text to the
@@ -950,16 +952,13 @@ public class VFilterSelect extends Composite implements Paintable, Field,
selectedItemIcon.setStyleName("v-icon");
selectedItemIcon.addLoadHandler(new LoadHandler() {
public void onLoad(LoadEvent event) {
+ if (BrowserInfo.get().isIE8()) {
+ // IE8 needs some help to discover it should reposition the
+ // text field
+ forceReflow();
+ }
updateRootWidth();
updateSelectedIconPosition();
- /*
- * Workaround for an IE bug where the text is positioned below
- * the icon (#3991)
- */
- if (BrowserInfo.get().isIE()) {
- Util.setStyleTemporarily(tb.getElement(), "paddingLeft",
- "0");
- }
}
});
@@ -1051,206 +1050,11 @@ public class VFilterSelect extends Composite implements Paintable, Field,
currentPage = page;
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal
- * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection)
- */
- @SuppressWarnings("deprecation")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- paintableId = uidl.getId();
- this.client = client;
-
- readonly = uidl.hasAttribute("readonly");
- enabled = !uidl.hasAttribute("disabled");
-
- tb.setEnabled(enabled);
- updateReadOnly();
-
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- // Inverse logic here to make the default case (text input enabled)
- // work without additional UIDL messages
- boolean noTextInput = uidl.hasAttribute(ATTR_NO_TEXT_INPUT)
- && uidl.getBooleanAttribute(ATTR_NO_TEXT_INPUT);
- setTextInputEnabled(!noTextInput);
-
- // not a FocusWidget -> needs own tabindex handling
- if (uidl.hasAttribute("tabindex")) {
- tb.setTabIndex(uidl.getIntAttribute("tabindex"));
- }
-
- if (uidl.hasAttribute("filteringmode")) {
- filteringmode = uidl.getIntAttribute("filteringmode");
- }
-
- immediate = uidl.hasAttribute("immediate");
-
- nullSelectionAllowed = uidl.hasAttribute("nullselect");
-
- nullSelectItem = uidl.hasAttribute("nullselectitem")
- && uidl.getBooleanAttribute("nullselectitem");
-
- currentPage = uidl.getIntVariable("page");
-
- if (uidl.hasAttribute("pagelength")) {
- pageLength = uidl.getIntAttribute("pagelength");
- }
-
- if (uidl.hasAttribute(ATTR_INPUTPROMPT)) {
- // input prompt changed from server
- inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT);
- } else {
- inputPrompt = "";
- }
-
- suggestionPopup.updateStyleNames(uidl);
-
- allowNewItem = uidl.hasAttribute("allownewitem");
- lastNewItemString = null;
-
- currentSuggestions.clear();
- if (!waitingForFilteringResponse) {
- /*
- * Clear the current suggestions as the server response always
- * includes the new ones. Exception is when filtering, then we need
- * to retain the value if the user does not select any of the
- * options matching the filter.
- */
- currentSuggestion = null;
- /*
- * Also ensure no old items in menu. Unless cleared the old values
- * may cause odd effects on blur events. Suggestions in menu might
- * not necessary exist in select at all anymore.
- */
- suggestionPopup.menu.clearItems();
-
- }
-
- final UIDL options = uidl.getChildUIDL(0);
- if (uidl.hasAttribute("totalMatches")) {
- totalMatches = uidl.getIntAttribute("totalMatches");
- } else {
- totalMatches = 0;
- }
-
- // used only to calculate minimum popup width
- String captions = Util.escapeHTML(inputPrompt);
-
- for (final Iterator<?> i = options.getChildIterator(); i.hasNext();) {
- final UIDL optionUidl = (UIDL) i.next();
- final FilterSelectSuggestion suggestion = new FilterSelectSuggestion(
- optionUidl);
- currentSuggestions.add(suggestion);
- if (optionUidl.hasAttribute("selected")) {
- if (!waitingForFilteringResponse || popupOpenerClicked) {
- String newSelectedOptionKey = Integer.toString(suggestion
- .getOptionKey());
- if (!newSelectedOptionKey.equals(selectedOptionKey)
- || suggestion.getReplacementString().equals(
- tb.getText())) {
- // Update text field if we've got a new selection
- // Also update if we've got the same text to retain old
- // text selection behavior
- setPromptingOff(suggestion.getReplacementString());
- selectedOptionKey = newSelectedOptionKey;
- }
- }
- currentSuggestion = suggestion;
- setSelectedItemIcon(suggestion.getIconUri());
- }
-
- // Collect captions so we can calculate minimum width for textarea
- if (captions.length() > 0) {
- captions += "|";
- }
- captions += Util.escapeHTML(suggestion.getReplacementString());
- }
-
- if ((!waitingForFilteringResponse || popupOpenerClicked)
- && uidl.hasVariable("selected")
- && uidl.getStringArrayVariable("selected").length == 0) {
- // select nulled
- if (!waitingForFilteringResponse || !popupOpenerClicked) {
- if (!focused) {
- /*
- * client.updateComponent overwrites all styles so we must
- * ALWAYS set the prompting style at this point, even though
- * we think it has been set already...
- */
- prompting = false;
- setPromptingOn();
- } else {
- // we have focus in field, prompting can't be set on,
- // instead just clear the input
- tb.setValue("");
- }
- }
-
- setSelectedItemIcon(null);
- selectedOptionKey = null;
- }
-
- if (waitingForFilteringResponse
- && lastFilter.toLowerCase().equals(
- uidl.getStringVariable("filter"))) {
- suggestionPopup.showSuggestions(currentSuggestions, currentPage,
- totalMatches);
- waitingForFilteringResponse = false;
- if (!popupOpenerClicked
- && selectPopupItemWhenResponseIsReceived != Select.NONE) {
- // we're paging w/ arrows
- if (selectPopupItemWhenResponseIsReceived == Select.LAST) {
- suggestionPopup.menu.selectLastItem();
- } else {
- suggestionPopup.menu.selectFirstItem();
- }
-
- // This is used for paging so we update the keyboard selection
- // variable as well.
- MenuItem activeMenuItem = suggestionPopup.menu
- .getSelectedItem();
- suggestionPopup.menu.setKeyboardSelectedItem(activeMenuItem);
-
- // Update text field to contain the correct text
- setTextboxText(activeMenuItem.getText());
- tb.setSelectionRange(lastFilter.length(), activeMenuItem
- .getText().length() - lastFilter.length());
-
- selectPopupItemWhenResponseIsReceived = Select.NONE; // reset
- }
- if (updateSelectionWhenReponseIsReceived) {
- suggestionPopup.menu.doPostFilterSelectedItemAction();
- }
- }
-
- // Calculate minumum textarea width
- suggestionPopupMinWidth = minWidth(captions);
-
- popupOpenerClicked = false;
-
- if (!initDone) {
- updateRootWidth();
- }
-
- // Focus dependent style names are lost during the update, so we add
- // them here back again
- if (focused) {
- addStyleDependentName("focus");
- }
-
- initDone = true;
- }
-
- private void updateReadOnly() {
+ protected void updateReadOnly() {
tb.setReadOnly(readonly || !textInputEnabled);
}
- private void setTextInputEnabled(boolean textInputEnabled) {
+ protected void setTextInputEnabled(boolean textInputEnabled) {
// Always update styles as they might have been overwritten
if (textInputEnabled) {
removeStyleDependentName(STYLE_NO_INPUT);
@@ -1267,45 +1071,20 @@ public class VFilterSelect extends Composite implements Paintable, Field,
}
/**
- * Sets the text in the text box using a deferred command if on Gecko. This
- * is required for performance reasons (see #3663).
+ * Sets the text in the text box.
*
* @param text
* the text to set in the text box
*/
- private void setTextboxText(final String text) {
- if (BrowserInfo.get().isFF3()) {
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- tb.setText(text);
- }
- });
- } else {
- tb.setText(text);
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.google.gwt.user.client.ui.Composite#onAttach()
- */
- @Override
- protected void onAttach() {
- super.onAttach();
-
- /*
- * We need to recalculate the root width when the select is attached, so
- * #2974 won't happen.
- */
- updateRootWidth();
+ protected void setTextboxText(final String text) {
+ tb.setText(text);
}
/**
* Turns prompting on. When prompting is turned on a command prompt is shown
* in the text box if nothing has been entered.
*/
- private void setPromptingOn() {
+ protected void setPromptingOn() {
if (!prompting) {
prompting = true;
addStyleDependentName(CLASSNAME_PROMPT);
@@ -1320,7 +1099,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* @param text
* The text the text box should contain.
*/
- private void setPromptingOff(String text) {
+ protected void setPromptingOff(String text) {
setTextboxText(text);
if (prompting) {
prompting = false;
@@ -1370,10 +1149,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* @param iconUri
* The URI of the icon
*/
- private void setSelectedItemIcon(String iconUri) {
+ protected void setSelectedItemIcon(String iconUri) {
if (iconUri == null || iconUri.length() == 0) {
if (selectedItemIcon.isAttached()) {
panel.remove(selectedItemIcon);
+ if (BrowserInfo.get().isIE8()) {
+ // IE8 needs some help to discover it should reposition the
+ // text field
+ forceReflow();
+ }
updateRootWidth();
}
} else {
@@ -1384,6 +1168,10 @@ public class VFilterSelect extends Composite implements Paintable, Field,
}
}
+ private void forceReflow() {
+ Util.setStyleTemporarily(tb.getElement(), "zoom", "1");
+ }
+
/**
* Positions the icon vertically in the middle. Should be called after the
* icon has loaded
@@ -1391,13 +1179,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
private void updateSelectedIconPosition() {
// Position icon vertically to middle
int availableHeight = 0;
- if (BrowserInfo.get().isIE6()) {
- getElement().getStyle().setOverflow(Overflow.HIDDEN);
- availableHeight = getOffsetHeight();
- getElement().getStyle().setProperty("overflow", "");
- } else {
- availableHeight = getOffsetHeight();
- }
+ availableHeight = getOffsetHeight();
int iconHeight = Util.getRequiredHeight(selectedItemIcon);
int marginTop = (availableHeight - iconHeight) / 2;
@@ -1658,7 +1440,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
/**
* Calculate minimum width for FilterSelect textarea
*/
- private native int minWidth(String captions)
+ protected native int minWidth(String captions)
/*-{
if(!captions || captions.length <= 0)
return 0;
@@ -1793,68 +1575,14 @@ public class VFilterSelect extends Composite implements Paintable, Field,
tb.setFocus(true);
}
- /*
- * (non-Javadoc)
- *
- * @see com.google.gwt.user.client.ui.UIObject#setWidth(java.lang.String)
- */
- @Override
- public void setWidth(String width) {
- if (width == null || width.equals("")) {
- this.width = null;
- } else {
- this.width = width;
- }
-
- if (BrowserInfo.get().isIE6()) {
- // Required in IE when textfield is wider than this.width
- getElement().getStyle().setOverflow(Overflow.HIDDEN);
- horizPaddingAndBorder = Util.setWidthExcludingPaddingAndBorder(
- this, width, horizPaddingAndBorder);
- getElement().getStyle().setProperty("overflow", "");
- } else {
- horizPaddingAndBorder = Util.setWidthExcludingPaddingAndBorder(
- this, width, horizPaddingAndBorder);
- }
-
- if (initDone) {
- updateRootWidth();
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.google.gwt.user.client.ui.UIObject#setHeight(java.lang.String)
- */
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- Util.setHeightExcludingPaddingAndBorder(tb, height, 3);
- }
-
/**
* Calculates the width of the select if the select has undefined width.
* Should be called when the width changes or when the icon changes.
*/
- private void updateRootWidth() {
- if (width == null) {
- /*
- * When the width is not specified we must specify width for root
- * div so the popupopener won't wrap to the next line and also so
- * the size of the combobox won't change over time.
- */
- int tbWidth = Util.getRequiredWidth(tb);
-
- /*
- * Note: iconWidth is here calculated as a negative pixel value so
- * you should consider this in further calculations.
- */
- int iconWidth = selectedItemIcon.isAttached() ? Util
- .measureMarginLeft(tb.getElement())
- - Util.measureMarginLeft(selectedItemIcon.getElement()) : 0;
-
- int w = tbWidth + getPopUpOpenerWidth() + iconWidth;
+ protected void updateRootWidth() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ if (paintable.isUndefinedWidth()) {
/*
* When the select has a undefined with we need to check that we are
@@ -1863,45 +1591,37 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* when the popup is used to view longer items than the text box is
* wide.
*/
+ int w = Util.getRequiredWidth(this);
if ((!initDone || currentPage + 1 < 0)
&& suggestionPopupMinWidth > w) {
- setTextboxWidth(suggestionPopupMinWidth);
- w = suggestionPopupMinWidth;
- } else {
/*
- * Firefox3 has its own way of doing rendering so we need to
- * specify the width for the TextField to make sure it actually
- * is rendered as wide as FF3 says it is
+ * We want to compensate for the paddings just to preserve the
+ * exact size as in Vaadin 6.x, but we get here before
+ * MeasuredSize has been initialized.
+ * Util.measureHorizontalPaddingAndBorder does not work with
+ * border-box, so we must do this the hard way.
*/
- tb.setWidth((tbWidth - getTextboxPadding()) + "px");
+ Style style = getElement().getStyle();
+ String originalPadding = style.getPadding();
+ String originalBorder = style.getBorderWidth();
+ style.setPaddingLeft(0, Unit.PX);
+ style.setBorderWidth(0, Unit.PX);
+ int offset = w - Util.getRequiredWidth(this);
+ style.setProperty("padding", originalPadding);
+ style.setProperty("borderWidth", originalBorder);
+
+ setWidth(suggestionPopupMinWidth + offset + "px");
}
- super.setWidth((w) + "px");
- // Freeze the initial width, so that it won't change even if the
- // icon size changes
- width = w + "px";
- } else {
/*
- * When the width is specified we also want to explicitly specify
- * widths for textbox and popupopener
+ * Lock the textbox width to its current value if it's not already
+ * locked
*/
- setTextboxWidth(getMainWidth() - getComponentPadding());
-
- }
- }
-
- /**
- * Only use the first page popup width so the textbox will not get resized
- * whenever the popup is resized. This also resolves issue where toggling
- * combo box between read only and normal state makes it grow larger.
- *
- * @return Width of popup opener
- */
- private int getPopUpOpenerWidth() {
- if (popupWidth < 0) {
- popupWidth = Util.getRequiredWidth(popupOpener);
+ if (!tb.getElement().getStyle().getWidth().endsWith("px")) {
+ tb.setWidth((tb.getOffsetWidth() - selectedItemIcon
+ .getOffsetWidth()) + "px");
+ }
}
- return popupWidth;
}
/**
@@ -1911,63 +1631,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* @return The width in pixels
*/
private int getMainWidth() {
- int componentWidth;
- if (BrowserInfo.get().isIE6()) {
- // Required in IE when textfield is wider than this.width
- getElement().getStyle().setOverflow(Overflow.HIDDEN);
- componentWidth = getOffsetWidth();
- getElement().getStyle().setProperty("overflow", "");
- } else {
- componentWidth = getOffsetWidth();
- }
- return componentWidth;
- }
-
- /**
- * Sets the text box width in pixels.
- *
- * @param componentWidth
- * The width of the text box in pixels
- */
- private void setTextboxWidth(int componentWidth) {
- int padding = getTextboxPadding();
- int iconWidth = selectedItemIcon.isAttached() ? Util
- .getRequiredWidth(selectedItemIcon) : 0;
-
- int textboxWidth = componentWidth - padding - getPopUpOpenerWidth()
- - iconWidth;
- if (textboxWidth < 0) {
- textboxWidth = 0;
- }
- tb.setWidth(textboxWidth + "px");
- }
-
- /**
- * Gets the horizontal padding of the text box in pixels. The measurement
- * includes the border width.
- *
- * @return The padding in pixels
- */
- private int getTextboxPadding() {
- if (textboxPadding < 0) {
- textboxPadding = Util.measureHorizontalPaddingAndBorder(
- tb.getElement(), 4);
- }
- return textboxPadding;
+ return getOffsetWidth();
}
- /**
- * Gets the horizontal padding of the select. The measurement includes the
- * border width.
- *
- * @return The padding in pixels
- */
- private int getComponentPadding() {
- if (componentPadding < 0) {
- componentPadding = Util.measureHorizontalPaddingAndBorder(
- getElement(), 3);
+ @Override
+ public void setWidth(String width) {
+ super.setWidth(width);
+ if (width.length() != 0) {
+ tb.setWidth("100%");
}
- return componentPadding;
}
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java
new file mode 100644
index 0000000000..7df31a8593
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java
@@ -0,0 +1,169 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.csslayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+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.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.csslayout.VCssLayout.FlowPane;
+import com.vaadin.ui.CssLayout;
+
+@Connect(CssLayout.class)
+public class CssLayoutConnector extends AbstractLayoutConnector {
+
+ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
+ this) {
+
+ @Override
+ protected ComponentConnector getChildComponent(Element element) {
+ return Util.getConnectorForElement(getConnection(), getWidget(),
+ element);
+ }
+
+ @Override
+ protected LayoutClickRpc getLayoutClickRPC() {
+ return rpc;
+ };
+ };
+
+ private CssLayoutServerRpc rpc;
+
+ private Map<ComponentConnector, VCaption> childToCaption = new HashMap<ComponentConnector, VCaption>();
+
+ @Override
+ protected void init() {
+ super.init();
+ rpc = RpcProxy.create(CssLayoutServerRpc.class, this);
+ }
+
+ @Override
+ public CssLayoutState getState() {
+ return (CssLayoutState) super.getState();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ getWidget().setMarginStyles(
+ new VMarginInfo(getState().getMarginsBitmask()));
+
+ for (ComponentConnector child : getChildren()) {
+ if (!getState().getChildCss().containsKey(child)) {
+ continue;
+ }
+ String css = getState().getChildCss().get(child);
+ Style style = child.getWidget().getElement().getStyle();
+ // should we remove styles also? How can we know what we have added
+ // as it is added directly to the child component?
+ String[] cssRules = css.split(";");
+ for (String cssRule : cssRules) {
+ String parts[] = cssRule.split(":");
+ if (parts.length == 2) {
+ style.setProperty(makeCamelCase(parts[0].trim()),
+ parts[1].trim());
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ int index = 0;
+ FlowPane cssLayoutWidgetContainer = getWidget().panel;
+ for (ComponentConnector child : getChildren()) {
+ VCaption childCaption = childToCaption.get(child);
+ if (childCaption != null) {
+ cssLayoutWidgetContainer.addOrMove(childCaption, index++);
+ }
+ cssLayoutWidgetContainer.addOrMove(child.getWidget(), index++);
+ }
+
+ // Detach old child widgets and possibly their caption
+ for (ComponentConnector child : event.getOldChildren()) {
+ if (child.getParent() == this) {
+ // Skip current children
+ continue;
+ }
+ cssLayoutWidgetContainer.remove(child.getWidget());
+ VCaption vCaption = childToCaption.remove(child);
+ if (vCaption != null) {
+ cssLayoutWidgetContainer.remove(vCaption);
+ }
+ }
+ }
+
+ private static final String makeCamelCase(String cssProperty) {
+ // TODO this might be cleaner to implement with regexp
+ while (cssProperty.contains("-")) {
+ int indexOf = cssProperty.indexOf("-");
+ cssProperty = cssProperty.substring(0, indexOf)
+ + String.valueOf(cssProperty.charAt(indexOf + 1))
+ .toUpperCase() + cssProperty.substring(indexOf + 2);
+ }
+ if ("float".equals(cssProperty)) {
+ if (BrowserInfo.get().isIE()) {
+ return "styleFloat";
+ } else {
+ return "cssFloat";
+ }
+ }
+ return cssProperty;
+ }
+
+ @Override
+ public VCssLayout getWidget() {
+ return (VCssLayout) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VCssLayout.class);
+ }
+
+ public void updateCaption(ComponentConnector child) {
+ Widget childWidget = child.getWidget();
+ FlowPane cssLayoutWidgetContainer = getWidget().panel;
+ int widgetPosition = cssLayoutWidgetContainer
+ .getWidgetIndex(childWidget);
+
+ VCaption caption = childToCaption.get(child);
+ if (VCaption.isNeeded(child.getState())) {
+ if (caption == null) {
+ caption = new VCaption(child, getConnection());
+ childToCaption.put(child, caption);
+ }
+ if (!caption.isAttached()) {
+ // Insert caption at widget index == before widget
+ cssLayoutWidgetContainer.insert(caption, widgetPosition);
+ }
+ caption.updateCaption();
+ } else if (caption != null) {
+ childToCaption.remove(child);
+ cssLayoutWidgetContainer.remove(caption);
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java
new file mode 100644
index 0000000000..7ba89d4c4c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.csslayout;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+
+public interface CssLayoutServerRpc extends LayoutClickRpc, ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutState.java
new file mode 100644
index 0000000000..07a8c1804a
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutState.java
@@ -0,0 +1,23 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.csslayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
+
+public class CssLayoutState extends AbstractLayoutState {
+ private Map<Connector, String> childCss = new HashMap<Connector, String>();
+
+ public Map<Connector, String> getChildCss() {
+ return childCss;
+ }
+
+ public void setChildCss(Map<Connector, String> childCss) {
+ this.childCss = childCss;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/VCssLayout.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/VCssLayout.java
new file mode 100644
index 0000000000..7076120388
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/VCssLayout.java
@@ -0,0 +1,72 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.csslayout;
+
+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.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.StyleConstants;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+
+public class VCssLayout extends SimplePanel {
+ public static final String TAGNAME = "csslayout";
+ public static final String CLASSNAME = "v-" + TAGNAME;
+
+ FlowPane panel = new FlowPane();
+
+ Element margin = DOM.createDiv();
+
+ public VCssLayout() {
+ super();
+ getElement().appendChild(margin);
+ setStyleName(CLASSNAME);
+ margin.setClassName(CLASSNAME + "-margin");
+ setWidget(panel);
+ }
+
+ @Override
+ protected Element getContainerElement() {
+ return margin;
+ }
+
+ public class FlowPane extends FlowPanel {
+
+ public FlowPane() {
+ super();
+ setStyleName(CLASSNAME + "-container");
+ }
+
+ void addOrMove(Widget child, int index) {
+ if (child.getParent() == this) {
+ int currentIndex = getWidgetIndex(child);
+ if (index == currentIndex) {
+ return;
+ }
+ }
+ insert(child, index);
+ }
+
+ }
+
+ /**
+ * Sets CSS classes for margin based on the given parameters.
+ *
+ * @param margins
+ * A {@link VMarginInfo} object that provides info on
+ * top/left/bottom/right margins
+ */
+ protected void setMarginStyles(VMarginInfo margins) {
+ setStyleName(margin, VCssLayout.CLASSNAME + "-"
+ + StyleConstants.MARGIN_TOP, margins.hasTop());
+ setStyleName(margin, VCssLayout.CLASSNAME + "-"
+ + StyleConstants.MARGIN_RIGHT, margins.hasRight());
+ setStyleName(margin, VCssLayout.CLASSNAME + "-"
+ + StyleConstants.MARGIN_BOTTOM, margins.hasBottom());
+ setStyleName(margin, VCssLayout.CLASSNAME + "-"
+ + StyleConstants.MARGIN_LEFT, margins.hasLeft());
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java
new file mode 100644
index 0000000000..a65187a461
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/CustomComponentConnector.java
@@ -0,0 +1,46 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.customcomponent;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.CustomComponent;
+
+@Connect(value = CustomComponent.class, loadStyle = LoadStyle.EAGER)
+public class CustomComponentConnector extends
+ AbstractComponentContainerConnector {
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VCustomComponent.class);
+ }
+
+ @Override
+ public VCustomComponent getWidget() {
+ return (VCustomComponent) super.getWidget();
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP, custom component dont render composition roots caption
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ ComponentConnector newChild = null;
+ if (getChildren().size() == 1) {
+ newChild = getChildren().get(0);
+ }
+
+ VCustomComponent customComponent = getWidget();
+ customComponent.setWidget(newChild.getWidget());
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customcomponent/VCustomComponent.java b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/VCustomComponent.java
new file mode 100644
index 0000000000..2b27bd0e58
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/customcomponent/VCustomComponent.java
@@ -0,0 +1,18 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.customcomponent;
+
+import com.google.gwt.user.client.ui.SimplePanel;
+
+public class VCustomComponent extends SimplePanel {
+
+ private static final String CLASSNAME = "v-customcomponent";
+
+ public VCustomComponent() {
+ super();
+ setStyleName(CLASSNAME);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customfield/CustomFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/customfield/CustomFieldConnector.java
new file mode 100644
index 0000000000..a2ba09da43
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/customfield/CustomFieldConnector.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.customfield;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.customcomponent.CustomComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.customcomponent.VCustomComponent;
+import com.vaadin.ui.CustomField;
+
+@Connect(value = CustomField.class)
+public class CustomFieldConnector extends CustomComponentConnector {
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VCustomComponent.class);
+ }
+
+ @Override
+ public VCustomComponent getWidget() {
+ return super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java
new file mode 100644
index 0000000000..5e8f01258f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutConnector.java
@@ -0,0 +1,118 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.customlayout;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.ui.CustomLayout;
+
+@Connect(CustomLayout.class)
+public class CustomLayoutConnector extends AbstractLayoutConnector implements
+ SimpleManagedLayout {
+
+ @Override
+ public CustomLayoutState getState() {
+ return (CustomLayoutState) super.getState();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ getWidget().client = getConnection();
+ getWidget().pid = getConnectorId();
+
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ // Evaluate scripts
+ VCustomLayout.eval(getWidget().scripts);
+ getWidget().scripts = null;
+
+ }
+
+ private void updateHtmlTemplate() {
+ if (getWidget().hasTemplate()) {
+ // We (currently) only do this once. You can't change the template
+ // later on.
+ return;
+ }
+ String templateName = getState().getTemplateName();
+ String templateContents = getState().getTemplateContents();
+
+ if (templateName != null) {
+ // Get the HTML-template from client. Overrides templateContents
+ // (even though both can never be given at the same time)
+ templateContents = getConnection().getResource(
+ "layouts/" + templateName + ".html");
+ if (templateContents == null) {
+ templateContents = "<em>Layout file layouts/"
+ + templateName
+ + ".html is missing. Components will be drawn for debug purposes.</em>";
+ }
+ }
+
+ getWidget().initializeHTML(templateContents,
+ getConnection().getThemeUri());
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ // Must do this once here so the HTML has been set up before we start
+ // adding child widgets.
+
+ updateHtmlTemplate();
+
+ // For all contained widgets
+ for (ComponentConnector child : getChildren()) {
+ String location = getState().getChildLocations().get(child);
+ try {
+ getWidget().setWidget(child.getWidget(), location);
+ } catch (final IllegalArgumentException e) {
+ // If no location is found, this component is not visible
+ }
+ }
+ for (ComponentConnector oldChild : event.getOldChildren()) {
+ if (oldChild.getParent() == this) {
+ // Connector still a child of this
+ continue;
+ }
+ Widget oldChildWidget = oldChild.getWidget();
+ if (oldChildWidget.isAttached()) {
+ // slot of this widget is emptied, remove it
+ getWidget().remove(oldChildWidget);
+ }
+ }
+
+ }
+
+ @Override
+ public VCustomLayout getWidget() {
+ return (VCustomLayout) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VCustomLayout.class);
+ }
+
+ public void updateCaption(ComponentConnector paintable) {
+ getWidget().updateCaption(paintable);
+ }
+
+ public void layout() {
+ getWidget().iLayoutJS(DOM.getFirstChild(getWidget().getElement()));
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutState.java
new file mode 100644
index 0000000000..6b374a8099
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/CustomLayoutState.java
@@ -0,0 +1,41 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.customlayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
+
+public class CustomLayoutState extends AbstractLayoutState {
+ Map<Connector, String> childLocations = new HashMap<Connector, String>();
+ private String templateContents;
+ private String templateName;
+
+ public String getTemplateContents() {
+ return templateContents;
+ }
+
+ public void setTemplateContents(String templateContents) {
+ this.templateContents = templateContents;
+ }
+
+ public String getTemplateName() {
+ return templateName;
+ }
+
+ public void setTemplateName(String templateName) {
+ this.templateName = templateName;
+ }
+
+ public Map<Connector, String> getChildLocations() {
+ return childLocations;
+ }
+
+ public void setChildLocations(Map<Connector, String> childLocations) {
+ this.childLocations = childLocations;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java
index f4aacf3ea2..b4194c40a6 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java
@@ -2,12 +2,10 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.customlayout;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.Set;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NodeList;
@@ -18,12 +16,7 @@ import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.ContainerResizedListener;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VCaption;
import com.vaadin.terminal.gwt.client.VCaptionWrapper;
@@ -34,8 +27,7 @@ import com.vaadin.terminal.gwt.client.VCaptionWrapper;
* @author Vaadin Ltd
*
*/
-public class VCustomLayout extends ComplexPanel implements Paintable,
- Container, ContainerResizedListener {
+public class VCustomLayout extends ComplexPanel {
public static final String CLASSNAME = "v-customlayout";
@@ -43,24 +35,23 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
private final HashMap<String, Element> locationToElement = new HashMap<String, Element>();
/** Location-name to contained widget map */
- private final HashMap<String, Widget> locationToWidget = new HashMap<String, Widget>();
+ final HashMap<String, Widget> locationToWidget = new HashMap<String, Widget>();
/** Widget to captionwrapper map */
- private final HashMap<Paintable, VCaptionWrapper> widgetToCaptionWrapper = new HashMap<Paintable, VCaptionWrapper>();
+ private final HashMap<Widget, VCaptionWrapper> childWidgetToCaptionWrapper = new HashMap<Widget, VCaptionWrapper>();
/** Name of the currently rendered style */
String currentTemplateName;
/** Unexecuted scripts loaded from the template */
- private String scripts = "";
+ String scripts = "";
/** Paintable ID of this paintable */
- private String pid;
+ String pid;
- private ApplicationConnection client;
+ ApplicationConnection client;
- /** Has the template been loaded from contents passed in UIDL **/
- private boolean hasTemplateContents = false;
+ private boolean htmlInitialized = false;
private Element elementWithNativeResizeFunction;
@@ -68,8 +59,6 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
private String width = "";
- private HashMap<String, FloatSize> locationToExtraSize = new HashMap<String, FloatSize>();
-
public VCustomLayout() {
setElement(DOM.createDiv());
// Clear any unwanted styling
@@ -131,94 +120,14 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
locationToWidget.put(location, widget);
}
- /** Update the layout from UIDL */
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- // ApplicationConnection manages generic component features
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- pid = uidl.getId();
- if (!hasTemplate()) {
- // Update HTML template only once
- initializeHTML(uidl, client);
- }
-
- // Evaluate scripts
- eval(scripts);
- scripts = null;
-
- iLayout();
- // TODO Check if this is needed
- client.runDescendentsLayout(this);
-
- Set<Widget> oldWidgets = new HashSet<Widget>();
- oldWidgets.addAll(locationToWidget.values());
-
- // For all contained widgets
- for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
- final UIDL uidlForChild = (UIDL) i.next();
- if (uidlForChild.getTag().equals("location")) {
- final String location = uidlForChild.getStringAttribute("name");
- final Paintable child = client.getPaintable(uidlForChild
- .getChildUIDL(0));
- try {
- setWidget((Widget) child, location);
- child.updateFromUIDL(uidlForChild.getChildUIDL(0), client);
- } catch (final IllegalArgumentException e) {
- // If no location is found, this component is not visible
- }
- oldWidgets.remove(child);
- }
- }
- for (Iterator<Widget> iterator = oldWidgets.iterator(); iterator
- .hasNext();) {
- Widget oldWidget = iterator.next();
- if (oldWidget.isAttached()) {
- // slot of this widget is emptied, remove it
- remove(oldWidget);
- }
- }
-
- iLayout();
- // TODO Check if this is needed
- client.runDescendentsLayout(this);
-
- }
-
/** Initialize HTML-layout. */
- private void initializeHTML(UIDL uidl, ApplicationConnection client) {
-
- final String newTemplateContents = uidl
- .getStringAttribute("templateContents");
- final String newTemplate = uidl.getStringAttribute("template");
-
- currentTemplateName = null;
- hasTemplateContents = false;
-
- String template = "";
- if (newTemplate != null) {
- // Get the HTML-template from client
- template = client.getResource("layouts/" + newTemplate + ".html");
- if (template == null) {
- template = "<em>Layout file layouts/"
- + newTemplate
- + ".html is missing. Components will be drawn for debug purposes.</em>";
- } else {
- currentTemplateName = newTemplate;
- }
- } else {
- hasTemplateContents = true;
- template = newTemplateContents;
- }
+ public void initializeHTML(String template, String themeUri) {
// Connect body of the template to DOM
template = extractBodyAndScriptsFromTemplate(template);
// TODO prefix img src:s here with a regeps, cannot work further with IE
- String themeUri = client.getThemeUri();
String relImgPrefix = themeUri + "/layouts/";
// prefix all relative image elements to point to theme dir with a
@@ -251,6 +160,7 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
}
publishResizedFunction(elementWithNativeResizeFunction);
+ htmlInitialized = true;
}
private native boolean uriEndsWithSlash()
@@ -261,12 +171,8 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
return false;
}-*/;
- private boolean hasTemplate() {
- if (currentTemplateName == null && !hasTemplateContents) {
- return false;
- } else {
- return true;
- }
+ boolean hasTemplate() {
+ return htmlInitialized;
}
/** Collect locations from template */
@@ -276,12 +182,6 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
if (!"".equals(location)) {
locationToElement.put(location, elem);
elem.setInnerHTML("");
- int x = Util.measureHorizontalPaddingAndBorder(elem, 0);
- int y = Util.measureVerticalPaddingAndBorder(elem, 0);
-
- FloatSize fs = new FloatSize(x, y);
-
- locationToExtraSize.put(location, fs);
} else {
final int len = DOM.getChildCount(elem);
@@ -292,7 +192,7 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
}
/** Evaluate given script in browser document */
- private static native void eval(String script)
+ static native void eval(String script)
/*-{
try {
if (script != null)
@@ -369,39 +269,27 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
return res;
}
- /** Replace child components */
- public void replaceChildComponent(Widget from, Widget to) {
- final String location = getLocation(from);
- if (location == null) {
- throw new IllegalArgumentException();
- }
- setWidget(to, location);
- }
-
- /** Does this layout contain given child */
- public boolean hasChildComponent(Widget component) {
- return locationToWidget.containsValue(component);
- }
-
/** Update caption for given widget */
- public void updateCaption(Paintable component, UIDL uidl) {
- VCaptionWrapper wrapper = widgetToCaptionWrapper.get(component);
- if (VCaption.isNeeded(uidl)) {
+ public void updateCaption(ComponentConnector paintable) {
+ Widget widget = paintable.getWidget();
+ VCaptionWrapper wrapper = childWidgetToCaptionWrapper.get(widget);
+ if (VCaption.isNeeded(paintable.getState())) {
if (wrapper == null) {
- final String loc = getLocation((Widget) component);
- super.remove((Widget) component);
- wrapper = new VCaptionWrapper(component, client);
+ // Add a wrapper between the layout and the child widget
+ final String loc = getLocation(widget);
+ super.remove(widget);
+ wrapper = new VCaptionWrapper(paintable, client);
super.add(wrapper, locationToElement.get(loc));
- widgetToCaptionWrapper.put(component, wrapper);
+ childWidgetToCaptionWrapper.put(widget, wrapper);
}
- wrapper.updateCaption(uidl);
+ wrapper.updateCaption();
} else {
if (wrapper != null) {
- final String loc = getLocation((Widget) component);
+ // Remove the wrapper and add the widget directly to the layout
+ final String loc = getLocation(widget);
super.remove(wrapper);
- super.add((Widget) wrapper.getPaintable(),
- locationToElement.get(loc));
- widgetToCaptionWrapper.remove(component);
+ super.add(widget, locationToElement.get(loc));
+ childWidgetToCaptionWrapper.remove(widget);
}
}
}
@@ -421,14 +309,13 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
/** Removes given widget from the layout */
@Override
public boolean remove(Widget w) {
- client.unregisterPaintable((Paintable) w);
final String location = getLocation(w);
if (location != null) {
locationToWidget.remove(location);
}
- final VCaptionWrapper cw = widgetToCaptionWrapper.get(w);
+ final VCaptionWrapper cw = childWidgetToCaptionWrapper.get(w);
if (cw != null) {
- widgetToCaptionWrapper.remove(w);
+ childWidgetToCaptionWrapper.remove(w);
return super.remove(cw);
} else if (w != null) {
return super.remove(w);
@@ -447,11 +334,7 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
public void clear() {
super.clear();
locationToWidget.clear();
- widgetToCaptionWrapper.clear();
- }
-
- public void iLayout() {
- iLayoutJS(DOM.getFirstChild(getElement()));
+ childWidgetToCaptionWrapper.clear();
}
/**
@@ -480,7 +363,7 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
/*-{
var self = this;
element.notifyChildrenOfSizeChange = $entry(function() {
- self.@com.vaadin.terminal.gwt.client.ui.VCustomLayout::notifyChildrenOfSizeChange()();
+ self.@com.vaadin.terminal.gwt.client.ui.customlayout.VCustomLayout::notifyChildrenOfSizeChange()();
});
}-*/;
@@ -499,7 +382,7 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
* @return true if layout function exists and was run successfully, else
* false.
*/
- private native boolean iLayoutJS(Element el)
+ native boolean iLayoutJS(Element el)
/*-{
if(el && el.iLayoutJS) {
try {
@@ -513,27 +396,6 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
}
}-*/;
- public boolean requestLayout(Set<Paintable> child) {
- updateRelativeSizedComponents(true, true);
-
- if (width.equals("") || height.equals("")) {
- /* Automatically propagated upwards if the size can change */
- return false;
- }
-
- return true;
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- com.google.gwt.dom.client.Element pe = child.getElement()
- .getParentElement();
-
- FloatSize extra = locationToExtraSize.get(getLocation(child));
- return new RenderSpace(pe.getOffsetWidth() - (int) extra.getWidth(),
- pe.getOffsetHeight() - (int) extra.getHeight(),
- Util.mayHaveScrollBars(pe));
- }
-
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -543,103 +405,4 @@ public class VCustomLayout extends ComplexPanel implements Paintable,
}
}
- @Override
- public void setHeight(String height) {
- if (this.height.equals(height)) {
- return;
- }
-
- boolean shrinking = true;
- if (isLarger(height, this.height)) {
- shrinking = false;
- }
-
- this.height = height;
- super.setHeight(height);
-
- /*
- * If the height shrinks we must remove all components with relative
- * height from the DOM, update their height when they do not affect the
- * available space and finally restore them to the original state
- */
- if (shrinking) {
- updateRelativeSizedComponents(false, true);
- }
- }
-
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
-
- boolean shrinking = true;
- if (isLarger(width, this.width)) {
- shrinking = false;
- }
-
- super.setWidth(width);
- this.width = width;
-
- /*
- * If the width shrinks we must remove all components with relative
- * width from the DOM, update their width when they do not affect the
- * available space and finally restore them to the original state
- */
- if (shrinking) {
- updateRelativeSizedComponents(true, false);
- }
- }
-
- private void updateRelativeSizedComponents(boolean relativeWidth,
- boolean relativeHeight) {
-
- Set<Widget> relativeSizeWidgets = new HashSet<Widget>();
-
- for (Widget widget : locationToWidget.values()) {
- FloatSize relativeSize = client.getRelativeSize(widget);
- if (relativeSize != null) {
- if ((relativeWidth && (relativeSize.getWidth() >= 0.0f))
- || (relativeHeight && (relativeSize.getHeight() >= 0.0f))) {
-
- relativeSizeWidgets.add(widget);
- widget.getElement().getStyle()
- .setProperty("position", "absolute");
- }
- }
- }
-
- for (Widget widget : relativeSizeWidgets) {
- client.handleComponentRelativeSize(widget);
- widget.getElement().getStyle().setProperty("position", "");
- }
- }
-
- /**
- * Compares newSize with currentSize and returns true if it is clear that
- * newSize is larger than currentSize. Returns false if newSize is smaller
- * or if it is unclear which one is smaller.
- *
- * @param newSize
- * @param currentSize
- * @return
- */
- private boolean isLarger(String newSize, String currentSize) {
- if (newSize.equals("") || currentSize.equals("")) {
- return false;
- }
-
- if (!newSize.endsWith("px") || !currentSize.endsWith("px")) {
- return false;
- }
-
- int newSizePx = Integer.parseInt(newSize.substring(0,
- newSize.length() - 2));
- int currentSizePx = Integer.parseInt(currentSize.substring(0,
- currentSize.length() - 2));
-
- boolean larger = newSizePx > currentSizePx;
- return larger;
- }
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/datefield/AbstractDateFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/AbstractDateFieldConnector.java
new file mode 100644
index 0000000000..e19d9b996f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/AbstractDateFieldConnector.java
@@ -0,0 +1,109 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.datefield;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.LocaleNotLoadedException;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+
+public class AbstractDateFieldConnector extends AbstractFieldConnector
+ implements Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // Save details
+ getWidget().client = client;
+ getWidget().paintableId = uidl.getId();
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().readonly = isReadOnly();
+ getWidget().enabled = isEnabled();
+
+ if (uidl.hasAttribute("locale")) {
+ final String locale = uidl.getStringAttribute("locale");
+ try {
+ getWidget().dts.setLocale(locale);
+ getWidget().currentLocale = locale;
+ } catch (final LocaleNotLoadedException e) {
+ getWidget().currentLocale = getWidget().dts.getLocale();
+ VConsole.error("Tried to use an unloaded locale \"" + locale
+ + "\". Using default locale ("
+ + getWidget().currentLocale + ").");
+ VConsole.error(e);
+ }
+ }
+
+ // We show week numbers only if the week starts with Monday, as ISO 8601
+ // specifies
+ getWidget().showISOWeekNumbers = uidl
+ .getBooleanAttribute(VDateField.WEEK_NUMBERS)
+ && getWidget().dts.getFirstDayOfWeek() == 1;
+
+ int newResolution;
+ if (uidl.hasVariable("sec")) {
+ newResolution = VDateField.RESOLUTION_SEC;
+ } else if (uidl.hasVariable("min")) {
+ newResolution = VDateField.RESOLUTION_MIN;
+ } else if (uidl.hasVariable("hour")) {
+ newResolution = VDateField.RESOLUTION_HOUR;
+ } else if (uidl.hasVariable("day")) {
+ newResolution = VDateField.RESOLUTION_DAY;
+ } else if (uidl.hasVariable("month")) {
+ newResolution = VDateField.RESOLUTION_MONTH;
+ } else {
+ newResolution = VDateField.RESOLUTION_YEAR;
+ }
+
+ getWidget().currentResolution = newResolution;
+
+ // Add stylename that indicates current resolution
+ getWidget()
+ .addStyleName(
+ VDateField.CLASSNAME
+ + "-"
+ + VDateField
+ .resolutionToString(getWidget().currentResolution));
+
+ final int year = uidl.getIntVariable("year");
+ final int month = (getWidget().currentResolution >= VDateField.RESOLUTION_MONTH) ? uidl
+ .getIntVariable("month") : -1;
+ final int day = (getWidget().currentResolution >= VDateField.RESOLUTION_DAY) ? uidl
+ .getIntVariable("day") : -1;
+ final int hour = (getWidget().currentResolution >= VDateField.RESOLUTION_HOUR) ? uidl
+ .getIntVariable("hour") : 0;
+ final int min = (getWidget().currentResolution >= VDateField.RESOLUTION_MIN) ? uidl
+ .getIntVariable("min") : 0;
+ final int sec = (getWidget().currentResolution >= VDateField.RESOLUTION_SEC) ? uidl
+ .getIntVariable("sec") : 0;
+
+ // Construct new date for this datefield (only if not null)
+ if (year > -1) {
+ getWidget().setCurrentDate(
+ new Date((long) getWidget().getTime(year, month, day, hour,
+ min, sec, 0)));
+ } else {
+ getWidget().setCurrentDate(null);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VDateField.class);
+ }
+
+ @Override
+ public VDateField getWidget() {
+ return (VDateField) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/datefield/InlineDateFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/InlineDateFieldConnector.java
new file mode 100644
index 0000000000..8a4840a088
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/InlineDateFieldConnector.java
@@ -0,0 +1,104 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.datefield;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.DateTimeService;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.FocusChangeListener;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.TimeChangeListener;
+import com.vaadin.ui.InlineDateField;
+
+@Connect(InlineDateField.class)
+public class InlineDateFieldConnector extends AbstractDateFieldConnector {
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ super.updateFromUIDL(uidl, client);
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().calendarPanel.setShowISOWeekNumbers(getWidget()
+ .isShowISOWeekNumbers());
+ getWidget().calendarPanel.setDateTimeService(getWidget()
+ .getDateTimeService());
+ getWidget().calendarPanel.setResolution(getWidget()
+ .getCurrentResolution());
+ Date currentDate = getWidget().getCurrentDate();
+ if (currentDate != null) {
+ getWidget().calendarPanel.setDate(new Date(currentDate.getTime()));
+ } else {
+ getWidget().calendarPanel.setDate(null);
+ }
+
+ if (getWidget().currentResolution > VDateField.RESOLUTION_DAY) {
+ getWidget().calendarPanel
+ .setTimeChangeListener(new TimeChangeListener() {
+ public void changed(int hour, int min, int sec, int msec) {
+ Date d = getWidget().getDate();
+ if (d == null) {
+ // date currently null, use the value from
+ // calendarPanel
+ // (~ client time at the init of the widget)
+ d = (Date) getWidget().calendarPanel.getDate()
+ .clone();
+ }
+ d.setHours(hour);
+ d.setMinutes(min);
+ d.setSeconds(sec);
+ DateTimeService.setMilliseconds(d, msec);
+
+ // Always update time changes to the server
+ getWidget().calendarPanel.setDate(d);
+ getWidget().updateValueFromPanel();
+ }
+ });
+ }
+
+ if (getWidget().currentResolution <= VDateField.RESOLUTION_MONTH) {
+ getWidget().calendarPanel
+ .setFocusChangeListener(new FocusChangeListener() {
+ public void focusChanged(Date date) {
+ Date date2 = new Date();
+ if (getWidget().calendarPanel.getDate() != null) {
+ date2.setTime(getWidget().calendarPanel
+ .getDate().getTime());
+ }
+ /*
+ * Update the value of calendarPanel
+ */
+ date2.setYear(date.getYear());
+ date2.setMonth(date.getMonth());
+ getWidget().calendarPanel.setDate(date2);
+ /*
+ * Then update the value from panel to server
+ */
+ getWidget().updateValueFromPanel();
+ }
+ });
+ } else {
+ getWidget().calendarPanel.setFocusChangeListener(null);
+ }
+
+ // Update possible changes
+ getWidget().calendarPanel.renderCalendar();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VDateFieldCalendar.class);
+ }
+
+ @Override
+ public VDateFieldCalendar getWidget() {
+ return (VDateFieldCalendar) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/datefield/PopupDateFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/PopupDateFieldConnector.java
new file mode 100644
index 0000000000..1bcb40d549
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/PopupDateFieldConnector.java
@@ -0,0 +1,124 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.datefield;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.DateTimeService;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.FocusChangeListener;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.TimeChangeListener;
+import com.vaadin.ui.DateField;
+
+@Connect(DateField.class)
+public class PopupDateFieldConnector extends TextualDateConnector {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.ui.VTextualDate#updateFromUIDL(com.vaadin
+ * .terminal.gwt.client.UIDL,
+ * com.vaadin.terminal.gwt.client.ApplicationConnection)
+ */
+ @Override
+ @SuppressWarnings("deprecation")
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ boolean lastReadOnlyState = getWidget().readonly;
+ boolean lastEnabledState = getWidget().isEnabled();
+
+ getWidget().parsable = uidl.getBooleanAttribute("parsable");
+
+ super.updateFromUIDL(uidl, client);
+
+ String popupStyleNames = getStyleNames(VPopupCalendar.POPUP_PRIMARY_STYLE_NAME);
+ popupStyleNames += " "
+ + VDateField.CLASSNAME
+ + "-"
+ + VPopupCalendar
+ .resolutionToString(getWidget().currentResolution);
+ getWidget().popup.setStyleName(popupStyleNames);
+
+ getWidget().calendar.setDateTimeService(getWidget()
+ .getDateTimeService());
+ getWidget().calendar.setShowISOWeekNumbers(getWidget()
+ .isShowISOWeekNumbers());
+ if (getWidget().calendar.getResolution() != getWidget().currentResolution) {
+ getWidget().calendar.setResolution(getWidget().currentResolution);
+ if (getWidget().calendar.getDate() != null) {
+ getWidget().calendar.setDate((Date) getWidget()
+ .getCurrentDate().clone());
+ // force re-render when changing resolution only
+ getWidget().calendar.renderCalendar();
+ }
+ }
+ getWidget().calendarToggle.setEnabled(getWidget().enabled);
+
+ if (getWidget().currentResolution <= VPopupCalendar.RESOLUTION_MONTH) {
+ getWidget().calendar
+ .setFocusChangeListener(new FocusChangeListener() {
+ public void focusChanged(Date date) {
+ getWidget().updateValue(date);
+ getWidget().buildDate();
+ Date date2 = getWidget().calendar.getDate();
+ date2.setYear(date.getYear());
+ date2.setMonth(date.getMonth());
+ }
+ });
+ } else {
+ getWidget().calendar.setFocusChangeListener(null);
+ }
+
+ if (getWidget().currentResolution > VPopupCalendar.RESOLUTION_DAY) {
+ getWidget().calendar
+ .setTimeChangeListener(new TimeChangeListener() {
+ public void changed(int hour, int min, int sec, int msec) {
+ Date d = getWidget().getDate();
+ if (d == null) {
+ // date currently null, use the value from
+ // calendarPanel
+ // (~ client time at the init of the widget)
+ d = (Date) getWidget().calendar.getDate()
+ .clone();
+ }
+ d.setHours(hour);
+ d.setMinutes(min);
+ d.setSeconds(sec);
+ DateTimeService.setMilliseconds(d, msec);
+
+ // Always update time changes to the server
+ getWidget().updateValue(d);
+
+ // Update text field
+ getWidget().buildDate();
+ }
+ });
+ }
+
+ if (getWidget().readonly) {
+ getWidget().calendarToggle.addStyleName(VPopupCalendar.CLASSNAME
+ + "-button-readonly");
+ } else {
+ getWidget().calendarToggle.removeStyleName(VPopupCalendar.CLASSNAME
+ + "-button-readonly");
+ }
+
+ getWidget().calendarToggle.setEnabled(true);
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VPopupCalendar.class);
+ }
+
+ @Override
+ public VPopupCalendar getWidget() {
+ return (VPopupCalendar) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/datefield/TextualDateConnector.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/TextualDateConnector.java
new file mode 100644
index 0000000000..90679f5705
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/TextualDateConnector.java
@@ -0,0 +1,56 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.datefield;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+
+public class TextualDateConnector extends AbstractDateFieldConnector {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ int origRes = getWidget().currentResolution;
+ String oldLocale = getWidget().currentLocale;
+ super.updateFromUIDL(uidl, client);
+ if (origRes != getWidget().currentResolution
+ || oldLocale != getWidget().currentLocale) {
+ // force recreating format string
+ getWidget().formatStr = null;
+ }
+ if (uidl.hasAttribute("format")) {
+ getWidget().formatStr = uidl.getStringAttribute("format");
+ }
+
+ getWidget().inputPrompt = uidl
+ .getStringAttribute(VTextualDate.ATTR_INPUTPROMPT);
+
+ getWidget().lenient = !uidl.getBooleanAttribute("strict");
+
+ getWidget().buildDate();
+ // not a FocusWidget -> needs own tabindex handling
+ if (uidl.hasAttribute("tabindex")) {
+ getWidget().text.setTabIndex(uidl.getIntAttribute("tabindex"));
+ }
+
+ if (getWidget().readonly) {
+ getWidget().text.addStyleDependentName("readonly");
+ } else {
+ getWidget().text.removeStyleDependentName("readonly");
+ }
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTextualDate.class);
+ }
+
+ @Override
+ public VTextualDate getWidget() {
+ return (VTextualDate) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VCalendarPanel.java
index 845ac837f6..acfff60d53 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VCalendarPanel.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.datefield;
import java.util.Date;
import java.util.Iterator;
@@ -40,6 +40,10 @@ import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.DateTimeService;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.FocusableFlexTable;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.label.VLabel;
+import com.vaadin.terminal.gwt.client.ui.nativeselect.VNativeSelect;
@SuppressWarnings("deprecation")
public class VCalendarPanel extends FocusableFlexTable implements
@@ -1139,7 +1143,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
// elapsed, another timer is triggered to go off every 150ms. Both
// timers are cancelled on mouseup or mouseout.
if (event.getSource() instanceof VEventButton) {
- final Widget sender = (Widget) event.getSource();
+ final VEventButton sender = (VEventButton) event.getSource();
processClickEvent(sender);
mouseTimer = new Timer() {
@Override
@@ -1228,8 +1232,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
private ListBox sec;
- private ListBox msec;
-
private ListBox ampm;
/**
@@ -1294,19 +1296,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
sec.addChangeHandler(this);
}
- if (getResolution() == VDateField.RESOLUTION_MSEC) {
- msec = createListBox();
- for (int i = 0; i < 1000; i++) {
- if (i < 10) {
- msec.addItem("00" + i);
- } else if (i < 100) {
- msec.addItem("0" + i);
- } else {
- msec.addItem("" + i);
- }
- }
- msec.addChangeHandler(this);
- }
final String delimiter = getDateTimeService().getClockDelimeter();
if (isReadonly()) {
@@ -1340,16 +1329,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
add(sec);
}
}
- if (getResolution() == VDateField.RESOLUTION_MSEC) {
- add(new VLabel("."));
- if (isReadonly()) {
- final int m = getMilliseconds();
- final String ms = m < 100 ? "0" + m : "" + m;
- add(new VLabel(m < 10 ? "0" + ms : ms));
- } else {
- add(msec);
- }
- }
if (getResolution() == VDateField.RESOLUTION_HOUR) {
add(new VLabel(delimiter + "00")); // o'clock
}
@@ -1425,13 +1404,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (getResolution() >= VDateField.RESOLUTION_SEC) {
sec.setSelectedIndex(value.getSeconds());
}
- if (getResolution() == VDateField.RESOLUTION_MSEC) {
- if (selected) {
- msec.setSelectedIndex(getMilliseconds());
- } else {
- msec.setSelectedIndex(0);
- }
- }
if (getDateTimeService().isTwelveHourClock()) {
ampm.setSelectedIndex(value.getHours() < 12 ? 0 : 1);
}
@@ -1443,9 +1415,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (sec != null) {
sec.setEnabled(isEnabled());
}
- if (msec != null) {
- msec.setEnabled(isEnabled());
- }
if (ampm != null) {
ampm.setEnabled(isEnabled());
}
@@ -1508,15 +1477,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
event.preventDefault();
event.stopPropagation();
- } else if (event.getSource() == msec) {
- final int ms = msec.getSelectedIndex();
- DateTimeService.setMilliseconds(value, ms);
- if (timeChangeListener != null) {
- timeChangeListener.changed(value.getHours(),
- value.getMinutes(), value.getSeconds(), ms);
- }
- event.preventDefault();
- event.stopPropagation();
} else if (event.getSource() == ampm) {
final int h = hours.getSelectedIndex()
+ (ampm.getSelectedIndex() * 12);
@@ -1699,8 +1659,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
return SUBPART_MINUTE_SELECT;
} else if (contains(time.sec, subElement)) {
return SUBPART_SECS_SELECT;
- } else if (contains(time.msec, subElement)) {
- return SUBPART_MSECS_SELECT;
} else if (contains(time.ampm, subElement)) {
return SUBPART_AMPM_SELECT;
@@ -1749,9 +1707,6 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (SUBPART_SECS_SELECT.equals(subPart)) {
return time.sec.getElement();
}
- if (SUBPART_MSECS_SELECT.equals(subPart)) {
- return time.msec.getElement();
- }
if (SUBPART_AMPM_SELECT.equals(subPart)) {
return time.ampm.getElement();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateField.java
index bc228675b2..d169b1b47e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateField.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.datefield;
import java.util.Date;
@@ -10,19 +10,16 @@ import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlowPanel;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.DateTimeService;
-import com.vaadin.terminal.gwt.client.LocaleNotLoadedException;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Field;
-public class VDateField extends FlowPanel implements Paintable, Field {
+public class VDateField extends FlowPanel implements Field {
public static final String CLASSNAME = "v-datefield";
- private String id;
+ protected String paintableId;
- private ApplicationConnection client;
+ protected ApplicationConnection client;
protected boolean immediate;
@@ -32,7 +29,6 @@ public class VDateField extends FlowPanel implements Paintable, Field {
public static final int RESOLUTION_HOUR = 8;
public static final int RESOLUTION_MIN = 16;
public static final int RESOLUTION_SEC = 32;
- public static final int RESOLUTION_MSEC = 64;
public static final String WEEK_NUMBERS = "wn";
@@ -65,7 +61,7 @@ public class VDateField extends FlowPanel implements Paintable, Field {
protected DateTimeService dts;
- private boolean showISOWeekNumbers = false;
+ protected boolean showISOWeekNumbers = false;
public VDateField() {
setStyleName(CLASSNAME);
@@ -81,88 +77,11 @@ public class VDateField extends FlowPanel implements Paintable, Field {
}
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Ensure correct implementation and let layout manage caption
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- // Save details
- this.client = client;
- id = uidl.getId();
- immediate = uidl.getBooleanAttribute("immediate");
-
- readonly = uidl.getBooleanAttribute("readonly");
- enabled = !uidl.getBooleanAttribute("disabled");
-
- if (uidl.hasAttribute("locale")) {
- final String locale = uidl.getStringAttribute("locale");
- try {
- dts.setLocale(locale);
- currentLocale = locale;
- } catch (final LocaleNotLoadedException e) {
- currentLocale = dts.getLocale();
- VConsole.error("Tried to use an unloaded locale \"" + locale
- + "\". Using default locale (" + currentLocale + ").");
- VConsole.error(e);
- }
- }
-
- // We show week numbers only if the week starts with Monday, as ISO 8601
- // specifies
- showISOWeekNumbers = uidl.getBooleanAttribute(WEEK_NUMBERS)
- && dts.getFirstDayOfWeek() == 1;
-
- int newResolution;
- if (uidl.hasVariable("msec")) {
- newResolution = RESOLUTION_MSEC;
- } else if (uidl.hasVariable("sec")) {
- newResolution = RESOLUTION_SEC;
- } else if (uidl.hasVariable("min")) {
- newResolution = RESOLUTION_MIN;
- } else if (uidl.hasVariable("hour")) {
- newResolution = RESOLUTION_HOUR;
- } else if (uidl.hasVariable("day")) {
- newResolution = RESOLUTION_DAY;
- } else if (uidl.hasVariable("month")) {
- newResolution = RESOLUTION_MONTH;
- } else {
- newResolution = RESOLUTION_YEAR;
- }
-
- currentResolution = newResolution;
-
- // Add stylename that indicates current resolution
- addStyleName(CLASSNAME + "-" + resolutionToString(currentResolution));
-
- final int year = uidl.getIntVariable("year");
- final int month = (currentResolution >= RESOLUTION_MONTH) ? uidl
- .getIntVariable("month") : -1;
- final int day = (currentResolution >= RESOLUTION_DAY) ? uidl
- .getIntVariable("day") : -1;
- final int hour = (currentResolution >= RESOLUTION_HOUR) ? uidl
- .getIntVariable("hour") : 0;
- final int min = (currentResolution >= RESOLUTION_MIN) ? uidl
- .getIntVariable("min") : 0;
- final int sec = (currentResolution >= RESOLUTION_SEC) ? uidl
- .getIntVariable("sec") : 0;
- final int msec = (currentResolution >= RESOLUTION_MSEC) ? uidl
- .getIntVariable("msec") : 0;
-
- // Construct new date for this datefield (only if not null)
- if (year > -1) {
- setCurrentDate(new Date((long) getTime(year, month, day, hour, min,
- sec, msec)));
- } else {
- setCurrentDate(null);
- }
- }
-
/*
* We need this redundant native function because Java's Date object doesn't
* have a setMilliseconds method.
*/
- private static native double getTime(int y, int m, int d, int h, int mi,
+ protected static native double getTime(int y, int m, int d, int h, int mi,
int s, int ms)
/*-{
try {
@@ -231,7 +150,7 @@ public class VDateField extends FlowPanel implements Paintable, Field {
}
public String getId() {
- return id;
+ return paintableId;
}
public ApplicationConnection getClient() {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java
index 91388edcaf..21e0e0820d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java
@@ -2,25 +2,21 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.datefield;
import java.util.Date;
import com.google.gwt.event.dom.client.DomEvent;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.DateTimeService;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusChangeListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusOutListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.SubmitListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.TimeChangeListener;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.FocusOutListener;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.SubmitListener;
/**
* A client side implementation for InlineDateField
*/
public class VDateFieldCalendar extends VDateField {
- private final VCalendarPanel calendarPanel;
+ protected final VCalendarPanel calendarPanel;
public VDateFieldCalendar() {
super();
@@ -44,73 +40,11 @@ public class VDateFieldCalendar extends VDateField {
});
}
- @Override
- @SuppressWarnings("deprecation")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- super.updateFromUIDL(uidl, client);
- calendarPanel.setShowISOWeekNumbers(isShowISOWeekNumbers());
- calendarPanel.setDateTimeService(getDateTimeService());
- calendarPanel.setResolution(getCurrentResolution());
- Date currentDate = getCurrentDate();
- if (currentDate != null) {
- calendarPanel.setDate(new Date(currentDate.getTime()));
- } else {
- calendarPanel.setDate(null);
- }
-
- if (currentResolution > RESOLUTION_DAY) {
- calendarPanel.setTimeChangeListener(new TimeChangeListener() {
- public void changed(int hour, int min, int sec, int msec) {
- Date d = getDate();
- if (d == null) {
- // date currently null, use the value from calendarPanel
- // (~ client time at the init of the widget)
- d = (Date) calendarPanel.getDate().clone();
- }
- d.setHours(hour);
- d.setMinutes(min);
- d.setSeconds(sec);
- DateTimeService.setMilliseconds(d, msec);
-
- // Always update time changes to the server
- calendarPanel.setDate(d);
- updateValueFromPanel();
- }
- });
- }
-
- if (currentResolution <= RESOLUTION_MONTH) {
- calendarPanel.setFocusChangeListener(new FocusChangeListener() {
- public void focusChanged(Date date) {
- Date date2 = new Date();
- if (calendarPanel.getDate() != null) {
- date2.setTime(calendarPanel.getDate().getTime());
- }
- /*
- * Update the value of calendarPanel
- */
- date2.setYear(date.getYear());
- date2.setMonth(date.getMonth());
- calendarPanel.setDate(date2);
- /*
- * Then update the value from panel to server
- */
- updateValueFromPanel();
- }
- });
- } else {
- calendarPanel.setFocusChangeListener(null);
- }
-
- // Update possible changes
- calendarPanel.renderCalendar();
- }
-
/**
* TODO refactor: almost same method as in VPopupCalendar.updateValue
*/
@SuppressWarnings("deprecation")
- private void updateValueFromPanel() {
+ protected void updateValueFromPanel() {
Date date2 = calendarPanel.getDate();
Date currentDate = getCurrentDate();
if (currentDate == null || date2.getTime() != currentDate.getTime()) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VPopupCalendar.java
index 8bf2f6bfbf..7011e5358b 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VPopupCalendar.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.datefield;
import java.util.Date;
@@ -21,16 +21,13 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.DateTimeService;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusChangeListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusOutListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.SubmitListener;
-import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.TimeChangeListener;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.FocusOutListener;
+import com.vaadin.terminal.gwt.client.ui.datefield.VCalendarPanel.SubmitListener;
/**
* Represents a date selection component with a text field and a popup date
@@ -42,19 +39,19 @@ import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.TimeChangeListener;
* <code>setCalendarPanel(VCalendarPanel panel)</code> method.
*
*/
-public class VPopupCalendar extends VTextualDate implements Paintable, Field,
+public class VPopupCalendar extends VTextualDate implements Field,
ClickHandler, CloseHandler<PopupPanel>, SubPartAware {
- private static final String POPUP_PRIMARY_STYLE_NAME = VDateField.CLASSNAME
+ protected static final String POPUP_PRIMARY_STYLE_NAME = VDateField.CLASSNAME
+ "-popup";
- private final Button calendarToggle;
+ protected final Button calendarToggle;
- private VCalendarPanel calendar;
+ protected VCalendarPanel calendar;
- private final VOverlay popup;
+ protected final VOverlay popup;
private boolean open = false;
- private boolean parsable = true;
+ protected boolean parsable = true;
public VPopupCalendar() {
super();
@@ -105,7 +102,7 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field,
}
@SuppressWarnings("deprecation")
- private void updateValue(Date newDate) {
+ protected void updateValue(Date newDate) {
Date currentDate = getCurrentDate();
if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
setCurrentDate((Date) newDate.clone());
@@ -126,14 +123,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field,
if (getCurrentResolution() > RESOLUTION_MIN) {
getClient().updateVariable(getId(), "sec",
newDate.getSeconds(), false);
- if (getCurrentResolution() == RESOLUTION_MSEC) {
- getClient().updateVariable(
- getId(),
- "msec",
- DateTimeService
- .getMilliseconds(newDate),
- false);
- }
}
}
}
@@ -149,96 +138,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field,
* (non-Javadoc)
*
* @see
- * com.vaadin.terminal.gwt.client.ui.VTextualDate#updateFromUIDL(com.vaadin
- * .terminal.gwt.client.UIDL,
- * com.vaadin.terminal.gwt.client.ApplicationConnection)
- */
- @Override
- @SuppressWarnings("deprecation")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- boolean lastReadOnlyState = readonly;
- boolean lastEnabledState = isEnabled();
-
- parsable = uidl.getBooleanAttribute("parsable");
-
- super.updateFromUIDL(uidl, client);
-
- String popupStyleNames = ApplicationConnection.getStyleName(
- POPUP_PRIMARY_STYLE_NAME, uidl, false);
- popupStyleNames += " " + VDateField.CLASSNAME + "-"
- + resolutionToString(currentResolution);
- popup.setStyleName(popupStyleNames);
-
- calendar.setDateTimeService(getDateTimeService());
- calendar.setShowISOWeekNumbers(isShowISOWeekNumbers());
- if (calendar.getResolution() != currentResolution) {
- calendar.setResolution(currentResolution);
- if (calendar.getDate() != null) {
- calendar.setDate((Date) getCurrentDate().clone());
- // force re-render when changing resolution only
- calendar.renderCalendar();
- }
- }
- calendarToggle.setEnabled(enabled);
-
- if (currentResolution <= RESOLUTION_MONTH) {
- calendar.setFocusChangeListener(new FocusChangeListener() {
- public void focusChanged(Date date) {
- updateValue(date);
- buildDate();
- Date date2 = calendar.getDate();
- date2.setYear(date.getYear());
- date2.setMonth(date.getMonth());
- }
- });
- } else {
- calendar.setFocusChangeListener(null);
- }
-
- if (currentResolution > RESOLUTION_DAY) {
- calendar.setTimeChangeListener(new TimeChangeListener() {
- public void changed(int hour, int min, int sec, int msec) {
- Date d = getDate();
- if (d == null) {
- // date currently null, use the value from calendarPanel
- // (~ client time at the init of the widget)
- d = (Date) calendar.getDate().clone();
- }
- d.setHours(hour);
- d.setMinutes(min);
- d.setSeconds(sec);
- DateTimeService.setMilliseconds(d, msec);
-
- // Always update time changes to the server
- updateValue(d);
-
- // Update text field
- buildDate();
- }
- });
- }
-
- if (readonly) {
- calendarToggle.addStyleName(CLASSNAME + "-button-readonly");
- } else {
- calendarToggle.removeStyleName(CLASSNAME + "-button-readonly");
- }
-
- if (lastReadOnlyState != readonly || lastEnabledState != isEnabled()) {
- // Enabled or readonly state changed. Differences in theming might
- // affect the width (for instance if the popup button is hidden) so
- // we have to recalculate the width (IF the width of the field is
- // fixed)
- updateWidth();
- }
-
- calendarToggle.setEnabled(true);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
* com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
*/
@Override
@@ -276,7 +175,7 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field,
int l = calendarToggle.getAbsoluteLeft();
// Add a little extra space to the right to avoid
- // problems with IE6/IE7 scrollbars and to make it look
+ // problems with IE7 scrollbars and to make it look
// nicer.
int extraSpace = 30;
@@ -381,20 +280,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field,
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.gwt.client.ui.VTextualDate#getFieldExtraWidth()
- */
- @Override
- protected int getFieldExtraWidth() {
- if (fieldExtraWidth < 0) {
- fieldExtraWidth = super.getFieldExtraWidth();
- fieldExtraWidth += calendarToggle.getOffsetWidth();
- }
- return fieldExtraWidth;
- }
-
- /*
- * (non-Javadoc)
- *
* @see com.vaadin.terminal.gwt.client.ui.VTextualDate#buildDate()
*/
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VTextualDate.java
index 56cdf05ddb..db4eca152a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VTextualDate.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.datefield;
import java.util.Date;
@@ -12,41 +12,32 @@ 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.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.TextBox;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.ContainerResizedListener;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.Focusable;
import com.vaadin.terminal.gwt.client.LocaleNotLoadedException;
import com.vaadin.terminal.gwt.client.LocaleService;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
-public class VTextualDate extends VDateField implements Paintable, Field,
- ChangeHandler, ContainerResizedListener, Focusable, SubPartAware {
+public class VTextualDate extends VDateField implements Field, ChangeHandler,
+ Focusable, SubPartAware {
private static final String PARSE_ERROR_CLASSNAME = CLASSNAME
+ "-parseerror";
- private final TextBox text;
+ protected final TextBox text;
- private String formatStr;
+ protected String formatStr;
- private String width;
-
- private boolean needLayout;
-
- protected int fieldExtraWidth = -1;
-
- private boolean lenient;
+ protected boolean lenient;
private static final String CLASSNAME_PROMPT = "prompt";
- private static final String ATTR_INPUTPROMPT = "prompt";
- private String inputPrompt = "";
+ protected static final String ATTR_INPUTPROMPT = "prompt";
+ protected String inputPrompt = "";
private boolean prompting = false;
public VTextualDate() {
@@ -94,37 +85,6 @@ public class VTextualDate extends VDateField implements Paintable, Field,
add(text);
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- int origRes = currentResolution;
- String oldLocale = currentLocale;
- super.updateFromUIDL(uidl, client);
- if (origRes != currentResolution || oldLocale != currentLocale) {
- // force recreating format string
- formatStr = null;
- }
- if (uidl.hasAttribute("format")) {
- formatStr = uidl.getStringAttribute("format");
- }
-
- inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT);
-
- lenient = !uidl.getBooleanAttribute("strict");
-
- buildDate();
- // not a FocusWidget -> needs own tabindex handling
- if (uidl.hasAttribute("tabindex")) {
- text.setTabIndex(uidl.getIntAttribute("tabindex"));
- }
-
- if (readonly) {
- text.addStyleDependentName("readonly");
- } else {
- text.removeStyleDependentName("readonly");
- }
-
- }
-
protected String getFormatString() {
if (formatStr == null) {
if (currentResolution == RESOLUTION_YEAR) {
@@ -148,9 +108,6 @@ public class VTextualDate extends VDateField implements Paintable, Field,
frmString += ":mm";
if (currentResolution >= RESOLUTION_SEC) {
frmString += ":ss";
- if (currentResolution >= RESOLUTION_MSEC) {
- frmString += ".SSS";
- }
}
}
if (dts.isTwelveHourClock()) {
@@ -300,10 +257,6 @@ public class VTextualDate extends VDateField implements Paintable, Field,
currentResolution == VDateField.RESOLUTION_SEC
&& immediate);
}
- if (currentResolution == VDateField.RESOLUTION_MSEC) {
- getClient().updateVariable(getId(), "msec",
- currentDate != null ? getMilliseconds() : -1, immediate);
- }
}
@@ -338,83 +291,6 @@ public class VTextualDate extends VDateField implements Paintable, Field,
return format.trim();
}
- @Override
- public void setWidth(String newWidth) {
- if (!"".equals(newWidth) && (isUndefinedWidth() || !newWidth.equals(width))) {
- if (BrowserInfo.get().isIE6()) {
- // in IE6 cols ~ min-width
- DOM.setElementProperty(text.getElement(), "size", "1");
- }
- needLayout = true;
- width = newWidth;
- super.setWidth(width);
- iLayout();
- if (newWidth.indexOf("%") < 0) {
- needLayout = false;
- }
- } else {
- if ("".equals(newWidth) && !isUndefinedWidth()) {
- // Changing from defined to undefined
- if (BrowserInfo.get().isIE6()) {
- // revert IE6 hack
- DOM.setElementProperty(text.getElement(), "size", "");
- }
- super.setWidth("");
- iLayout(true);
- width = null;
- }
- }
- }
- protected boolean isUndefinedWidth() {
- return width == null || "".equals(width);
- }
-
- /**
- * Returns pixels in x-axis reserved for other than textfield content.
- *
- * @return extra width in pixels
- */
- protected int getFieldExtraWidth() {
- if (fieldExtraWidth < 0) {
- text.setWidth("0");
- fieldExtraWidth = text.getOffsetWidth();
- if (BrowserInfo.get().isFF3()) {
- // Firefox somehow always leaves the INPUT element 2px wide
- fieldExtraWidth -= 2;
- }
- }
- return fieldExtraWidth;
- }
-
- /**
- * Force an recalculation of the width of the component IF the width has
- * been defined. Does nothing if width is undefined as the width will be
- * automatically adjusted by the browser.
- */
- public void updateWidth() {
- if (isUndefinedWidth()) {
- return;
- }
- needLayout = true;
- fieldExtraWidth = -1;
- iLayout(true);
- }
-
- public void iLayout() {
- iLayout(false);
- }
-
- public void iLayout(boolean force) {
- if (needLayout || force) {
- int textFieldWidth = getOffsetWidth() - getFieldExtraWidth();
- if (textFieldWidth < 0) {
- // Field can never be smaller than 0 (causes exception in IE)
- textFieldWidth = 0;
- }
- text.setWidth(textFieldWidth + "px");
- }
- }
-
public void focus() {
text.setFocus(true);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VAbstractDropHandler.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VAbstractDropHandler.java
index 6280031d84..ce47c7d13a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VAbstractDropHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VAbstractDropHandler.java
@@ -9,7 +9,7 @@ import com.google.gwt.user.client.Command;
import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
public abstract class VAbstractDropHandler implements VDropHandler {
@@ -129,6 +129,6 @@ public abstract class VAbstractDropHandler implements VDropHandler {
* side counterpart of the Paintable is expected to implement
* {@link DropTarget} interface.
*/
- public abstract Paintable getPaintable();
+ public abstract ComponentConnector getConnector();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java
index 74ab1dcc47..2f404a3028 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragAndDropManager.java
@@ -23,8 +23,9 @@ import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.ValueMap;
@@ -226,7 +227,7 @@ public class VDragAndDropManager {
ENTER, LEAVE, OVER, DROP
}
- private static final String DD_SERVICE = "DD";
+ public static final String DD_SERVICE = "DD";
private static VDragAndDropManager instance;
private HandlerRegistration handlerRegistration;
@@ -337,10 +338,10 @@ public class VDragAndDropManager {
}
private void addActiveDragSourceStyleName() {
- Paintable dragSource = currentDrag.getTransferable()
+ ComponentConnector dragSource = currentDrag.getTransferable()
.getDragSource();
- ((Widget) dragSource)
- .addStyleName(ACTIVE_DRAG_SOURCE_STYLENAME);
+ dragSource.getWidget().addStyleName(
+ ACTIVE_DRAG_SOURCE_STYLENAME);
}
};
@@ -503,8 +504,8 @@ public class VDragAndDropManager {
* handled. E.g. hidden on start, removed in drophandler ->
* would flicker in case removed eagerly.
*/
- final Paintable dragSource = currentDrag.getTransferable()
- .getDragSource();
+ final ComponentConnector dragSource = currentDrag
+ .getTransferable().getDragSource();
final ApplicationConnection client = currentDropHandler
.getApplicationConnection();
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
@@ -547,8 +548,8 @@ public class VDragAndDropManager {
}
- private void removeActiveDragSourceStyleName(Paintable dragSource) {
- ((Widget) dragSource).removeStyleName(ACTIVE_DRAG_SOURCE_STYLENAME);
+ private void removeActiveDragSourceStyleName(ComponentConnector dragSource) {
+ dragSource.getWidget().removeStyleName(ACTIVE_DRAG_SOURCE_STYLENAME);
}
private void clearDragElement() {
@@ -582,7 +583,7 @@ public class VDragAndDropManager {
if (currentDropHandler == null) {
return;
}
- Paintable paintable = currentDropHandler.getPaintable();
+ ComponentConnector paintable = currentDropHandler.getConnector();
ApplicationConnection client = currentDropHandler
.getApplicationConnection();
/*
@@ -610,8 +611,9 @@ public class VDragAndDropManager {
if (currentDrag.getCurrentGwtEvent() != null) {
try {
- MouseEventDetails mouseEventDetails = new MouseEventDetails(
- currentDrag.getCurrentGwtEvent());
+ MouseEventDetails mouseEventDetails = MouseEventDetailsBuilder
+ .buildMouseEventDetails(currentDrag
+ .getCurrentGwtEvent());
currentDrag.getDropDetails().put("mouseEvent",
mouseEventDetails.serialize());
} catch (Exception e) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
index 1278ed7fdb..aabbf58b24 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VDragSourceIs.java
@@ -3,7 +3,8 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
/**
@@ -16,14 +17,17 @@ final public class VDragSourceIs extends VAcceptCriterion {
@Override
protected boolean accept(VDragEvent drag, UIDL configuration) {
try {
- Paintable component = drag.getTransferable().getDragSource();
+ ComponentConnector component = drag.getTransferable()
+ .getDragSource();
int c = configuration.getIntAttribute("c");
for (int i = 0; i < c; i++) {
String requiredPid = configuration
.getStringAttribute("component" + i);
- Paintable paintable = VDragAndDropManager.get()
- .getCurrentDropHandler().getApplicationConnection()
- .getPaintable(requiredPid);
+ VDropHandler currentDropHandler = VDragAndDropManager.get()
+ .getCurrentDropHandler();
+ ComponentConnector paintable = (ComponentConnector) ConnectorMap
+ .get(currentDropHandler.getApplicationConnection())
+ .getConnector(requiredPid);
if (paintable == component) {
return true;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VDropHandler.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VDropHandler.java
index a597e9ed30..92bd6abe61 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VDropHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VDropHandler.java
@@ -4,7 +4,7 @@
package com.vaadin.terminal.gwt.client.ui.dd;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
/**
* Vaadin Widgets that want to receive something via drag and drop implement
@@ -59,9 +59,9 @@ public interface VDropHandler {
public void dragOver(VDragEvent currentDrag);
/**
- * Returns the Paintable into which this DragHandler is associated
+ * Returns the ComponentConnector with which this DropHandler is associated
*/
- public Paintable getPaintable();
+ public ComponentConnector getConnector();
/**
* Returns the application connection to which this {@link VDropHandler}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VHasDropHandler.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VHasDropHandler.java
index 0195862431..6d6f7c776d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VHasDropHandler.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VHasDropHandler.java
@@ -3,13 +3,13 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
/**
* Used to detect Widget from widget tree that has {@link #getDropHandler()}
*
* Decide whether to get rid of this class. If so, {@link VAbstractDropHandler}
- * must extend {@link Paintable}.
+ * must extend {@link ComponentConnector}.
*
*/
public interface VHasDropHandler {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
index 58dc0d3956..90e2b033c9 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VIsOverId.java
@@ -6,7 +6,8 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.UIDL;
final public class VIsOverId extends VAcceptCriterion {
@@ -16,10 +17,14 @@ final public class VIsOverId extends VAcceptCriterion {
try {
String pid = configuration.getStringAttribute("s");
- Paintable paintable = VDragAndDropManager.get()
- .getCurrentDropHandler().getPaintable();
- String pid2 = VDragAndDropManager.get().getCurrentDropHandler()
- .getApplicationConnection().getPid(paintable);
+ VDropHandler currentDropHandler = VDragAndDropManager.get()
+ .getCurrentDropHandler();
+ ComponentConnector dropHandlerConnector = currentDropHandler
+ .getConnector();
+ ConnectorMap paintableMap = ConnectorMap.get(currentDropHandler
+ .getApplicationConnection());
+
+ String pid2 = dropHandlerConnector.getConnectorId();
if (pid2.equals(pid)) {
Object searchedId = drag.getDropDetails().get("itemIdOver");
String[] stringArrayAttribute = configuration
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
index ada7a3c78a..eb55c1a91c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VItemIdIs.java
@@ -6,7 +6,7 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
final public class VItemIdIs extends VAcceptCriterion {
@@ -15,9 +15,11 @@ final public class VItemIdIs extends VAcceptCriterion {
protected boolean accept(VDragEvent drag, UIDL configuration) {
try {
String pid = configuration.getStringAttribute("s");
- Paintable dragSource = drag.getTransferable().getDragSource();
- String pid2 = VDragAndDropManager.get().getCurrentDropHandler()
- .getApplicationConnection().getPid(dragSource);
+ ComponentConnector dragSource = drag.getTransferable()
+ .getDragSource();
+ VDropHandler currentDropHandler = VDragAndDropManager.get()
+ .getCurrentDropHandler();
+ String pid2 = dragSource.getConnectorId();
if (pid2.equals(pid)) {
Object searchedId = drag.getTransferable().getData("itemId");
String[] stringArrayAttribute = configuration
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
index ff4c4f1d35..430b422b34 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VSourceIsTarget.java
@@ -6,16 +6,16 @@
*/
package com.vaadin.terminal.gwt.client.ui.dd;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
final public class VSourceIsTarget extends VAcceptCriterion {
@Override
protected boolean accept(VDragEvent drag, UIDL configuration) {
- Paintable dragSource = drag.getTransferable().getDragSource();
- Paintable paintable = VDragAndDropManager.get().getCurrentDropHandler()
- .getPaintable();
+ ComponentConnector dragSource = drag.getTransferable().getDragSource();
+ ComponentConnector paintable = VDragAndDropManager.get()
+ .getCurrentDropHandler().getConnector();
return paintable == dragSource;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
index e1f2e34063..f69fa85290 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VTargetInSubtree.java
@@ -8,8 +8,8 @@ package com.vaadin.terminal.gwt.client.ui.dd;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.ui.VTree;
-import com.vaadin.terminal.gwt.client.ui.VTree.TreeNode;
+import com.vaadin.terminal.gwt.client.ui.tree.VTree;
+import com.vaadin.terminal.gwt.client.ui.tree.VTree.TreeNode;
final public class VTargetInSubtree extends VAcceptCriterion {
@@ -17,7 +17,7 @@ final public class VTargetInSubtree extends VAcceptCriterion {
protected boolean accept(VDragEvent drag, UIDL configuration) {
VTree tree = (VTree) VDragAndDropManager.get().getCurrentDropHandler()
- .getPaintable();
+ .getConnector();
TreeNode treeNode = tree.getNodeByKey((String) drag.getDropDetails()
.get("itemIdOver"));
if (treeNode != null) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
index fe51ea82e4..f87da378d7 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VTransferable.java
@@ -8,7 +8,7 @@ import java.util.HashMap;
import java.util.Map;
import com.vaadin.event.dd.DragSource;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
/**
* Client side counterpart for Transferable in com.vaadin.event.Transferable
@@ -16,7 +16,7 @@ import com.vaadin.terminal.gwt.client.Paintable;
*/
public class VTransferable {
- private Paintable component;
+ private ComponentConnector component;
private final Map<String, Object> variables = new HashMap<String, Object>();
@@ -26,7 +26,7 @@ public class VTransferable {
*
* @return the component
*/
- public Paintable getDragSource() {
+ public ComponentConnector getDragSource() {
return component;
}
@@ -41,7 +41,7 @@ public class VTransferable {
* @param component
* the component to set
*/
- public void setDragSource(Paintable component) {
+ public void setDragSource(ComponentConnector component) {
this.component = component;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
new file mode 100644
index 0000000000..db531ac70f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
@@ -0,0 +1,75 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.draganddropwrapper;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.customcomponent.CustomComponentConnector;
+import com.vaadin.ui.DragAndDropWrapper;
+
+@Connect(DragAndDropWrapper.class)
+public class DragAndDropWrapperConnector extends CustomComponentConnector
+ implements Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().client = client;
+ if (isRealUpdate(uidl) && !uidl.hasAttribute("hidden")) {
+ UIDL acceptCrit = uidl.getChildByTagName("-ac");
+ if (acceptCrit == null) {
+ getWidget().dropHandler = null;
+ } else {
+ if (getWidget().dropHandler == null) {
+ getWidget().dropHandler = getWidget().new CustomDropHandler();
+ }
+ getWidget().dropHandler.updateAcceptRules(acceptCrit);
+ }
+
+ Set<String> variableNames = uidl.getVariableNames();
+ for (String fileId : variableNames) {
+ if (fileId.startsWith("rec-")) {
+ String receiverUrl = uidl.getStringVariable(fileId);
+ fileId = fileId.substring(4);
+ if (getWidget().fileIdToReceiver == null) {
+ getWidget().fileIdToReceiver = new HashMap<String, String>();
+ }
+ if ("".equals(receiverUrl)) {
+ Integer id = Integer.parseInt(fileId);
+ int indexOf = getWidget().fileIds.indexOf(id);
+ if (indexOf != -1) {
+ getWidget().files.remove(indexOf);
+ getWidget().fileIds.remove(indexOf);
+ }
+ } else {
+ getWidget().fileIdToReceiver.put(fileId, receiverUrl);
+ }
+ }
+ }
+ getWidget().startNextUpload();
+
+ getWidget().dragStartMode = uidl
+ .getIntAttribute(VDragAndDropWrapper.DRAG_START_MODE);
+ getWidget().initDragStartMode();
+ getWidget().html5DataFlavors = uidl
+ .getMapAttribute(VDragAndDropWrapper.HTML5_DATA_FLAVORS);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VDragAndDropWrapper.class);
+ }
+
+ @Override
+ public VDragAndDropWrapper getWidget() {
+ return (VDragAndDropWrapper) super.getWidget();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
index ff649ebeb1..d09b81e1e1 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
@@ -1,13 +1,11 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.draganddropwrapper;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArrayString;
@@ -25,15 +23,15 @@ import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
import com.vaadin.terminal.gwt.client.ValueMap;
+import com.vaadin.terminal.gwt.client.ui.customcomponent.VCustomComponent;
import com.vaadin.terminal.gwt.client.ui.dd.DDUtil;
import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation;
import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler;
@@ -109,28 +107,23 @@ public class VDragAndDropWrapper extends VCustomComponent implements
private boolean startDrag(NativeEvent event) {
if (dragStartMode == WRAPPER || dragStartMode == COMPONENT) {
VTransferable transferable = new VTransferable();
- transferable.setDragSource(VDragAndDropWrapper.this);
-
- Paintable paintable;
- Widget w = Util.findWidget((Element) event.getEventTarget().cast(),
- null);
- while (w != null && !(w instanceof Paintable)) {
- w = w.getParent();
- }
- paintable = (Paintable) w;
+ transferable.setDragSource(ConnectorMap.get(client).getConnector(
+ VDragAndDropWrapper.this));
+ ComponentConnector paintable = Util.findPaintable(client,
+ (Element) event.getEventTarget().cast());
+ Widget widget = paintable.getWidget();
transferable.setData("component", paintable);
VDragEvent dragEvent = VDragAndDropManager.get().startDrag(
transferable, event, true);
- transferable.setData("mouseDown",
- new MouseEventDetails(event).serialize());
+ transferable.setData("mouseDown", MouseEventDetailsBuilder
+ .buildMouseEventDetails(event).serialize());
if (dragStartMode == WRAPPER) {
dragEvent.createDragImage(getElement(), true);
} else {
- dragEvent.createDragImage(((Widget) paintable).getElement(),
- true);
+ dragEvent.createDragImage(widget.getElement(), true);
}
return true;
}
@@ -144,58 +137,15 @@ public class VDragAndDropWrapper extends VCustomComponent implements
protected int dragStartMode;
- private ApplicationConnection client;
- private VAbstractDropHandler dropHandler;
+ ApplicationConnection client;
+ VAbstractDropHandler dropHandler;
private VDragEvent vaadinDragEvent;
- private int filecounter = 0;
- private Map<String, String> fileIdToReceiver;
- private ValueMap html5DataFlavors;
+ int filecounter = 0;
+ Map<String, String> fileIdToReceiver;
+ ValueMap html5DataFlavors;
private Element dragStartElement;
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- super.updateFromUIDL(uidl, client);
- if (!uidl.hasAttribute("cached") && !uidl.hasAttribute("hidden")) {
- UIDL acceptCrit = uidl.getChildByTagName("-ac");
- if (acceptCrit == null) {
- dropHandler = null;
- } else {
- if (dropHandler == null) {
- dropHandler = new CustomDropHandler();
- }
- dropHandler.updateAcceptRules(acceptCrit);
- }
-
- Set<String> variableNames = uidl.getVariableNames();
- for (String fileId : variableNames) {
- if (fileId.startsWith("rec-")) {
- String receiverUrl = uidl.getStringVariable(fileId);
- fileId = fileId.substring(4);
- if (fileIdToReceiver == null) {
- fileIdToReceiver = new HashMap<String, String>();
- }
- if ("".equals(receiverUrl)) {
- Integer id = Integer.parseInt(fileId);
- int indexOf = fileIds.indexOf(id);
- if (indexOf != -1) {
- files.remove(indexOf);
- fileIds.remove(indexOf);
- }
- } else {
- fileIdToReceiver.put(fileId, receiverUrl);
- }
- }
- }
- startNextUpload();
-
- dragStartMode = uidl.getIntAttribute(DRAG_START_MODE);
- initDragStartMode();
- html5DataFlavors = uidl.getMapAttribute(HTML5_DATA_FLAVORS);
- }
- }
-
protected void initDragStartMode() {
Element div = getElement();
if (dragStartMode == HTML5) {
@@ -235,7 +185,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
};
private Timer dragleavetimer;
- private void startNextUpload() {
+ void startNextUpload() {
Scheduler.get().scheduleDeferred(new Command() {
public void execute() {
@@ -291,7 +241,8 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
if (VDragAndDropManager.get().getCurrentDropHandler() != getDropHandler()) {
VTransferable transferable = new VTransferable();
- transferable.setDragSource(this);
+ transferable.setDragSource(ConnectorMap.get(client)
+ .getConnector(this));
vaadinDragEvent = VDragAndDropManager.get().startDrag(
transferable, event, false);
@@ -459,18 +410,14 @@ public class VDragAndDropWrapper extends VCustomComponent implements
* @param fileId
* @param data
*/
- private List<Integer> fileIds = new ArrayList<Integer>();
- private List<VHtml5File> files = new ArrayList<VHtml5File>();
+ List<Integer> fileIds = new ArrayList<Integer>();
+ List<VHtml5File> files = new ArrayList<VHtml5File>();
private void queueFilePost(final int fileId, final VHtml5File file) {
fileIds.add(fileId);
files.add(file);
}
- private String getPid() {
- return client.getPid(this);
- }
-
public VDropHandler getDropHandler() {
return dropHandler;
}
@@ -547,8 +494,9 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
@Override
- public Paintable getPaintable() {
- return VDragAndDropWrapper.this;
+ public ComponentConnector getConnector() {
+ return ConnectorMap.get(client).getConnector(
+ VDragAndDropWrapper.this);
}
public ApplicationConnection getApplicationConnection() {
@@ -561,7 +509,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
/*-{
var me = this;
el.addEventListener("dragstart", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}), false);
}-*/;
@@ -575,19 +523,19 @@ public class VDragAndDropWrapper extends VCustomComponent implements
var me = this;
el.addEventListener("dragenter", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}), false);
el.addEventListener("dragleave", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}), false);
el.addEventListener("dragover", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}), false);
el.addEventListener("drop", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}), false);
}-*/;
@@ -610,11 +558,6 @@ public class VDragAndDropWrapper extends VCustomComponent implements
}
protected void deEmphasis(boolean doLayout) {
- Size size = null;
- if (doLayout) {
- size = new RenderInformation.Size(getOffsetWidth(),
- getOffsetHeight());
- }
if (emphasizedVDrop != null) {
VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE, false);
VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
@@ -623,13 +566,16 @@ public class VDragAndDropWrapper extends VCustomComponent implements
+ emphasizedHDrop.toString().toLowerCase(), false);
}
if (doLayout) {
- handleVaadinRelatedSizeChange(size);
+ notifySizePotentiallyChanged();
}
}
+ private void notifySizePotentiallyChanged() {
+ LayoutManager.get(client).setNeedsMeasure(
+ ConnectorMap.get(client).getConnector(getElement()));
+ }
+
protected void emphasis(VDragEvent drag) {
- Size size = new RenderInformation.Size(getOffsetWidth(),
- getOffsetHeight());
deEmphasis(false);
VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE, true);
VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
@@ -641,20 +587,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
// TODO build (to be an example) an emphasis mode where drag image
// is fitted before or after the content
- handleVaadinRelatedSizeChange(size);
-
- }
-
- protected void handleVaadinRelatedSizeChange(Size originalSize) {
- if (isDynamicHeight() || isDynamicWidth()) {
- if (!originalSize.equals(new RenderInformation.Size(
- getOffsetWidth(), getOffsetHeight()))) {
- Util.notifyParentOfSizeChange(VDragAndDropWrapper.this, false);
- }
- }
- client.handleComponentRelativeSize(VDragAndDropWrapper.this);
- Util.notifyParentOfSizeChange(this, false);
-
+ notifySizePotentiallyChanged();
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java
index 30483545e9..bb511524e5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapperIE.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java
@@ -1,8 +1,8 @@
-/*
+/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.draganddropwrapper;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Document;
@@ -40,7 +40,7 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
var me = this;
el.attachEvent("ondragstart", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}));
}-*/;
@@ -50,19 +50,19 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
var me = this;
el.attachEvent("ondragenter", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}));
el.attachEvent("ondragleave", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}));
el.attachEvent("ondragover", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}));
el.attachEvent("ondrop", $entry(function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
+ return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
}));
}-*/;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java
new file mode 100644
index 0000000000..81ac195c8e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java
@@ -0,0 +1,205 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.embedded;
+
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+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.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.Embedded;
+
+@Connect(Embedded.class)
+public class EmbeddedConnector extends AbstractComponentConnector implements
+ Paintable {
+
+ public static final String ALTERNATE_TEXT = "alt";
+
+ EmbeddedServerRpc rpc;
+
+ @Override
+ protected void init() {
+ super.init();
+ rpc = RpcProxy.create(EmbeddedServerRpc.class, this);
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // Save details
+ getWidget().client = client;
+
+ boolean clearBrowserElement = true;
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ if (uidl.hasAttribute("type")) {
+ getWidget().type = uidl.getStringAttribute("type");
+ if (getWidget().type.equals("image")) {
+ getWidget().addStyleName(VEmbedded.CLASSNAME + "-image");
+ Element el = null;
+ boolean created = false;
+ NodeList<Node> nodes = getWidget().getElement().getChildNodes();
+ if (nodes != null && nodes.getLength() == 1) {
+ Node n = nodes.getItem(0);
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ Element e = (Element) n;
+ if (e.getTagName().equals("IMG")) {
+ el = e;
+ }
+ }
+ }
+ if (el == null) {
+ getWidget().setHTML("");
+ el = DOM.createImg();
+ created = true;
+ DOM.sinkEvents(el, Event.ONLOAD);
+ }
+
+ // Set attributes
+ Style style = el.getStyle();
+ style.setProperty("width", getState().getWidth());
+ style.setProperty("height", getState().getHeight());
+
+ DOM.setElementProperty(el, "src",
+ getWidget().getSrc(uidl, client));
+
+ if (uidl.hasAttribute(ALTERNATE_TEXT)) {
+ el.setPropertyString(ALTERNATE_TEXT,
+ uidl.getStringAttribute(ALTERNATE_TEXT));
+ }
+
+ if (created) {
+ // insert in dom late
+ getWidget().getElement().appendChild(el);
+ }
+
+ /*
+ * Sink tooltip events so tooltip is displayed when hovering the
+ * image.
+ */
+ getWidget().sinkEvents(VTooltip.TOOLTIP_EVENTS);
+
+ } else if (getWidget().type.equals("browser")) {
+ getWidget().addStyleName(VEmbedded.CLASSNAME + "-browser");
+ if (getWidget().browserElement == null) {
+ getWidget().setHTML(
+ "<iframe width=\"100%\" height=\"100%\" frameborder=\"0\""
+ + " allowTransparency=\"true\" src=\"\""
+ + " name=\"" + uidl.getId()
+ + "\"></iframe>");
+ getWidget().browserElement = DOM.getFirstChild(getWidget()
+ .getElement());
+ }
+ DOM.setElementAttribute(getWidget().browserElement, "src",
+ getWidget().getSrc(uidl, client));
+ clearBrowserElement = false;
+ } else {
+ VConsole.log("Unknown Embedded type '" + getWidget().type + "'");
+ }
+ } else if (uidl.hasAttribute("mimetype")) {
+ final String mime = uidl.getStringAttribute("mimetype");
+ if (mime.equals("application/x-shockwave-flash")) {
+ // Handle embedding of Flash
+ getWidget().addStyleName(VEmbedded.CLASSNAME + "-flash");
+ getWidget().setHTML(getWidget().createFlashEmbed(uidl));
+
+ } else if (mime.equals("image/svg+xml")) {
+ getWidget().addStyleName(VEmbedded.CLASSNAME + "-svg");
+ String data;
+ Map<String, String> parameters = VEmbedded.getParameters(uidl);
+ if (parameters.get("data") == null) {
+ data = getWidget().getSrc(uidl, client);
+ } else {
+ data = "data:image/svg+xml," + parameters.get("data");
+ }
+ getWidget().setHTML("");
+ ObjectElement obj = Document.get().createObjectElement();
+ obj.setType(mime);
+ obj.setData(data);
+ if (!isUndefinedWidth()) {
+ obj.getStyle().setProperty("width", "100%");
+ }
+ if (!isUndefinedHeight()) {
+ obj.getStyle().setProperty("height", "100%");
+ }
+ if (uidl.hasAttribute("classid")) {
+ obj.setAttribute("classid",
+ uidl.getStringAttribute("classid"));
+ }
+ if (uidl.hasAttribute("codebase")) {
+ obj.setAttribute("codebase",
+ uidl.getStringAttribute("codebase"));
+ }
+ if (uidl.hasAttribute("codetype")) {
+ obj.setAttribute("codetype",
+ uidl.getStringAttribute("codetype"));
+ }
+ if (uidl.hasAttribute("archive")) {
+ obj.setAttribute("archive",
+ uidl.getStringAttribute("archive"));
+ }
+ if (uidl.hasAttribute("standby")) {
+ obj.setAttribute("standby",
+ uidl.getStringAttribute("standby"));
+ }
+ getWidget().getElement().appendChild(obj);
+ if (uidl.hasAttribute(ALTERNATE_TEXT)) {
+ obj.setInnerText(uidl.getStringAttribute(ALTERNATE_TEXT));
+ }
+ } else {
+ VConsole.log("Unknown Embedded mimetype '" + mime + "'");
+ }
+ } else {
+ VConsole.log("Unknown Embedded; no type or mimetype attribute");
+ }
+
+ if (clearBrowserElement) {
+ getWidget().browserElement = null;
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VEmbedded.class);
+ }
+
+ @Override
+ public VEmbedded getWidget() {
+ return (VEmbedded) super.getWidget();
+ }
+
+ protected final ClickEventHandler clickEventHandler = new ClickEventHandler(
+ this) {
+
+ @Override
+ protected void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails) {
+ rpc.click(mouseDetails);
+ }
+
+ };
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java
new file mode 100644
index 0000000000..7f36c812bc
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java
@@ -0,0 +1,10 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.embedded;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.ClickRpc;
+
+public interface EmbeddedServerRpc extends ClickRpc, ServerRpc {
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/embedded/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/embedded/VEmbedded.java
new file mode 100644
index 0000000000..203e7362f3
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/embedded/VEmbedded.java
@@ -0,0 +1,239 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.embedded;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+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.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+
+public class VEmbedded extends HTML {
+ public static String CLASSNAME = "v-embedded";
+
+ protected Element browserElement;
+
+ protected String type;
+
+ protected ApplicationConnection client;
+
+ public VEmbedded() {
+ setStyleName(CLASSNAME);
+ }
+
+ /**
+ * Creates the Object and Embed tags for the Flash plugin so it works
+ * cross-browser
+ *
+ * @param uidl
+ * The UIDL
+ * @return Tags concatenated into a string
+ */
+ protected String createFlashEmbed(UIDL uidl) {
+ /*
+ * To ensure cross-browser compatibility we are using the twice-cooked
+ * method to embed flash i.e. we add a OBJECT tag for IE ActiveX and
+ * inside it a EMBED for all other browsers.
+ */
+
+ StringBuilder html = new StringBuilder();
+
+ // Start the object tag
+ html.append("<object ");
+
+ /*
+ * Add classid required for ActiveX to recognize the flash. This is a
+ * predefined value which ActiveX recognizes and must be the given
+ * value. More info can be found on
+ * http://kb2.adobe.com/cps/415/tn_4150.html. Allow user to override
+ * this by setting his own classid.
+ */
+ if (uidl.hasAttribute("classid")) {
+ html.append("classid=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("classid"))
+ + "\" ");
+ } else {
+ html.append("classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" ");
+ }
+
+ /*
+ * Add codebase required for ActiveX and must be exactly this according
+ * to http://kb2.adobe.com/cps/415/tn_4150.html to work with the above
+ * given classid. Again, see more info on
+ * http://kb2.adobe.com/cps/415/tn_4150.html. Limiting Flash version to
+ * 6.0.0.0 and above. Allow user to override this by setting his own
+ * codebase
+ */
+ if (uidl.hasAttribute("codebase")) {
+ html.append("codebase=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("codebase"))
+ + "\" ");
+ } else {
+ html.append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ");
+ }
+
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ String height = paintable.getState().getHeight();
+ String width = paintable.getState().getWidth();
+
+ // Add width and height
+ html.append("width=\"" + Util.escapeAttribute(width) + "\" ");
+ html.append("height=\"" + Util.escapeAttribute(height) + "\" ");
+ html.append("type=\"application/x-shockwave-flash\" ");
+
+ // Codetype
+ if (uidl.hasAttribute("codetype")) {
+ html.append("codetype=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("codetype"))
+ + "\" ");
+ }
+
+ // Standby
+ if (uidl.hasAttribute("standby")) {
+ html.append("standby=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("standby"))
+ + "\" ");
+ }
+
+ // Archive
+ if (uidl.hasAttribute("archive")) {
+ html.append("archive=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("archive"))
+ + "\" ");
+ }
+
+ // End object tag
+ html.append(">");
+
+ // Ensure we have an movie parameter
+ Map<String, String> parameters = getParameters(uidl);
+ if (parameters.get("movie") == null) {
+ parameters.put("movie", getSrc(uidl, client));
+ }
+
+ // Add parameters to OBJECT
+ for (String name : parameters.keySet()) {
+ html.append("<param ");
+ html.append("name=\"" + Util.escapeAttribute(name) + "\" ");
+ html.append("value=\"" + Util.escapeAttribute(parameters.get(name))
+ + "\" ");
+ html.append("/>");
+ }
+
+ // Build inner EMBED tag
+ html.append("<embed ");
+ html.append("src=\"" + Util.escapeAttribute(getSrc(uidl, client))
+ + "\" ");
+ html.append("width=\"" + Util.escapeAttribute(width) + "\" ");
+ html.append("height=\"" + Util.escapeAttribute(height) + "\" ");
+ html.append("type=\"application/x-shockwave-flash\" ");
+
+ // Add the parameters to the Embed
+ for (String name : parameters.keySet()) {
+ html.append(Util.escapeAttribute(name));
+ html.append("=");
+ html.append("\"" + Util.escapeAttribute(parameters.get(name))
+ + "\"");
+ }
+
+ // End embed tag
+ html.append("></embed>");
+
+ if (uidl.hasAttribute(EmbeddedConnector.ALTERNATE_TEXT)) {
+ html.append(uidl
+ .getStringAttribute(EmbeddedConnector.ALTERNATE_TEXT));
+ }
+
+ // End object tag
+ html.append("</object>");
+
+ return html.toString();
+ }
+
+ /**
+ * Returns a map (name -> value) of all parameters in the UIDL.
+ *
+ * @param uidl
+ * @return
+ */
+ protected static Map<String, String> getParameters(UIDL uidl) {
+ Map<String, String> parameters = new HashMap<String, String>();
+
+ Iterator<Object> childIterator = uidl.getChildIterator();
+ while (childIterator.hasNext()) {
+
+ Object child = childIterator.next();
+ if (child instanceof UIDL) {
+
+ UIDL childUIDL = (UIDL) child;
+ if (childUIDL.getTag().equals("embeddedparam")) {
+ String name = childUIDL.getStringAttribute("name");
+ String value = childUIDL.getStringAttribute("value");
+ parameters.put(name, value);
+ }
+ }
+
+ }
+
+ return parameters;
+ }
+
+ /**
+ * Helper to return translated src-attribute from embedded's UIDL
+ *
+ * @param uidl
+ * @param client
+ * @return
+ */
+ protected String getSrc(UIDL uidl, ApplicationConnection client) {
+ String url = client.translateVaadinUri(uidl.getStringAttribute("src"));
+ if (url == null) {
+ return "";
+ }
+ return url;
+ }
+
+ @Override
+ protected void onDetach() {
+ if (BrowserInfo.get().isIE()) {
+ // Force browser to fire unload event when component is detached
+ // from the view (IE doesn't do this automatically)
+ if (browserElement != null) {
+ /*
+ * src was previously set to javascript:false, but this was not
+ * enough to overcome a bug when detaching an iframe with a pdf
+ * loaded in IE9. about:blank seems to cause the adobe reader
+ * plugin to unload properly before the iframe is removed. See
+ * #7855
+ */
+ DOM.setElementAttribute(browserElement, "src", "about:blank");
+ }
+ }
+ super.onDetach();
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ if (DOM.eventGetType(event) == Event.ONLOAD) {
+ VConsole.log("Embeddable onload");
+ Util.notifyParentOfSizeChange(this, true);
+ }
+
+ client.handleTooltipEvent(event, this);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java b/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java
new file mode 100644
index 0000000000..82cbc95b2d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java
@@ -0,0 +1,209 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.form;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+import com.vaadin.ui.Form;
+
+@Connect(Form.class)
+public class FormConnector extends AbstractComponentContainerConnector
+ implements Paintable, MayScrollChildren {
+
+ private final ElementResizeListener footerResizeListener = new ElementResizeListener() {
+ public void onElementResize(ElementResizeEvent e) {
+ VForm form = getWidget();
+
+ int footerHeight;
+ if (form.footer != null) {
+ LayoutManager lm = getLayoutManager();
+ footerHeight = lm.getOuterHeight(form.footer.getElement());
+ } else {
+ footerHeight = 0;
+ }
+
+ form.fieldContainer.getStyle().setPaddingBottom(footerHeight,
+ Unit.PX);
+ form.footerContainer.getStyle()
+ .setMarginTop(-footerHeight, Unit.PX);
+ }
+ };
+
+ @Override
+ public void onUnregister() {
+ VForm form = getWidget();
+ if (form.footer != null) {
+ getLayoutManager().removeElementResizeListener(
+ form.footer.getElement(), footerResizeListener);
+ }
+ }
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().client = client;
+ getWidget().id = uidl.getId();
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ boolean legendEmpty = true;
+ if (getState().getCaption() != null) {
+ getWidget().caption.setInnerText(getState().getCaption());
+ legendEmpty = false;
+ } else {
+ getWidget().caption.setInnerText("");
+ }
+ if (getState().getIcon() != null) {
+ if (getWidget().icon == null) {
+ getWidget().icon = new Icon(client);
+ getWidget().legend.insertFirst(getWidget().icon.getElement());
+ }
+ getWidget().icon.setUri(getState().getIcon().getURL());
+ legendEmpty = false;
+ } else {
+ if (getWidget().icon != null) {
+ getWidget().legend.removeChild(getWidget().icon.getElement());
+ }
+ }
+ if (legendEmpty) {
+ getWidget().addStyleDependentName("nocaption");
+ } else {
+ getWidget().removeStyleDependentName("nocaption");
+ }
+
+ if (null != getState().getErrorMessage()) {
+ getWidget().errorMessage
+ .updateMessage(getState().getErrorMessage());
+ getWidget().errorMessage.setVisible(true);
+ } else {
+ getWidget().errorMessage.setVisible(false);
+ }
+
+ if (getState().hasDescription()) {
+ getWidget().desc.setInnerHTML(getState().getDescription());
+ if (getWidget().desc.getParentElement() == null) {
+ getWidget().fieldSet.insertAfter(getWidget().desc,
+ getWidget().legend);
+ }
+ } else {
+ getWidget().desc.setInnerHTML("");
+ if (getWidget().desc.getParentElement() != null) {
+ getWidget().fieldSet.removeChild(getWidget().desc);
+ }
+ }
+
+ // first render footer so it will be easier to handle relative height of
+ // main layout
+ if (getState().getFooter() != null) {
+ // render footer
+ ComponentConnector newFooter = (ComponentConnector) getState()
+ .getFooter();
+ Widget newFooterWidget = newFooter.getWidget();
+ if (getWidget().footer == null) {
+ getLayoutManager().addElementResizeListener(
+ newFooterWidget.getElement(), footerResizeListener);
+ getWidget().add(newFooter.getWidget(),
+ getWidget().footerContainer);
+ getWidget().footer = newFooterWidget;
+ } else if (newFooter != getWidget().footer) {
+ getLayoutManager().removeElementResizeListener(
+ getWidget().footer.getElement(), footerResizeListener);
+ getLayoutManager().addElementResizeListener(
+ newFooterWidget.getElement(), footerResizeListener);
+ getWidget().remove(getWidget().footer);
+ getWidget().add(newFooter.getWidget(),
+ getWidget().footerContainer);
+ }
+ getWidget().footer = newFooterWidget;
+ } else {
+ if (getWidget().footer != null) {
+ getLayoutManager().removeElementResizeListener(
+ getWidget().footer.getElement(), footerResizeListener);
+ getWidget().remove(getWidget().footer);
+ getWidget().footer = null;
+ }
+ }
+
+ ComponentConnector newLayout = (ComponentConnector) getState()
+ .getLayout();
+ Widget newLayoutWidget = newLayout.getWidget();
+ if (getWidget().lo == null) {
+ // Layout not rendered before
+ getWidget().lo = newLayoutWidget;
+ getWidget().add(newLayoutWidget, getWidget().fieldContainer);
+ } else if (getWidget().lo != newLayoutWidget) {
+ // Layout has changed
+ getWidget().remove(getWidget().lo);
+ getWidget().lo = newLayoutWidget;
+ getWidget().add(newLayoutWidget, getWidget().fieldContainer);
+ }
+
+ // also recalculates size of the footer if undefined size form - see
+ // #3710
+ client.runDescendentsLayout(getWidget());
+
+ // We may have actions attached
+ if (uidl.getChildCount() >= 1) {
+ UIDL childUidl = uidl.getChildByTagName("actions");
+ if (childUidl != null) {
+ if (getWidget().shortcutHandler == null) {
+ getWidget().shortcutHandler = new ShortcutActionHandler(
+ getConnectorId(), client);
+ getWidget().keyDownRegistration = getWidget()
+ .addDomHandler(getWidget(), KeyDownEvent.getType());
+ }
+ getWidget().shortcutHandler.updateActionMap(childUidl);
+ }
+ } else if (getWidget().shortcutHandler != null) {
+ getWidget().keyDownRegistration.removeHandler();
+ getWidget().shortcutHandler = null;
+ getWidget().keyDownRegistration = null;
+ }
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP form don't render caption for neither field layout nor footer
+ // layout
+ }
+
+ @Override
+ public VForm getWidget() {
+ return (VForm) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VForm.class);
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly() || getState().isPropertyReadOnly();
+ }
+
+ @Override
+ public FormState getState() {
+ return (FormState) super.getState();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/form/FormState.java b/src/com/vaadin/terminal/gwt/client/ui/form/FormState.java
new file mode 100644
index 0000000000..c1acc0971d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/form/FormState.java
@@ -0,0 +1,29 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.form;
+
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
+import com.vaadin.terminal.gwt.client.Connector;
+
+public class FormState extends AbstractFieldState {
+ private Connector layout;
+ private Connector footer;
+
+ public Connector getLayout() {
+ return layout;
+ }
+
+ public void setLayout(Connector layout) {
+ this.layout = layout;
+ }
+
+ public Connector getFooter() {
+ return footer;
+ }
+
+ public void setFooter(Connector footer) {
+ this.footer = footer;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java b/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java
new file mode 100644
index 0000000000..e3a0c9b321
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/form/VForm.java
@@ -0,0 +1,80 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.form;
+
+import com.google.gwt.dom.client.Style.Display;
+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;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.VErrorMessage;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+
+public class VForm extends ComplexPanel implements KeyDownHandler {
+
+ protected String id;
+
+ public static final String CLASSNAME = "v-form";
+
+ Widget lo;
+ Element legend = DOM.createLegend();
+ Element caption = DOM.createSpan();
+ private Element errorIndicatorElement = DOM.createDiv();
+ Element desc = DOM.createDiv();
+ Icon icon;
+ VErrorMessage errorMessage = new VErrorMessage();
+
+ Element fieldContainer = DOM.createDiv();
+
+ Element footerContainer = DOM.createDiv();
+
+ Element fieldSet = DOM.createFieldSet();
+
+ Widget footer;
+
+ ApplicationConnection client;
+
+ ShortcutActionHandler shortcutHandler;
+
+ HandlerRegistration keyDownRegistration;
+
+ public VForm() {
+ setElement(DOM.createDiv());
+ getElement().appendChild(fieldSet);
+ setStyleName(CLASSNAME);
+ fieldSet.appendChild(legend);
+ legend.appendChild(caption);
+ errorIndicatorElement.setClassName("v-errorindicator");
+ errorIndicatorElement.getStyle().setDisplay(Display.NONE);
+ errorIndicatorElement.setInnerText(" "); // needed for IE
+ desc.setClassName("v-form-description");
+ fieldSet.appendChild(desc); // Adding description for initial padding
+ // measurements, removed later if no
+ // description is set
+ fieldContainer.setClassName(CLASSNAME + "-content");
+ fieldSet.appendChild(fieldContainer);
+ errorMessage.setVisible(false);
+ errorMessage.setStyleName(CLASSNAME + "-errormessage");
+ fieldSet.appendChild(errorMessage.getElement());
+ fieldSet.appendChild(footerContainer);
+ }
+
+ public void onKeyDown(KeyDownEvent event) {
+ shortcutHandler.handleKeyboardEvent(Event.as(event.getNativeEvent()));
+ }
+
+ @Override
+ protected void add(Widget child, Element container) {
+ // Overridden to allow VFormPaintable to call this. Should be removed
+ // once functionality from VFormPaintable is moved to VForm.
+ super.add(child, container);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java
new file mode 100644
index 0000000000..d7774b66ef
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/formlayout/FormLayoutConnector.java
@@ -0,0 +1,105 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.formlayout;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.formlayout.VFormLayout.Caption;
+import com.vaadin.terminal.gwt.client.ui.formlayout.VFormLayout.ErrorFlag;
+import com.vaadin.terminal.gwt.client.ui.formlayout.VFormLayout.VFormLayoutTable;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutState;
+import com.vaadin.ui.FormLayout;
+
+@Connect(FormLayout.class)
+public class FormLayoutConnector extends AbstractLayoutConnector {
+
+ @Override
+ public AbstractOrderedLayoutState getState() {
+ return (AbstractOrderedLayoutState) super.getState();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ VFormLayoutTable formLayoutTable = getWidget().table;
+
+ formLayoutTable.setMargins(new VMarginInfo(getState()
+ .getMarginsBitmask()));
+ formLayoutTable.setSpacing(getState().isSpacing());
+
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ VFormLayout formLayout = getWidget();
+ VFormLayoutTable formLayoutTable = getWidget().table;
+
+ int childId = 0;
+
+ formLayoutTable.setRowCount(getChildren().size());
+
+ for (ComponentConnector child : getChildren()) {
+ Widget childWidget = child.getWidget();
+
+ Caption caption = formLayoutTable.getCaption(childWidget);
+ if (caption == null) {
+ caption = formLayout.new Caption(child);
+ caption.addClickHandler(formLayoutTable);
+ }
+
+ ErrorFlag error = formLayoutTable.getError(childWidget);
+ if (error == null) {
+ error = formLayout.new ErrorFlag(child);
+ }
+
+ formLayoutTable.setChild(childId, childWidget, caption, error);
+ childId++;
+ }
+
+ for (ComponentConnector oldChild : event.getOldChildren()) {
+ if (oldChild.getParent() == this) {
+ continue;
+ }
+
+ formLayoutTable.cleanReferences(oldChild.getWidget());
+ }
+
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ getWidget().table.updateCaption(component.getWidget(),
+ component.getState(), component.isEnabled());
+ boolean hideErrors = false;
+
+ // FIXME This incorrectly depends on AbstractFieldConnector
+ if (component instanceof AbstractFieldConnector) {
+ hideErrors = ((AbstractFieldConnector) component).getState()
+ .isHideErrors();
+ }
+
+ getWidget().table.updateError(component.getWidget(), component
+ .getState().getErrorMessage(), hideErrors);
+ }
+
+ @Override
+ public VFormLayout getWidget() {
+ return (VFormLayout) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VFormLayout.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java b/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java
new file mode 100644
index 0000000000..85584755a6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/formlayout/VFormLayout.java
@@ -0,0 +1,379 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.formlayout;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+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.FlexTable;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Focusable;
+import com.vaadin.terminal.gwt.client.StyleConstants;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+
+/**
+ * Two col Layout that places caption on left col and field on right col
+ */
+public class VFormLayout extends SimplePanel {
+
+ private final static String CLASSNAME = "v-formlayout";
+
+ ApplicationConnection client;
+ VFormLayoutTable table;
+
+ public VFormLayout() {
+ super();
+ setStyleName(CLASSNAME);
+ table = new VFormLayoutTable();
+ setWidget(table);
+ }
+
+ /**
+ * Parses the stylenames from shared state
+ *
+ * @param state
+ * shared state of the component
+ * @param enabled
+ * @return An array of stylenames
+ */
+ private String[] getStylesFromState(ComponentState state, boolean enabled) {
+ List<String> styles = new ArrayList<String>();
+ if (state.hasStyles()) {
+ for (String name : state.getStyles()) {
+ styles.add(name);
+ }
+ }
+
+ if (!enabled) {
+ styles.add(ApplicationConnection.DISABLED_CLASSNAME);
+ }
+
+ return styles.toArray(new String[styles.size()]);
+ }
+
+ public class VFormLayoutTable extends FlexTable implements ClickHandler {
+
+ private static final int COLUMN_CAPTION = 0;
+ private static final int COLUMN_ERRORFLAG = 1;
+ private static final int COLUMN_WIDGET = 2;
+
+ private HashMap<Widget, Caption> widgetToCaption = new HashMap<Widget, Caption>();
+ private HashMap<Widget, ErrorFlag> widgetToError = new HashMap<Widget, ErrorFlag>();
+
+ public VFormLayoutTable() {
+ DOM.setElementProperty(getElement(), "cellPadding", "0");
+ DOM.setElementProperty(getElement(), "cellSpacing", "0");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt
+ * .event.dom.client.ClickEvent)
+ */
+ public void onClick(ClickEvent event) {
+ Caption caption = (Caption) event.getSource();
+ if (caption.getOwner() != null) {
+ if (caption.getOwner() instanceof Focusable) {
+ ((Focusable) caption.getOwner()).focus();
+ } else if (caption.getOwner() instanceof com.google.gwt.user.client.ui.Focusable) {
+ ((com.google.gwt.user.client.ui.Focusable) caption
+ .getOwner()).setFocus(true);
+ }
+ }
+ }
+
+ public void setMargins(VMarginInfo margins) {
+ Element margin = getElement();
+ setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP,
+ margins.hasTop());
+ setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT,
+ margins.hasRight());
+ setStyleName(margin,
+ CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM,
+ margins.hasBottom());
+ setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT,
+ margins.hasLeft());
+
+ }
+
+ public void setSpacing(boolean spacing) {
+ setStyleName(getElement(), CLASSNAME + "-" + "spacing", spacing);
+
+ }
+
+ public void setRowCount(int rowNr) {
+ for (int i = 0; i < rowNr; i++) {
+ prepareCell(i, COLUMN_CAPTION);
+ getCellFormatter().setStyleName(i, COLUMN_CAPTION,
+ CLASSNAME + "-captioncell");
+
+ prepareCell(i, 1);
+ getCellFormatter().setStyleName(i, COLUMN_ERRORFLAG,
+ CLASSNAME + "-errorcell");
+
+ prepareCell(i, 2);
+ getCellFormatter().setStyleName(i, COLUMN_WIDGET,
+ CLASSNAME + "-contentcell");
+
+ String rowstyles = CLASSNAME + "-row";
+ if (i == 0) {
+ rowstyles += " " + CLASSNAME + "-firstrow";
+ }
+ if (i == rowNr - 1) {
+ rowstyles += " " + CLASSNAME + "-lastrow";
+ }
+
+ getRowFormatter().setStyleName(i, rowstyles);
+
+ }
+ while (getRowCount() != rowNr) {
+ removeRow(rowNr);
+ }
+ }
+
+ public void setChild(int rowNr, Widget childWidget, Caption caption,
+ ErrorFlag error) {
+ setWidget(rowNr, COLUMN_WIDGET, childWidget);
+ setWidget(rowNr, COLUMN_CAPTION, caption);
+ setWidget(rowNr, COLUMN_ERRORFLAG, error);
+
+ widgetToCaption.put(childWidget, caption);
+ widgetToError.put(childWidget, error);
+
+ }
+
+ public Caption getCaption(Widget childWidget) {
+ return widgetToCaption.get(childWidget);
+ }
+
+ public ErrorFlag getError(Widget childWidget) {
+ return widgetToError.get(childWidget);
+ }
+
+ public void cleanReferences(Widget oldChildWidget) {
+ widgetToError.remove(oldChildWidget);
+ widgetToCaption.remove(oldChildWidget);
+
+ }
+
+ public void updateCaption(Widget widget, ComponentState state,
+ boolean enabled) {
+ final Caption c = widgetToCaption.get(widget);
+ if (c != null) {
+ c.updateCaption(state, enabled);
+ }
+ }
+
+ public void updateError(Widget widget, String errorMessage,
+ boolean hideErrors) {
+ final ErrorFlag e = widgetToError.get(widget);
+ if (e != null) {
+ e.updateError(errorMessage, hideErrors);
+ }
+
+ }
+
+ }
+
+ // TODO why duplicated here?
+ public class Caption extends HTML {
+
+ public static final String CLASSNAME = "v-caption";
+
+ private final ComponentConnector owner;
+
+ private Element requiredFieldIndicator;
+
+ private Icon icon;
+
+ private Element captionText;
+
+ /**
+ *
+ * @param component
+ * optional owner of caption. If not set, getOwner will
+ * return null
+ */
+ public Caption(ComponentConnector component) {
+ super();
+ owner = component;
+
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ }
+
+ private void setStyles(String[] styles) {
+ String styleName = CLASSNAME;
+
+ if (styles != null) {
+ for (String style : styles) {
+ if (ApplicationConnection.DISABLED_CLASSNAME.equals(style)) {
+ // Add v-disabled also without classname prefix so
+ // generic v-disabled CSS rules work
+ styleName += " " + style;
+ }
+
+ styleName += " " + CLASSNAME + "-" + style;
+ }
+ }
+
+ setStyleName(styleName);
+ }
+
+ public void updateCaption(ComponentState state, boolean enabled) {
+ // Update styles as they might have changed when the caption changed
+ setStyles(getStylesFromState(state, enabled));
+
+ boolean isEmpty = true;
+
+ if (state.getIcon() != null) {
+ if (icon == null) {
+ icon = new Icon(owner.getConnection());
+
+ DOM.insertChild(getElement(), icon.getElement(), 0);
+ }
+ icon.setUri(state.getIcon().getURL());
+ isEmpty = false;
+ } else {
+ if (icon != null) {
+ DOM.removeChild(getElement(), icon.getElement());
+ icon = null;
+ }
+
+ }
+
+ if (state.getCaption() != null) {
+ if (captionText == null) {
+ captionText = DOM.createSpan();
+ DOM.insertChild(getElement(), captionText, icon == null ? 0
+ : 1);
+ }
+ String c = state.getCaption();
+ if (c == null) {
+ c = "";
+ } else {
+ isEmpty = false;
+ }
+ DOM.setInnerText(captionText, c);
+ } else {
+ // TODO should span also be removed
+ }
+
+ if (state.hasDescription() && captionText != null) {
+ addStyleDependentName("hasdescription");
+ } else {
+ removeStyleDependentName("hasdescription");
+ }
+
+ boolean required = owner instanceof AbstractFieldConnector
+ && ((AbstractFieldConnector) owner).isRequired();
+ if (required) {
+ if (requiredFieldIndicator == null) {
+ requiredFieldIndicator = DOM.createSpan();
+ DOM.setInnerText(requiredFieldIndicator, "*");
+ DOM.setElementProperty(requiredFieldIndicator, "className",
+ "v-required-field-indicator");
+ DOM.appendChild(getElement(), requiredFieldIndicator);
+ }
+ } else {
+ if (requiredFieldIndicator != null) {
+ DOM.removeChild(getElement(), requiredFieldIndicator);
+ requiredFieldIndicator = null;
+ }
+ }
+
+ // Workaround for IE weirdness, sometimes returns bad height in some
+ // circumstances when Caption is empty. See #1444
+ // IE7 bugs more often. I wonder what happens when IE8 arrives...
+ // FIXME: This could be unnecessary for IE8+
+ if (BrowserInfo.get().isIE()) {
+ if (isEmpty) {
+ setHeight("0px");
+ DOM.setStyleAttribute(getElement(), "overflow", "hidden");
+ } else {
+ setHeight("");
+ DOM.setStyleAttribute(getElement(), "overflow", "");
+ }
+
+ }
+
+ }
+
+ /**
+ * Returns Paintable for which this Caption belongs to.
+ *
+ * @return owner Widget
+ */
+ public ComponentConnector getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ owner.getConnection().handleTooltipEvent(event, owner);
+ }
+ }
+
+ class ErrorFlag extends HTML {
+ private static final String CLASSNAME = VFormLayout.CLASSNAME
+ + "-error-indicator";
+ Element errorIndicatorElement;
+
+ private ComponentConnector owner;
+
+ public ErrorFlag(ComponentConnector owner) {
+ setStyleName(CLASSNAME);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ this.owner = owner;
+ }
+
+ public void updateError(String errorMessage, boolean hideErrors) {
+ boolean showError = null != errorMessage;
+ if (hideErrors) {
+ showError = false;
+ }
+
+ if (showError) {
+ if (errorIndicatorElement == null) {
+ errorIndicatorElement = DOM.createDiv();
+ DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
+ DOM.setElementProperty(errorIndicatorElement, "className",
+ "v-errorindicator");
+ DOM.appendChild(getElement(), errorIndicatorElement);
+ }
+
+ } else if (errorIndicatorElement != null) {
+ DOM.removeChild(getElement(), errorIndicatorElement);
+ errorIndicatorElement = null;
+ }
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ if (owner != null) {
+ client.handleTooltipEvent(event, owner);
+ }
+ }
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java
new file mode 100644
index 0000000000..e4a31b96ef
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java
@@ -0,0 +1,239 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.gridlayout;
+
+import java.util.Iterator;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.AlignmentInfo;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout.Cell;
+import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot;
+import com.vaadin.ui.GridLayout;
+
+@Connect(GridLayout.class)
+public class GridLayoutConnector extends AbstractComponentContainerConnector
+ implements Paintable, DirectionalManagedLayout {
+
+ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
+ this) {
+
+ @Override
+ protected ComponentConnector getChildComponent(Element element) {
+ return getWidget().getComponent(element);
+ }
+
+ @Override
+ protected LayoutClickRpc getLayoutClickRPC() {
+ return rpc;
+ };
+
+ };
+
+ private GridLayoutServerRpc rpc;
+ private boolean needCaptionUpdate = false;
+
+ @Override
+ public void init() {
+ rpc = RpcProxy.create(GridLayoutServerRpc.class, this);
+ getLayoutManager().registerDependency(this,
+ getWidget().spacingMeasureElement);
+ }
+
+ @Override
+ public void onUnregister() {
+ VGridLayout layout = getWidget();
+ getLayoutManager().unregisterDependency(this,
+ layout.spacingMeasureElement);
+
+ // Unregister caption size dependencies
+ for (ComponentConnector child : getChildren()) {
+ Cell cell = layout.widgetToCell.get(child.getWidget());
+ cell.slot.setCaption(null);
+ }
+ }
+
+ @Override
+ public GridLayoutState getState() {
+ return (GridLayoutState) super.getState();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ VGridLayout layout = getWidget();
+ layout.client = client;
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ int cols = getState().getColumns();
+ int rows = getState().getRows();
+
+ layout.columnWidths = new int[cols];
+ layout.rowHeights = new int[rows];
+
+ layout.setSize(rows, cols);
+
+ final int[] alignments = uidl.getIntArrayAttribute("alignments");
+ int alignmentIndex = 0;
+
+ for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
+ final UIDL r = (UIDL) i.next();
+ if ("gr".equals(r.getTag())) {
+ for (final Iterator<?> j = r.getChildIterator(); j.hasNext();) {
+ final UIDL cellUidl = (UIDL) j.next();
+ if ("gc".equals(cellUidl.getTag())) {
+ int row = cellUidl.getIntAttribute("y");
+ int col = cellUidl.getIntAttribute("x");
+
+ Widget previousWidget = null;
+
+ Cell cell = layout.getCell(row, col);
+ if (cell != null && cell.slot != null) {
+ // This is an update. Track if the widget changes
+ // and update the caption if that happens. This
+ // workaround can be removed once the DOM update is
+ // done in onContainerHierarchyChange
+ previousWidget = cell.slot.getWidget();
+ }
+
+ cell = layout.createCell(row, col);
+
+ cell.updateFromUidl(cellUidl);
+
+ if (cell.hasContent()) {
+ cell.setAlignment(new AlignmentInfo(
+ alignments[alignmentIndex++]));
+ if (cell.slot.getWidget() != previousWidget) {
+ // Widget changed or widget moved from another
+ // slot. Update its caption as the widget might
+ // have called updateCaption when the widget was
+ // still in its old slot. This workaround can be
+ // removed once the DOM update
+ // is done in onContainerHierarchyChange
+ updateCaption(ConnectorMap.get(getConnection())
+ .getConnector(cell.slot.getWidget()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ layout.colExpandRatioArray = uidl.getIntArrayAttribute("colExpand");
+ layout.rowExpandRatioArray = uidl.getIntArrayAttribute("rowExpand");
+
+ layout.updateMarginStyleNames(new VMarginInfo(getState()
+ .getMarginsBitmask()));
+
+ layout.updateSpacingStyleName(getState().isSpacing());
+
+ if (needCaptionUpdate) {
+ needCaptionUpdate = false;
+
+ for (ComponentConnector child : getChildren()) {
+ updateCaption(child);
+ }
+ }
+ getLayoutManager().setNeedsLayout(this);
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ VGridLayout layout = getWidget();
+
+ // clean non rendered components
+ for (ComponentConnector oldChild : event.getOldChildren()) {
+ if (oldChild.getParent() == this) {
+ continue;
+ }
+
+ Widget childWidget = oldChild.getWidget();
+ layout.remove(childWidget);
+
+ Cell cell = layout.widgetToCell.remove(childWidget);
+ cell.slot.setCaption(null);
+ cell.slot.getWrapperElement().removeFromParent();
+ cell.slot = null;
+ }
+
+ }
+
+ public void updateCaption(ComponentConnector childConnector) {
+ if (!childConnector.delegateCaptionHandling()) {
+ // Check not required by interface but by workarounds in this class
+ // when updateCaption is explicitly called for all children.
+ return;
+ }
+
+ VGridLayout layout = getWidget();
+ Cell cell = layout.widgetToCell.get(childConnector.getWidget());
+ if (cell == null) {
+ // workaround before updateFromUidl is removed. We currently update
+ // the captions at the end of updateFromUidl instead of immediately
+ // because the DOM has not been set up at this point (as it is done
+ // in updateFromUidl)
+ needCaptionUpdate = true;
+ return;
+ }
+ if (VCaption.isNeeded(childConnector.getState())) {
+ VLayoutSlot layoutSlot = cell.slot;
+ VCaption caption = layoutSlot.getCaption();
+ if (caption == null) {
+ caption = new VCaption(childConnector, getConnection());
+
+ Widget widget = childConnector.getWidget();
+
+ layout.setCaption(widget, caption);
+ }
+ caption.updateCaption();
+ } else {
+ layout.setCaption(childConnector.getWidget(), null);
+ }
+ }
+
+ @Override
+ public VGridLayout getWidget() {
+ return (VGridLayout) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VGridLayout.class);
+ }
+
+ public void layoutVertically() {
+ getWidget().updateHeight();
+ }
+
+ public void layoutHorizontally() {
+ getWidget().updateWidth();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java
new file mode 100644
index 0000000000..cd8df297ec
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.gridlayout;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+
+public interface GridLayoutServerRpc extends LayoutClickRpc, ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutState.java
new file mode 100644
index 0000000000..109dc7dea6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutState.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.gridlayout;
+
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
+
+public class GridLayoutState extends AbstractLayoutState {
+ private boolean spacing = false;
+ private int rows = 0;
+ private int columns = 0;
+
+ public boolean isSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(boolean spacing) {
+ this.spacing = spacing;
+ }
+
+ public int getRows() {
+ return rows;
+ }
+
+ public void setRows(int rows) {
+ this.rows = rows;
+ }
+
+ public int getColumns() {
+ return columns;
+ }
+
+ public void setColumns(int cols) {
+ columns = cols;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java
new file mode 100644
index 0000000000..7629e09cac
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java
@@ -0,0 +1,702 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.gridlayout;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+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.ui.ComplexPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.AlignmentInfo;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.layout.ComponentConnectorLayoutSlot;
+import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot;
+
+public class VGridLayout extends ComplexPanel {
+
+ public static final String CLASSNAME = "v-gridlayout";
+
+ ApplicationConnection client;
+
+ HashMap<Widget, Cell> widgetToCell = new HashMap<Widget, Cell>();
+
+ int[] columnWidths;
+ int[] rowHeights;
+
+ int[] colExpandRatioArray;
+
+ int[] rowExpandRatioArray;
+
+ int[] minColumnWidths;
+
+ private int[] minRowHeights;
+
+ DivElement spacingMeasureElement;
+
+ public VGridLayout() {
+ super();
+ setElement(Document.get().createDivElement());
+
+ spacingMeasureElement = Document.get().createDivElement();
+ Style spacingStyle = spacingMeasureElement.getStyle();
+ spacingStyle.setPosition(Position.ABSOLUTE);
+ getElement().appendChild(spacingMeasureElement);
+
+ setStyleName(CLASSNAME);
+ }
+
+ private GridLayoutConnector getConnector() {
+ return (GridLayoutConnector) ConnectorMap.get(client)
+ .getConnector(this);
+ }
+
+ /**
+ * Returns the column widths measured in pixels
+ *
+ * @return
+ */
+ protected int[] getColumnWidths() {
+ return columnWidths;
+ }
+
+ /**
+ * Returns the row heights measured in pixels
+ *
+ * @return
+ */
+ protected int[] getRowHeights() {
+ return rowHeights;
+ }
+
+ /**
+ * Returns the spacing between the cells horizontally in pixels
+ *
+ * @return
+ */
+ protected int getHorizontalSpacing() {
+ return LayoutManager.get(client).getOuterWidth(spacingMeasureElement);
+ }
+
+ /**
+ * Returns the spacing between the cells vertically in pixels
+ *
+ * @return
+ */
+ protected int getVerticalSpacing() {
+ return LayoutManager.get(client).getOuterHeight(spacingMeasureElement);
+ }
+
+ static int[] cloneArray(int[] toBeCloned) {
+ int[] clone = new int[toBeCloned.length];
+ for (int i = 0; i < clone.length; i++) {
+ clone[i] = toBeCloned[i] * 1;
+ }
+ return clone;
+ }
+
+ void expandRows() {
+ if (!isUndefinedHeight()) {
+ int usedSpace = minRowHeights[0];
+ int verticalSpacing = getVerticalSpacing();
+ for (int i = 1; i < minRowHeights.length; i++) {
+ usedSpace += verticalSpacing + minRowHeights[i];
+ }
+ int availableSpace = LayoutManager.get(client).getInnerHeight(
+ getElement());
+ int excessSpace = availableSpace - usedSpace;
+ int distributed = 0;
+ if (excessSpace > 0) {
+ for (int i = 0; i < rowHeights.length; i++) {
+ int ew = excessSpace * rowExpandRatioArray[i] / 1000;
+ rowHeights[i] = minRowHeights[i] + ew;
+ distributed += ew;
+ }
+ excessSpace -= distributed;
+ int c = 0;
+ while (excessSpace > 0) {
+ rowHeights[c % rowHeights.length]++;
+ excessSpace--;
+ c++;
+ }
+ }
+ }
+ }
+
+ void updateHeight() {
+ // Detect minimum heights & calculate spans
+ detectRowHeights();
+
+ // Expand
+ expandRows();
+
+ // Position
+ layoutCellsVertically();
+ }
+
+ void updateWidth() {
+ // Detect widths & calculate spans
+ detectColWidths();
+ // Expand
+ expandColumns();
+ // Position
+ layoutCellsHorizontally();
+
+ }
+
+ void expandColumns() {
+ if (!isUndefinedWidth()) {
+ int usedSpace = minColumnWidths[0];
+ int horizontalSpacing = getHorizontalSpacing();
+ for (int i = 1; i < minColumnWidths.length; i++) {
+ usedSpace += horizontalSpacing + minColumnWidths[i];
+ }
+
+ int availableSpace = LayoutManager.get(client).getInnerWidth(
+ getElement());
+ int excessSpace = availableSpace - usedSpace;
+ int distributed = 0;
+ if (excessSpace > 0) {
+ for (int i = 0; i < columnWidths.length; i++) {
+ int ew = excessSpace * colExpandRatioArray[i] / 1000;
+ columnWidths[i] = minColumnWidths[i] + ew;
+ distributed += ew;
+ }
+ excessSpace -= distributed;
+ int c = 0;
+ while (excessSpace > 0) {
+ columnWidths[c % columnWidths.length]++;
+ excessSpace--;
+ c++;
+ }
+ }
+ }
+ }
+
+ void layoutCellsVertically() {
+ int verticalSpacing = getVerticalSpacing();
+ LayoutManager layoutManager = LayoutManager.get(client);
+ Element element = getElement();
+ int paddingTop = layoutManager.getPaddingTop(element);
+ int paddingBottom = layoutManager.getPaddingBottom(element);
+ int y = paddingTop;
+
+ for (int i = 0; i < cells.length; i++) {
+ y = paddingTop;
+ for (int j = 0; j < cells[i].length; j++) {
+ Cell cell = cells[i][j];
+ if (cell != null) {
+ int reservedMargin;
+ if (cell.rowspan + j >= cells[i].length) {
+ // Make room for layout padding for cells reaching the
+ // bottom of the layout
+ reservedMargin = paddingBottom;
+ } else {
+ reservedMargin = 0;
+ }
+ cell.layoutVertically(y, reservedMargin);
+ }
+ y += rowHeights[j] + verticalSpacing;
+ }
+ }
+
+ if (isUndefinedHeight()) {
+ int outerHeight = y - verticalSpacing
+ + layoutManager.getPaddingBottom(element)
+ + layoutManager.getBorderHeight(element);
+ element.getStyle().setHeight(outerHeight, Unit.PX);
+ getConnector().getLayoutManager().reportOuterHeight(getConnector(),
+ outerHeight);
+ }
+ }
+
+ void layoutCellsHorizontally() {
+ LayoutManager layoutManager = LayoutManager.get(client);
+ Element element = getElement();
+ int x = layoutManager.getPaddingLeft(element);
+ int paddingRight = layoutManager.getPaddingRight(element);
+ int horizontalSpacing = getHorizontalSpacing();
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < cells[i].length; j++) {
+ Cell cell = cells[i][j];
+ if (cell != null) {
+ int reservedMargin;
+ // Make room for layout padding for cells reaching the
+ // right edge of the layout
+ if (i + cell.colspan >= cells.length) {
+ reservedMargin = paddingRight;
+ } else {
+ reservedMargin = 0;
+ }
+ cell.layoutHorizontally(x, reservedMargin);
+ }
+ }
+ x += columnWidths[i] + horizontalSpacing;
+ }
+
+ if (isUndefinedWidth()) {
+ int outerWidth = x - horizontalSpacing
+ + layoutManager.getPaddingRight(element)
+ + layoutManager.getBorderWidth(element);
+ element.getStyle().setWidth(outerWidth, Unit.PX);
+ getConnector().getLayoutManager().reportOuterWidth(getConnector(),
+ outerWidth);
+ }
+ }
+
+ private boolean isUndefinedHeight() {
+ return getConnector().isUndefinedHeight();
+ }
+
+ private boolean isUndefinedWidth() {
+ return getConnector().isUndefinedWidth();
+ }
+
+ private void detectRowHeights() {
+ for (int i = 0; i < rowHeights.length; i++) {
+ rowHeights[i] = 0;
+ }
+
+ // collect min rowheight from non-rowspanned cells
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < cells[i].length; j++) {
+ Cell cell = cells[i][j];
+ if (cell != null) {
+ if (cell.rowspan == 1) {
+ if (!cell.hasRelativeHeight()
+ && rowHeights[j] < cell.getHeight()) {
+ rowHeights[j] = cell.getHeight();
+ }
+ } else {
+ storeRowSpannedCell(cell);
+ }
+ }
+ }
+ }
+
+ distributeRowSpanHeights();
+
+ minRowHeights = cloneArray(rowHeights);
+ }
+
+ private void detectColWidths() {
+ // collect min colwidths from non-colspanned cells
+ for (int i = 0; i < columnWidths.length; i++) {
+ columnWidths[i] = 0;
+ }
+
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < cells[i].length; j++) {
+ Cell cell = cells[i][j];
+ if (cell != null) {
+ if (cell.colspan == 1) {
+ if (!cell.hasRelativeWidth()
+ && columnWidths[i] < cell.getWidth()) {
+ columnWidths[i] = cell.getWidth();
+ }
+ } else {
+ storeColSpannedCell(cell);
+ }
+ }
+ }
+ }
+
+ distributeColSpanWidths();
+
+ minColumnWidths = cloneArray(columnWidths);
+ }
+
+ private void storeRowSpannedCell(Cell cell) {
+ SpanList l = null;
+ for (SpanList list : rowSpans) {
+ if (list.span < cell.rowspan) {
+ continue;
+ } else {
+ // insert before this
+ l = list;
+ break;
+ }
+ }
+ if (l == null) {
+ l = new SpanList(cell.rowspan);
+ rowSpans.add(l);
+ } else if (l.span != cell.rowspan) {
+ SpanList newL = new SpanList(cell.rowspan);
+ rowSpans.add(rowSpans.indexOf(l), newL);
+ l = newL;
+ }
+ l.cells.add(cell);
+ }
+
+ /**
+ * Iterates colspanned cells, ensures cols have enough space to accommodate
+ * them
+ */
+ void distributeColSpanWidths() {
+ for (SpanList list : colSpans) {
+ for (Cell cell : list.cells) {
+ // cells with relative content may return non 0 here if on
+ // subsequent renders
+ int width = cell.hasRelativeWidth() ? 0 : cell.getWidth();
+ distributeSpanSize(columnWidths, cell.col, cell.colspan,
+ getHorizontalSpacing(), width, colExpandRatioArray);
+ }
+ }
+ }
+
+ /**
+ * Iterates rowspanned cells, ensures rows have enough space to accommodate
+ * them
+ */
+ private void distributeRowSpanHeights() {
+ for (SpanList list : rowSpans) {
+ for (Cell cell : list.cells) {
+ // cells with relative content may return non 0 here if on
+ // subsequent renders
+ int height = cell.hasRelativeHeight() ? 0 : cell.getHeight();
+ distributeSpanSize(rowHeights, cell.row, cell.rowspan,
+ getVerticalSpacing(), height, rowExpandRatioArray);
+ }
+ }
+ }
+
+ private static void distributeSpanSize(int[] dimensions,
+ int spanStartIndex, int spanSize, int spacingSize, int size,
+ int[] expansionRatios) {
+ int allocated = dimensions[spanStartIndex];
+ for (int i = 1; i < spanSize; i++) {
+ allocated += spacingSize + dimensions[spanStartIndex + i];
+ }
+ if (allocated < size) {
+ // dimensions needs to be expanded due spanned cell
+ int neededExtraSpace = size - allocated;
+ int allocatedExtraSpace = 0;
+
+ // Divide space according to expansion ratios if any span has a
+ // ratio
+ int totalExpansion = 0;
+ for (int i = 0; i < spanSize; i++) {
+ int itemIndex = spanStartIndex + i;
+ totalExpansion += expansionRatios[itemIndex];
+ }
+
+ for (int i = 0; i < spanSize; i++) {
+ int itemIndex = spanStartIndex + i;
+ int expansion;
+ if (totalExpansion == 0) {
+ // Divide equally among all cells if there are no
+ // expansion ratios
+ expansion = neededExtraSpace / spanSize;
+ } else {
+ expansion = neededExtraSpace * expansionRatios[itemIndex]
+ / totalExpansion;
+ }
+ dimensions[itemIndex] += expansion;
+ allocatedExtraSpace += expansion;
+ }
+
+ // We might still miss a couple of pixels because of
+ // rounding errors...
+ if (neededExtraSpace > allocatedExtraSpace) {
+ for (int i = 0; i < spanSize; i++) {
+ // Add one pixel to every cell until we have
+ // compensated for any rounding error
+ int itemIndex = spanStartIndex + i;
+ dimensions[itemIndex] += 1;
+ allocatedExtraSpace += 1;
+ if (neededExtraSpace == allocatedExtraSpace) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private LinkedList<SpanList> colSpans = new LinkedList<SpanList>();
+ private LinkedList<SpanList> rowSpans = new LinkedList<SpanList>();
+
+ private class SpanList {
+ final int span;
+ List<Cell> cells = new LinkedList<Cell>();
+
+ public SpanList(int span) {
+ this.span = span;
+ }
+ }
+
+ void storeColSpannedCell(Cell cell) {
+ SpanList l = null;
+ for (SpanList list : colSpans) {
+ if (list.span < cell.colspan) {
+ continue;
+ } else {
+ // insert before this
+ l = list;
+ break;
+ }
+ }
+ if (l == null) {
+ l = new SpanList(cell.colspan);
+ colSpans.add(l);
+ } else if (l.span != cell.colspan) {
+
+ SpanList newL = new SpanList(cell.colspan);
+ colSpans.add(colSpans.indexOf(l), newL);
+ l = newL;
+ }
+ l.cells.add(cell);
+ }
+
+ Cell[][] cells;
+
+ /**
+ * Private helper class.
+ */
+ class Cell {
+ public Cell(int row, int col) {
+ this.row = row;
+ this.col = col;
+ }
+
+ public boolean hasContent() {
+ return hasContent;
+ }
+
+ public boolean hasRelativeHeight() {
+ if (slot != null) {
+ return slot.getChild().isRelativeHeight();
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @return total of spanned cols
+ */
+ private int getAvailableWidth() {
+ int width = columnWidths[col];
+ for (int i = 1; i < colspan; i++) {
+ width += getHorizontalSpacing() + columnWidths[col + i];
+ }
+ return width;
+ }
+
+ /**
+ * @return total of spanned rows
+ */
+ private int getAvailableHeight() {
+ int height = rowHeights[row];
+ for (int i = 1; i < rowspan; i++) {
+ height += getVerticalSpacing() + rowHeights[row + i];
+ }
+ return height;
+ }
+
+ public void layoutHorizontally(int x, int marginRight) {
+ if (slot != null) {
+ slot.positionHorizontally(x, getAvailableWidth(), marginRight);
+ }
+ }
+
+ public void layoutVertically(int y, int marginBottom) {
+ if (slot != null) {
+ slot.positionVertically(y, getAvailableHeight(), marginBottom);
+ }
+ }
+
+ public int getWidth() {
+ if (slot != null) {
+ return slot.getUsedWidth();
+ } else {
+ return 0;
+ }
+ }
+
+ public int getHeight() {
+ if (slot != null) {
+ return slot.getUsedHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ protected boolean hasRelativeWidth() {
+ if (slot != null) {
+ return slot.getChild().isRelativeWidth();
+ } else {
+ return true;
+ }
+ }
+
+ final int row;
+ final int col;
+ int colspan = 1;
+ int rowspan = 1;
+
+ private boolean hasContent;
+
+ private AlignmentInfo alignment;
+
+ ComponentConnectorLayoutSlot slot;
+
+ public void updateFromUidl(UIDL cellUidl) {
+ // Set cell width
+ colspan = cellUidl.hasAttribute("w") ? cellUidl
+ .getIntAttribute("w") : 1;
+ // Set cell height
+ rowspan = cellUidl.hasAttribute("h") ? cellUidl
+ .getIntAttribute("h") : 1;
+ // ensure we will lose reference to old cells, now overlapped by
+ // this cell
+ for (int i = 0; i < colspan; i++) {
+ for (int j = 0; j < rowspan; j++) {
+ if (i > 0 || j > 0) {
+ cells[col + i][row + j] = null;
+ }
+ }
+ }
+
+ UIDL childUidl = cellUidl.getChildUIDL(0); // we are interested
+ // about childUidl
+ hasContent = childUidl != null;
+ if (hasContent) {
+ ComponentConnector childConnector = client
+ .getPaintable(childUidl);
+
+ if (slot == null || slot.getChild() != childConnector) {
+ slot = new ComponentConnectorLayoutSlot(CLASSNAME,
+ childConnector, getConnector());
+ if (childConnector.isRelativeWidth()) {
+ slot.getWrapperElement().getStyle()
+ .setWidth(100, Unit.PCT);
+ }
+ Element slotWrapper = slot.getWrapperElement();
+ getElement().appendChild(slotWrapper);
+
+ Widget widget = childConnector.getWidget();
+ insert(widget, slotWrapper, getWidgetCount(), false);
+ Cell oldCell = widgetToCell.put(widget, this);
+ if (oldCell != null) {
+ oldCell.slot.getWrapperElement().removeFromParent();
+ oldCell.slot = null;
+ }
+ }
+
+ }
+ }
+
+ public void setAlignment(AlignmentInfo alignmentInfo) {
+ slot.setAlignment(alignmentInfo);
+ }
+ }
+
+ Cell getCell(int row, int col) {
+ return cells[col][row];
+ }
+
+ /**
+ * Creates a new Cell with the given coordinates. If an existing cell was
+ * found, returns that one.
+ *
+ * @param row
+ * @param col
+ * @return
+ */
+ Cell createCell(int row, int col) {
+ Cell cell = getCell(row, col);
+ if (cell == null) {
+ cell = new Cell(row, col);
+ cells[col][row] = cell;
+ }
+ return cell;
+ }
+
+ /**
+ * 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.
+ */
+ ComponentConnector getComponent(Element element) {
+ return Util.getConnectorForElement(client, this, element);
+ }
+
+ void setCaption(Widget widget, VCaption caption) {
+ VLayoutSlot slot = widgetToCell.get(widget).slot;
+
+ if (caption != null) {
+ // Logical attach.
+ getChildren().add(caption);
+ }
+
+ // Physical attach if not null, also removes old caption
+ slot.setCaption(caption);
+
+ if (caption != null) {
+ // Adopt.
+ adopt(caption);
+ }
+ }
+
+ private void togglePrefixedStyleName(String name, boolean enabled) {
+ if (enabled) {
+ addStyleDependentName(name);
+ } else {
+ removeStyleDependentName(name);
+ }
+ }
+
+ void updateMarginStyleNames(VMarginInfo marginInfo) {
+ togglePrefixedStyleName("margin-top", marginInfo.hasTop());
+ togglePrefixedStyleName("margin-right", marginInfo.hasRight());
+ togglePrefixedStyleName("margin-bottom", marginInfo.hasBottom());
+ togglePrefixedStyleName("margin-left", marginInfo.hasLeft());
+ }
+
+ void updateSpacingStyleName(boolean spacingEnabled) {
+ String styleName = getStylePrimaryName();
+ if (spacingEnabled) {
+ spacingMeasureElement.addClassName(styleName + "-spacing-on");
+ spacingMeasureElement.removeClassName(styleName + "-spacing-off");
+ } else {
+ spacingMeasureElement.removeClassName(styleName + "-spacing-on");
+ spacingMeasureElement.addClassName(styleName + "-spacing-off");
+ }
+ }
+
+ public void setSize(int rows, int cols) {
+ if (cells == null) {
+ cells = new Cell[cols][rows];
+ } else if (cells.length != cols || cells[0].length != rows) {
+ Cell[][] newCells = new Cell[cols][rows];
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < cells[i].length; j++) {
+ if (i < cols && j < rows) {
+ newCells[i][j] = cells[i][j];
+ }
+ }
+ }
+ cells = newCells;
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java
new file mode 100644
index 0000000000..308a4860b5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/label/LabelConnector.java
@@ -0,0 +1,74 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.label;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.PreElement;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.Label;
+
+@Connect(value = Label.class, loadStyle = LoadStyle.EAGER)
+public class LabelConnector extends AbstractComponentConnector implements
+ Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().setConnection(client);
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ boolean sinkOnloads = false;
+
+ final String mode = uidl.getStringAttribute("mode");
+ if (mode == null || "text".equals(mode)) {
+ getWidget().setText(uidl.getChildString(0));
+ } else if ("pre".equals(mode)) {
+ PreElement preElement = Document.get().createPreElement();
+ preElement.setInnerText(uidl.getChildUIDL(0).getChildString(0));
+ // clear existing content
+ getWidget().setHTML("");
+ // add preformatted text to dom
+ getWidget().getElement().appendChild(preElement);
+ } else if ("uidl".equals(mode)) {
+ getWidget().setHTML(uidl.getChildrenAsXML());
+ } else if ("xhtml".equals(mode)) {
+ UIDL content = uidl.getChildUIDL(0).getChildUIDL(0);
+ if (content.getChildCount() > 0) {
+ getWidget().setHTML(content.getChildString(0));
+ } else {
+ getWidget().setHTML("");
+ }
+ sinkOnloads = true;
+ } else if ("xml".equals(mode)) {
+ getWidget().setHTML(uidl.getChildUIDL(0).getChildString(0));
+ } else if ("raw".equals(mode)) {
+ getWidget().setHTML(uidl.getChildUIDL(0).getChildString(0));
+ sinkOnloads = true;
+ } else {
+ getWidget().setText("");
+ }
+ if (sinkOnloads) {
+ Util.sinkOnloadForImages(getWidget().getElement());
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VLabel.class);
+ }
+
+ @Override
+ public VLabel getWidget() {
+ return (VLabel) super.getWidget();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/label/VLabel.java b/src/com/vaadin/terminal/gwt/client/ui/label/VLabel.java
new file mode 100644
index 0000000000..f47b8437b7
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/label/VLabel.java
@@ -0,0 +1,73 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.label;
+
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VTooltip;
+
+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);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ }
+
+ public VLabel(String text) {
+ super(text);
+ setStyleName(CLASSNAME);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ if (event.getTypeInt() == Event.ONLOAD) {
+ Util.notifyParentOfSizeChange(this, true);
+ event.stopPropagation();
+ return;
+ }
+ if (connection != null) {
+ connection.handleTooltipEvent(event, this);
+ }
+ }
+
+ @Override
+ public void setWidth(String width) {
+ super.setWidth(width);
+ if (width == null || width.equals("")) {
+ setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, true);
+ getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
+ } else {
+ setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, false);
+ getElement().getStyle().clearDisplay();
+ }
+ }
+
+ @Override
+ public void setText(String text) {
+ if (BrowserInfo.get().isIE8()) {
+ // #3983 - IE8 incorrectly replaces \n with <br> so we do the
+ // escaping manually and set as HTML
+ super.setHTML(Util.escapeHTML(text));
+ } else {
+ super.setText(text);
+ }
+ }
+
+ void setConnection(ApplicationConnection client) {
+ connection = client;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java
deleted file mode 100644
index 2ca58a38c2..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui.layout;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.user.client.ui.ComplexPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
-
-public abstract class CellBasedLayout extends ComplexPanel implements Container {
-
- protected Map<Widget, ChildComponentContainer> widgetToComponentContainer = new HashMap<Widget, ChildComponentContainer>();
-
- protected ApplicationConnection client = null;
-
- protected DivElement root;
-
- public static final int ORIENTATION_VERTICAL = 0;
- public static final int ORIENTATION_HORIZONTAL = 1;
-
- protected Margins activeMargins = new Margins(0, 0, 0, 0);
- protected VMarginInfo activeMarginsInfo = new VMarginInfo(-1);
-
- protected boolean spacingEnabled = false;
- protected final Spacing spacingFromCSS = new Spacing(12, 12);
- protected final Spacing activeSpacing = new Spacing(0, 0);
-
- private boolean dynamicWidth;
-
- private boolean dynamicHeight;
-
- private final DivElement clearElement = Document.get().createDivElement();
-
- private String lastStyleName = "";
-
- private boolean marginsNeedsRecalculation = false;
-
- protected String STYLENAME_SPACING = "";
- protected String STYLENAME_MARGIN_TOP = "";
- protected String STYLENAME_MARGIN_RIGHT = "";
- protected String STYLENAME_MARGIN_BOTTOM = "";
- protected String STYLENAME_MARGIN_LEFT = "";
-
- public static class Spacing {
-
- public int hSpacing = 0;
- public int vSpacing = 0;
-
- public Spacing(int hSpacing, int vSpacing) {
- this.hSpacing = hSpacing;
- this.vSpacing = vSpacing;
- }
-
- @Override
- public String toString() {
- return "Spacing [hSpacing=" + hSpacing + ",vSpacing=" + vSpacing
- + "]";
- }
-
- }
-
- public CellBasedLayout() {
- super();
-
- setElement(Document.get().createDivElement());
- getElement().getStyle().setProperty("overflow", "hidden");
- if (BrowserInfo.get().isIE()) {
- getElement().getStyle().setProperty("position", "relative");
- getElement().getStyle().setProperty("zoom", "1");
- }
-
- root = Document.get().createDivElement();
- root.getStyle().setProperty("overflow", "hidden");
- if (BrowserInfo.get().isIE()) {
- root.getStyle().setProperty("position", "relative");
- }
-
- getElement().appendChild(root);
-
- Style style = clearElement.getStyle();
- style.setProperty("width", "0px");
- style.setProperty("height", "0px");
- style.setProperty("clear", "both");
- style.setProperty("overflow", "hidden");
- root.appendChild(clearElement);
-
- }
-
- public boolean hasChildComponent(Widget component) {
- return widgetToComponentContainer.containsKey(component);
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
-
- // Only non-cached UIDL:s can introduce changes
- if (uidl.getBooleanAttribute("cached")) {
- return;
- }
-
- /**
- * Margin and spacind detection depends on classNames and must be set
- * before setting size. Here just update the details from UIDL and from
- * overridden setStyleName run actual margin detections.
- */
- updateMarginAndSpacingInfo(uidl);
-
- /*
- * This call should be made first. Ensure correct implementation, handle
- * size etc.
- */
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- handleDynamicDimensions(uidl);
- }
-
- @Override
- public void setStyleName(String styleName) {
- super.setStyleName(styleName);
-
- if (isAttached() && marginsNeedsRecalculation
- || !lastStyleName.equals(styleName)) {
- measureMarginsAndSpacing();
- lastStyleName = styleName;
- marginsNeedsRecalculation = false;
- }
-
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
-
- /*
- * Ensure the the dynamic width stays up to date even if size is altered
- * only on client side.
- */
- if (width == null || width.equals("")) {
- dynamicWidth = true;
- } else {
- dynamicWidth = false;
- }
- }
-
- private void handleDynamicDimensions(UIDL uidl) {
- String w = uidl.hasAttribute("width") ? uidl
- .getStringAttribute("width") : "";
-
- String h = uidl.hasAttribute("height") ? uidl
- .getStringAttribute("height") : "";
-
- if (w.equals("")) {
- dynamicWidth = true;
- } else {
- dynamicWidth = false;
- }
-
- if (h.equals("")) {
- dynamicHeight = true;
- } else {
- dynamicHeight = false;
- }
-
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
-
- /*
- * Ensure the the dynamic height stays up to date even if size is
- * altered only on client side.
- */
- if (height == null || height.equals("")) {
- dynamicHeight = true;
- } else {
- dynamicHeight = false;
- }
- }
-
- protected void addOrMoveChild(ChildComponentContainer childComponent,
- int position) {
- if (childComponent.getParent() == this) {
- if (getWidgetIndex(childComponent) != position) {
- // Detach from old position child.
- childComponent.removeFromParent();
-
- // Logical attach.
- getChildren().insert(childComponent, position);
-
- root.insertBefore(childComponent.getElement(), root
- .getChildNodes().getItem(position));
-
- adopt(childComponent);
- }
- } else {
- widgetToComponentContainer.put(childComponent.getWidget(),
- childComponent);
-
- // Logical attach.
- getChildren().insert(childComponent, position);
-
- // avoid inserts (they are slower than appends)
- boolean insert = true;
- if (widgetToComponentContainer.size() == position) {
- insert = false;
- }
- if (insert) {
- root.insertBefore(childComponent.getElement(), root
- .getChildNodes().getItem(position));
- } else {
- root.insertBefore(childComponent.getElement(), clearElement);
- }
- // Adopt.
- adopt(childComponent);
-
- }
-
- }
-
- protected ChildComponentContainer getComponentContainer(Widget child) {
- return widgetToComponentContainer.get(child);
- }
-
- protected boolean isDynamicWidth() {
- return dynamicWidth;
- }
-
- protected boolean isDynamicHeight() {
- return dynamicHeight;
- }
-
- private void updateMarginAndSpacingInfo(UIDL uidl) {
- if (!uidl.hasAttribute("invisible")) {
- int bitMask = uidl.getIntAttribute("margins");
- if (activeMarginsInfo.getBitMask() != bitMask) {
- activeMarginsInfo = new VMarginInfo(bitMask);
- marginsNeedsRecalculation = true;
- }
- boolean spacing = uidl.getBooleanAttribute("spacing");
- if (spacing != spacingEnabled) {
- marginsNeedsRecalculation = true;
- spacingEnabled = spacing;
- }
- }
- }
-
- private static DivElement measurement;
- private static DivElement measurement2;
- private static DivElement measurement3;
- private static DivElement helper;
-
- static {
- helper = Document.get().createDivElement();
- helper.setInnerHTML("<div style=\"position:absolute;top:0;left:0;height:0;visibility:hidden;overflow:hidden;\">"
- + "<div style=\"width:0;height:0;visibility:hidden;overflow:hidden;\">"
- + "</div></div>"
- + "<div style=\"position:absolute;height:0;overflow:hidden;\"></div>");
- NodeList<Node> childNodes = helper.getChildNodes();
- measurement = (DivElement) childNodes.getItem(0);
- measurement2 = (DivElement) measurement.getFirstChildElement();
- measurement3 = (DivElement) childNodes.getItem(1);
- }
-
- protected boolean measureMarginsAndSpacing() {
- if (!isAttached()) {
- return false;
- }
-
- // Measure spacing (actually CSS padding)
- measurement3.setClassName(STYLENAME_SPACING
- + (spacingEnabled ? "-on" : "-off"));
-
- String sn = getStylePrimaryName() + "-margin";
-
- if (activeMarginsInfo.hasTop()) {
- sn += " " + STYLENAME_MARGIN_TOP;
- }
- if (activeMarginsInfo.hasBottom()) {
- sn += " " + STYLENAME_MARGIN_BOTTOM;
- }
- if (activeMarginsInfo.hasLeft()) {
- sn += " " + STYLENAME_MARGIN_LEFT;
- }
- if (activeMarginsInfo.hasRight()) {
- sn += " " + STYLENAME_MARGIN_RIGHT;
- }
-
- // Measure top and left margins (actually CSS padding)
- measurement.setClassName(sn);
-
- root.appendChild(helper);
-
- activeSpacing.vSpacing = measurement3.getOffsetHeight();
- activeSpacing.hSpacing = measurement3.getOffsetWidth();
-
- activeMargins.setMarginTop(measurement2.getOffsetTop());
- activeMargins.setMarginLeft(measurement2.getOffsetLeft());
- activeMargins.setMarginRight(measurement.getOffsetWidth()
- - activeMargins.getMarginLeft());
- activeMargins.setMarginBottom(measurement.getOffsetHeight()
- - activeMargins.getMarginTop());
-
- // ApplicationConnection.getConsole().log("Margins: " + activeMargins);
- // ApplicationConnection.getConsole().log("Spacing: " + activeSpacing);
- // Util.alert("Margins: " + activeMargins);
- root.removeChild(helper);
-
- // apply margin
- Style style = root.getStyle();
- style.setPropertyPx("marginLeft", activeMargins.getMarginLeft());
- style.setPropertyPx("marginRight", activeMargins.getMarginRight());
- style.setPropertyPx("marginTop", activeMargins.getMarginTop());
- style.setPropertyPx("marginBottom", activeMargins.getMarginBottom());
-
- return true;
- }
-
- protected ChildComponentContainer getFirstChildComponentContainer() {
- int size = getChildren().size();
- if (size < 1) {
- return null;
- }
-
- return (ChildComponentContainer) getChildren().get(0);
- }
-
- protected void removeChildrenAfter(int pos) {
- // Remove all children after position "pos" but leave the clear element
- // in place
-
- int toRemove = getChildren().size() - pos;
- while (toRemove-- > 0) {
- /* flag to not if widget has been moved and rendered elsewhere */
- boolean relocated = false;
- ChildComponentContainer child = (ChildComponentContainer) getChildren()
- .get(pos);
- Widget widget = child.getWidget();
- if (widget == null) {
- // a rare case where child component has been relocated and
- // rendered elsewhere
- // clean widgetToComponentContainer map by iterating the correct
- // mapping
- Iterator<Widget> iterator = widgetToComponentContainer.keySet()
- .iterator();
- while (iterator.hasNext()) {
- Widget key = iterator.next();
- if (widgetToComponentContainer.get(key) == child) {
- widget = key;
- relocated = true;
- break;
- }
- }
- if (widget == null) {
- throw new NullPointerException();
- }
- }
- // ChildComponentContainer remove =
- widgetToComponentContainer.remove(widget);
- remove(child);
- if (!relocated) {
- Paintable p = (Paintable) widget;
- client.unregisterPaintable(p);
- }
- }
-
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- ChildComponentContainer componentContainer = widgetToComponentContainer
- .remove(oldComponent);
- if (componentContainer == null) {
- return;
- }
-
- componentContainer.setWidget(newComponent);
- client.unregisterPaintable((Paintable) oldComponent);
- widgetToComponentContainer.put(newComponent, componentContainer);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java
deleted file mode 100644
index 36d18306fa..0000000000
--- a/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.client.ui.layout;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-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.BorderStyle;
-import com.google.gwt.dom.client.TableElement;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VCaption;
-import com.vaadin.terminal.gwt.client.VConsole;
-import com.vaadin.terminal.gwt.client.ui.AlignmentInfo;
-
-public class ChildComponentContainer extends Panel {
-
- /**
- * Size of the container DIV excluding any margins and also excluding the
- * expansion amount (containerExpansion)
- */
- private Size contSize = new Size(0, 0);
-
- /**
- * Size of the widget inside the container DIV
- */
- private Size widgetSize = new Size(0, 0);
- /**
- * Size of the caption
- */
- private int captionRequiredWidth = 0;
- private int captionWidth = 0;
- private int captionHeight = 0;
-
- /**
- *
- * Padding added to the container when it is larger than the component.
- */
- private Size containerExpansion = new Size(0, 0);
-
- private double expandRatio;
-
- // private int containerMarginLeft = 0;
- private int containerMarginTop = 0;
-
- AlignmentInfo alignment = AlignmentInfo.TOP_LEFT;
-
- private int alignmentLeftOffsetForWidget = 0;
- private int alignmentLeftOffsetForCaption = 0;
- /**
- * Top offset for implementing alignment. Top offset is set to the container
- * DIV as it otherwise would have to be set to either the Caption or the
- * Widget depending on whether there is a caption and where the caption is
- * located.
- */
- private int alignmentTopOffset = 0;
-
- // private Margins alignmentOffset = new Margins(0, 0, 0, 0);
- private VCaption caption = null;
- private DivElement containerDIV;
- private DivElement widgetDIV;
- private Widget widget;
- private FloatSize relativeSize = null;
-
- public ChildComponentContainer(Widget widget, int orientation) {
- super();
-
- containerDIV = Document.get().createDivElement();
-
- widgetDIV = Document.get().createDivElement();
- if (BrowserInfo.get().isFF2()) {
- // Style style = widgetDIV.getStyle();
- // FF2 chokes on some floats very easily. Measuring size escpecially
- // becomes terribly slow
- TableElement tableEl = Document.get().createTableElement();
- tableEl.setInnerHTML("<tbody><tr><td><div></div></td></tr></tbody>");
- DivElement div = (DivElement) tableEl.getFirstChildElement()
- .getFirstChildElement().getFirstChildElement()
- .getFirstChildElement();
- tableEl.setCellPadding(0);
- tableEl.setCellSpacing(0);
- tableEl.setBorder(0);
- div.getStyle().setProperty("padding", "0");
-
- setElement(tableEl);
- containerDIV = div;
- } else {
- setFloat(widgetDIV, "left");
- setElement(containerDIV);
- containerDIV.getStyle().setProperty("height", "0");
- containerDIV.getStyle().setProperty("width", "0px");
- containerDIV.getStyle().setProperty("overflow", "hidden");
- }
-
- if (BrowserInfo.get().isIE()) {
- /*
- * IE requires position: relative on overflow:hidden elements if
- * they should hide position:relative elements. Without this e.g. a
- * 1000x1000 Panel inside an 500x500 OrderedLayout will not be
- * clipped but fully shown.
- */
- containerDIV.getStyle().setProperty("position", "relative");
- widgetDIV.getStyle().setProperty("position", "relative");
- }
-
- containerDIV.appendChild(widgetDIV);
-
- setOrientation(orientation);
-
- setWidget(widget);
-
- }
-
- public void setWidget(Widget w) {
- // Validate
- if (w == widget) {
- return;
- }
-
- // Detach new child.
- if (w != null) {
- w.removeFromParent();
- }
-
- // Remove old child.
- if (widget != null) {
- remove(widget);
- }
-
- // Logical attach.
- widget = w;
-
- if (w != null) {
- // Physical attach.
- widgetDIV.appendChild(widget.getElement());
- adopt(w);
- }
- }
-
- private static void setFloat(Element div, String floatString) {
- if (BrowserInfo.get().isIE()) {
- div.getStyle().setProperty("styleFloat", floatString);
- // IE requires display:inline for margin-left to work together
- // with float:left
- if (floatString.equals("left")) {
- div.getStyle().setProperty("display", "inline");
- } else {
- div.getStyle().setProperty("display", "block");
- }
-
- } else {
- div.getStyle().setProperty("cssFloat", floatString);
- }
- }
-
- public void setOrientation(int orientation) {
- if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
- setFloat(getElement(), "left");
- } else {
- setFloat(getElement(), "");
- }
- setHeight("0px");
- // setWidth("0px");
- contSize.setHeight(0);
- contSize.setWidth(0);
- // containerMarginLeft = 0;
- containerMarginTop = 0;
- containerDIV.getStyle().setProperty("paddingLeft", "0");
- containerDIV.getStyle().setProperty("paddingTop", "0");
-
- containerExpansion.setHeight(0);
- containerExpansion.setWidth(0);
-
- // Clear old alignments
- clearAlignments();
-
- }
-
- public void renderChild(UIDL childUIDL, ApplicationConnection client,
- int fixedWidth) {
- /*
- * Must remove width specification from container before rendering to
- * allow components to grow in horizontal direction.
- *
- * For fixed width layouts we specify the width directly so that height
- * is automatically calculated correctly (e.g. for Labels).
- */
- /*
- * This should no longer be needed (after #2563) as all components are
- * such that they can be rendered inside a 0x0 DIV.
- *
- * The exception seems to be complex components (Tree and Table) on
- * Opera (#3444).
- */
- if (fixedWidth < 0 && BrowserInfo.get().isOpera()) {
- setUnlimitedContainerWidth();
- }
- ((Paintable) widget).updateFromUIDL(childUIDL, client);
- }
-
- public void setUnlimitedContainerWidth() {
- setLimitedContainerWidth(1000000);
- }
-
- public void setLimitedContainerWidth(int width) {
- containerDIV.getStyle().setProperty("width", width + "px");
- }
-
- public void updateWidgetSize() {
- /*
- * Widget wrapper includes margin which the widget offsetWidth/Height
- * does not include
- */
- int w = Util.getRequiredWidth(widgetDIV);
-
- // IE7 ignores the width of the content if there's a border (#3915)
- if (BrowserInfo.get().isIE7()) {
- // Also read the inner width of the target element
- int clientWidth = widget.getElement().getClientWidth();
-
- // If the widths are different, there might be a border involved and
- // then the width should be calculated without borders
- if (w != clientWidth) {
- // Remember old border style and remove current border
- Style style = widget.getElement().getStyle();
- String oldBorderStyle = style.getBorderStyle();
- style.setBorderStyle(BorderStyle.NONE);
-
- // Calculate width without borders
- int newWidth = Util.getRequiredWidth(widgetDIV);
-
- // Restore old border style
- style.setProperty("borderStyle", oldBorderStyle);
-
- // Borders triggered the bug if the element is wider without
- // borders
- if (newWidth > w) {
- // Use new measured width + the border width calculated as
- // the difference between previous inner and outer widths
- w = newWidth + (w - clientWidth);
- }
- }
- }
-
- int h = Util.getRequiredHeight(widgetDIV);
-
- widgetSize.setWidth(w);
- widgetSize.setHeight(h);
-
- // ApplicationConnection.getConsole().log(
- // Util.getSimpleName(widget) + " size is " + w + "," + h);
-
- }
-
- public void setMarginLeft(int marginLeft) {
- // containerMarginLeft = marginLeft;
- containerDIV.getStyle().setPropertyPx("paddingLeft", marginLeft);
- }
-
- public void setMarginTop(int marginTop) {
- containerMarginTop = marginTop;
- containerDIV.getStyle().setPropertyPx("paddingTop",
- marginTop + alignmentTopOffset);
-
- updateContainerDOMSize();
- }
-
- public void updateAlignments(int parentWidth, int parentHeight) {
- if (parentHeight == -1) {
- parentHeight = contSize.getHeight();
- }
- if (parentWidth == -1) {
- parentWidth = contSize.getWidth();
- }
-
- alignmentTopOffset = calculateVerticalAlignmentTopOffset(parentHeight);
-
- calculateHorizontalAlignment(parentWidth);
-
- applyAlignments();
-
- }
-
- private void applyAlignments() {
-
- // Update top margin to take alignment into account
- setMarginTop(containerMarginTop);
-
- if (caption != null) {
- caption.getElement().getStyle()
- .setPropertyPx("marginLeft", alignmentLeftOffsetForCaption);
- }
- widgetDIV.getStyle().setPropertyPx("marginLeft",
- alignmentLeftOffsetForWidget);
- }
-
- public int getCaptionRequiredWidth() {
- if (caption == null) {
- return 0;
- }
-
- return captionRequiredWidth;
- }
-
- public int getCaptionWidth() {
- if (caption == null) {
- return 0;
- }
-
- return captionWidth;
- }
-
- public int getCaptionHeight() {
- if (caption == null) {
- return 0;
- }
-
- return captionHeight;
- }
-
- public int getCaptionWidthAfterComponent() {
- if (caption == null || !caption.shouldBePlacedAfterComponent()) {
- return 0;
- }
-
- return getCaptionWidth();
- }
-
- public int getCaptionHeightAboveComponent() {
- if (caption == null || caption.shouldBePlacedAfterComponent()) {
- return 0;
- }
-
- return getCaptionHeight();
- }
-
- private int calculateVerticalAlignmentTopOffset(int emptySpace) {
- if (alignment.isTop()) {
- return 0;
- }
-
- if (caption != null) {
- if (caption.shouldBePlacedAfterComponent()) {
- /*
- * Take into account the rare case that the caption on the right
- * side of the component AND is higher than the component
- */
- emptySpace -= Math.max(widgetSize.getHeight(),
- caption.getHeight());
- } else {
- emptySpace -= widgetSize.getHeight();
- emptySpace -= getCaptionHeight();
- }
- } else {
- /*
- * There is no caption and thus we do not need to take anything but
- * the widget into account
- */
- emptySpace -= widgetSize.getHeight();
- }
-
- int top = 0;
- if (alignment.isVerticalCenter()) {
- top = emptySpace / 2;
- } else if (alignment.isBottom()) {
- top = emptySpace;
- }
-
- if (top < 0) {
- top = 0;
- }
- return top;
- }
-
- private void calculateHorizontalAlignment(int emptySpace) {
- alignmentLeftOffsetForCaption = 0;
- alignmentLeftOffsetForWidget = 0;
-
- if (alignment.isLeft()) {
- return;
- }
-
- int captionSpace = emptySpace;
- int widgetSpace = emptySpace;
-
- if (caption != null) {
- // There is a caption
- if (caption.shouldBePlacedAfterComponent()) {
- /*
- * The caption is after component. In this case the caption
- * needs no alignment.
- */
- captionSpace = 0;
- widgetSpace -= widgetSize.getWidth();
- widgetSpace -= getCaptionWidth();
- } else {
- /*
- * The caption is above the component. Caption and widget needs
- * separate alignment offsets.
- */
- widgetSpace -= widgetSize.getWidth();
- captionSpace -= getCaptionWidth();
- }
- } else {
- /*
- * There is no caption and thus we do not need to take anything but
- * the widget into account
- */
- captionSpace = 0;
- widgetSpace -= widgetSize.getWidth();
- }
-
- if (alignment.isHorizontalCenter()) {
- alignmentLeftOffsetForCaption = captionSpace / 2;
- alignmentLeftOffsetForWidget = widgetSpace / 2;
- } else if (alignment.isRight()) {
- alignmentLeftOffsetForCaption = captionSpace;
- alignmentLeftOffsetForWidget = widgetSpace;
- }
-
- if (alignmentLeftOffsetForCaption < 0) {
- alignmentLeftOffsetForCaption = 0;
- }
- if (alignmentLeftOffsetForWidget < 0) {
- alignmentLeftOffsetForWidget = 0;
- }
-
- }
-
- public void setAlignment(AlignmentInfo alignmentInfo) {
- alignment = alignmentInfo;
- }
-
- public Size getWidgetSize() {
- return widgetSize;
- }
-
- public void updateCaption(UIDL uidl, ApplicationConnection client) {
- if (VCaption.isNeeded(uidl)) {
- // We need a caption
-
- VCaption newCaption = caption;
-
- if (newCaption == null) {
- newCaption = new VCaption((Paintable) widget, client);
- // Set initial height to avoid Safari flicker
- newCaption.setHeight("18px");
- // newCaption.setHeight(newCaption.getHeight()); // This might
- // be better... ??
- if (BrowserInfo.get().isIE()) {
- /*
- * Must attach caption here so IE sends an immediate onload
- * event for images coming from the cache
- */
- setCaption(newCaption);
- }
- }
-
- boolean positionChanged = newCaption.updateCaption(uidl);
-
- if (newCaption != caption || positionChanged) {
- setCaption(newCaption);
- }
-
- } else {
- // Caption is not needed
- if (caption != null) {
- remove(caption);
- }
-
- }
-
- updateCaptionSize();
-
- if (relativeSize == null) {
- /*
- * relativeSize may be null if component is updated via independent
- * update, after it has initially been hidden. See #4608
- *
- * It might also change in which case there would be similar issues.
- *
- * Yes, it is an ugly hack. Don't come telling me about it.
- */
- setRelativeSize(Util.parseRelativeSize(uidl));
- }
- }
-
- public void updateCaptionSize() {
- captionWidth = 0;
- captionHeight = 0;
-
- if (caption != null) {
- captionWidth = caption.getRenderedWidth();
- captionHeight = caption.getHeight();
- captionRequiredWidth = caption.getRequiredWidth();
-
- /*
- * ApplicationConnection.getConsole().log(
- * "Caption rendered width: " + captionWidth +
- * ", caption required width: " + captionRequiredWidth +
- * ", caption height: " + captionHeight);
- */
- }
-
- }
-
- private void setCaption(VCaption newCaption) {
- // Validate
- // if (newCaption == caption) {
- // return;
- // }
-
- // Detach new child.
- if (newCaption != null) {
- newCaption.removeFromParent();
- }
-
- // Remove old child.
- if (caption != null && newCaption != caption) {
- remove(caption);
- }
-
- // Logical attach.
- caption = newCaption;
-
- if (caption != null) {
- // Physical attach.
- if (caption.shouldBePlacedAfterComponent()) {
- Util.setFloat(caption.getElement(), "left");
- containerDIV.appendChild(caption.getElement());
- } else {
- Util.setFloat(caption.getElement(), "");
- containerDIV.insertBefore(caption.getElement(), widgetDIV);
- }
-
- adopt(caption);
- }
-
- }
-
- @Override
- public boolean remove(Widget child) {
- // Validate
- if (child != caption && child != widget) {
- return false;
- }
-
- // Orphan
- orphan(child);
-
- // Physical && Logical Detach
- if (child == caption) {
- containerDIV.removeChild(child.getElement());
- caption = null;
- } else {
- widgetDIV.removeChild(child.getElement());
- widget = null;
- }
-
- return true;
- }
-
- public Iterator<Widget> iterator() {
- return new ChildComponentContainerIterator<Widget>();
- }
-
- public class ChildComponentContainerIterator<T> implements Iterator<Widget> {
- private int id = 0;
-
- public boolean hasNext() {
- return (id < size());
- }
-
- public Widget next() {
- Widget w = get(id);
- id++;
- return w;
- }
-
- private Widget get(int i) {
- if (i == 0) {
- if (widget != null) {
- return widget;
- } else if (caption != null) {
- return caption;
- } else {
- throw new NoSuchElementException();
- }
- } else if (i == 1) {
- if (widget != null && caption != null) {
- return caption;
- } else {
- throw new NoSuchElementException();
- }
- } else {
- throw new NoSuchElementException();
- }
- }
-
- public void remove() {
- int toRemove = id - 1;
- if (toRemove == 0) {
- if (widget != null) {
- ChildComponentContainer.this.remove(widget);
- } else if (caption != null) {
- ChildComponentContainer.this.remove(caption);
- } else {
- throw new IllegalStateException();
- }
-
- } else if (toRemove == 1) {
- if (widget != null && caption != null) {
- ChildComponentContainer.this.remove(caption);
- } else {
- throw new IllegalStateException();
- }
- } else {
- throw new IllegalStateException();
- }
-
- id--;
- }
- }
-
- public int size() {
- if (widget != null) {
- if (caption != null) {
- return 2;
- } else {
- return 1;
- }
- } else {
- if (caption != null) {
- return 1;
- } else {
- return 0;
- }
- }
- }
-
- public Widget getWidget() {
- return widget;
- }
-
- /**
- * Return true if the size of the widget has been specified in the selected
- * orientation.
- *
- * @return
- */
- public boolean widgetHasSizeSpecified(int orientation) {
- String size;
- if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
- size = widget.getElement().getStyle().getProperty("width");
- } else {
- size = widget.getElement().getStyle().getProperty("height");
- }
- return (size != null && !size.equals(""));
- }
-
- public boolean isComponentRelativeSized(int orientation) {
- if (relativeSize == null) {
- return false;
- }
- if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
- return relativeSize.getWidth() >= 0;
- } else {
- return relativeSize.getHeight() >= 0;
- }
- }
-
- public void setRelativeSize(FloatSize relativeSize) {
- this.relativeSize = relativeSize;
- }
-
- public Size getContSize() {
- return contSize;
- }
-
- public void clearAlignments() {
- alignmentLeftOffsetForCaption = 0;
- alignmentLeftOffsetForWidget = 0;
- alignmentTopOffset = 0;
- applyAlignments();
-
- }
-
- /**
- * Sets the normalized expand ratio of this slot. The fraction that this
- * slot will use of "excess space".
- *
- * @param expandRatio
- */
- public void setNormalizedExpandRatio(double expandRatio) {
- this.expandRatio = expandRatio;
- }
-
- public int expand(int orientation, int spaceForExpansion) {
- int expansionAmount = (int) (spaceForExpansion * expandRatio);
-
- if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
- // HORIZONTAL
- containerExpansion.setWidth(expansionAmount);
- } else {
- // VERTICAL
- containerExpansion.setHeight(expansionAmount);
- }
-
- return expansionAmount;
- }
-
- public void expandExtra(int orientation, int extra) {
- if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
- // HORIZONTAL
- containerExpansion.setWidth(containerExpansion.getWidth() + extra);
- } else {
- // VERTICAL
- containerExpansion
- .setHeight(containerExpansion.getHeight() + extra);
- }
-
- }
-
- public void setContainerSize(int widgetAndCaptionWidth,
- int widgetAndCaptionHeight) {
-
- int containerWidth = widgetAndCaptionWidth;
- containerWidth += containerExpansion.getWidth();
-
- int containerHeight = widgetAndCaptionHeight;
- containerHeight += containerExpansion.getHeight();
-
- // ApplicationConnection.getConsole().log(
- // "Setting container size for " + Util.getSimpleName(widget)
- // + " to " + containerWidth + "," + containerHeight);
-
- if (containerWidth < 0) {
- VConsole.log("containerWidth should never be negative: "
- + containerWidth);
- containerWidth = 0;
- }
- if (containerHeight < 0) {
- VConsole.log("containerHeight should never be negative: "
- + containerHeight);
- containerHeight = 0;
- }
-
- contSize.setWidth(containerWidth);
- contSize.setHeight(containerHeight);
-
- updateContainerDOMSize();
- }
-
- public void updateContainerDOMSize() {
- int width = contSize.getWidth();
- int height = contSize.getHeight() - alignmentTopOffset;
- if (width < 0) {
- width = 0;
- }
- if (height < 0) {
- height = 0;
- }
-
- setWidth(width + "px");
- setHeight(height + "px");
-
- // Also update caption max width
- if (caption != null) {
- if (caption.shouldBePlacedAfterComponent()) {
- caption.setMaxWidth(captionWidth);
- } else {
- caption.setMaxWidth(width);
- }
- captionWidth = caption.getRenderedWidth();
-
- // Remove initial height
- caption.setHeight("");
- }
-
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java
new file mode 100644
index 0000000000..d479e8da9d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java
@@ -0,0 +1,99 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
+
+public class ComponentConnectorLayoutSlot extends VLayoutSlot {
+
+ final ComponentConnector child;
+ final ManagedLayout layout;
+
+ public ComponentConnectorLayoutSlot(String baseClassName,
+ ComponentConnector child, ManagedLayout layout) {
+ super(baseClassName, child.getWidget());
+ this.child = child;
+ this.layout = layout;
+ }
+
+ public ComponentConnector getChild() {
+ return child;
+ }
+
+ @Override
+ protected int getCaptionHeight() {
+ VCaption caption = getCaption();
+ return caption != null ? getLayoutManager().getOuterHeight(
+ caption.getElement()) : 0;
+ }
+
+ @Override
+ protected int getCaptionWidth() {
+ VCaption caption = getCaption();
+ return caption != null ? getLayoutManager().getOuterWidth(
+ caption.getElement()) : 0;
+ }
+
+ public LayoutManager getLayoutManager() {
+ return layout.getLayoutManager();
+ }
+
+ @Override
+ public void setCaption(VCaption caption) {
+ VCaption oldCaption = getCaption();
+ if (oldCaption != null) {
+ getLayoutManager().unregisterDependency(layout,
+ oldCaption.getElement());
+ }
+ super.setCaption(caption);
+ if (caption != null) {
+ getLayoutManager().registerDependency(
+ (ManagedLayout) child.getParent(), caption.getElement());
+ }
+ }
+
+ @Override
+ protected void reportActualRelativeHeight(int allocatedHeight) {
+ getLayoutManager().reportOuterHeight(child, allocatedHeight);
+ }
+
+ @Override
+ protected void reportActualRelativeWidth(int allocatedWidth) {
+ getLayoutManager().reportOuterWidth(child, allocatedWidth);
+ }
+
+ @Override
+ public int getWidgetHeight() {
+ return getLayoutManager()
+ .getOuterHeight(child.getWidget().getElement());
+ }
+
+ @Override
+ public int getWidgetWidth() {
+ return getLayoutManager().getOuterWidth(child.getWidget().getElement());
+ }
+
+ @Override
+ public boolean isUndefinedHeight() {
+ return child.isUndefinedHeight();
+ }
+
+ @Override
+ public boolean isUndefinedWidth() {
+ return child.isUndefinedWidth();
+ }
+
+ @Override
+ public boolean isRelativeHeight() {
+ return child.isRelativeHeight();
+ }
+
+ @Override
+ public boolean isRelativeWidth() {
+ return child.isRelativeWidth();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java
new file mode 100644
index 0000000000..a519f5db87
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+import com.google.gwt.dom.client.Element;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+
+public class ElementResizeEvent {
+ private final Element element;
+ private final LayoutManager layoutManager;
+
+ public ElementResizeEvent(LayoutManager layoutManager, Element element) {
+ this.layoutManager = layoutManager;
+ this.element = element;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public LayoutManager getLayoutManager() {
+ return layoutManager;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java
new file mode 100644
index 0000000000..d6d3de48b8
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java
@@ -0,0 +1,9 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+public interface ElementResizeListener {
+ public void onElementResize(ElementResizeEvent e);
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java
new file mode 100644
index 0000000000..1ca8933ff2
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java
@@ -0,0 +1,521 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
+
+public class LayoutDependencyTree {
+ private class LayoutDependency {
+ private final ComponentConnector connector;
+ private final int direction;
+
+ private boolean needsLayout = false;
+ private boolean needsMeasure = false;
+
+ private boolean scrollingParentCached = false;
+ private ComponentConnector scrollingBoundary = null;
+
+ private Set<ComponentConnector> measureBlockers = new HashSet<ComponentConnector>();
+ private Set<ComponentConnector> layoutBlockers = new HashSet<ComponentConnector>();
+
+ public LayoutDependency(ComponentConnector connector, int direction) {
+ this.connector = connector;
+ this.direction = direction;
+ }
+
+ private void addLayoutBlocker(ComponentConnector blocker) {
+ boolean blockerAdded = layoutBlockers.add(blocker);
+ if (blockerAdded && layoutBlockers.size() == 1) {
+ if (needsLayout) {
+ getLayoutQueue(direction).remove(connector);
+ } else {
+ // Propagation already done if needsLayout is set
+ propagatePotentialLayout();
+ }
+ }
+ }
+
+ private void removeLayoutBlocker(ComponentConnector blocker) {
+ boolean removed = layoutBlockers.remove(blocker);
+ if (removed && layoutBlockers.isEmpty()) {
+ if (needsLayout) {
+ getLayoutQueue(direction).add((ManagedLayout) connector);
+ } else {
+ propagateNoUpcomingLayout();
+ }
+ }
+ }
+
+ private void addMeasureBlocker(ComponentConnector blocker) {
+ boolean blockerAdded = measureBlockers.add(blocker);
+ if (blockerAdded && measureBlockers.size() == 1) {
+ if (needsMeasure) {
+ getMeasureQueue(direction).remove(connector);
+ } else {
+ propagatePotentialResize();
+ }
+ }
+ }
+
+ private void removeMeasureBlocker(ComponentConnector blocker) {
+ boolean removed = measureBlockers.remove(blocker);
+ if (removed && measureBlockers.isEmpty()) {
+ if (needsMeasure) {
+ getMeasureQueue(direction).add(connector);
+ } else {
+ propagateNoUpcomingResize();
+ }
+ }
+ }
+
+ public void setNeedsMeasure(boolean needsMeasure) {
+ if (needsMeasure && !this.needsMeasure) {
+ // If enabling needsMeasure
+ this.needsMeasure = needsMeasure;
+
+ if (measureBlockers.isEmpty()) {
+ // Add to queue if there are no blockers
+ getMeasureQueue(direction).add(connector);
+ // Only need to propagate if not already propagated when
+ // setting blockers
+ propagatePotentialResize();
+ }
+ } else if (!needsMeasure && this.needsMeasure
+ && measureBlockers.isEmpty()) {
+ // Only disable if there are no blockers (elements gets measured
+ // in both directions even if there is a blocker in one
+ // direction)
+ this.needsMeasure = needsMeasure;
+ getMeasureQueue(direction).remove(connector);
+ propagateNoUpcomingResize();
+ }
+ }
+
+ public void setNeedsLayout(boolean needsLayout) {
+ if (!(connector instanceof ManagedLayout)) {
+ throw new IllegalStateException(
+ "Only managed layouts can need layout, layout attempted for "
+ + Util.getConnectorString(connector));
+ }
+ if (needsLayout && !this.needsLayout) {
+ // If enabling needsLayout
+ this.needsLayout = needsLayout;
+
+ if (layoutBlockers.isEmpty()) {
+ // Add to queue if there are no blockers
+ getLayoutQueue(direction).add((ManagedLayout) connector);
+ // Only need to propagate if not already propagated when
+ // setting blockers
+ propagatePotentialLayout();
+ }
+ } else if (!needsLayout && this.needsLayout
+ && layoutBlockers.isEmpty()) {
+ // Only disable if there are no layout blockers
+ // (SimpleManagedLayout gets layouted in both directions
+ // even if there is a blocker in one direction)
+ this.needsLayout = needsLayout;
+ getLayoutQueue(direction).remove(connector);
+ propagateNoUpcomingLayout();
+ }
+ }
+
+ private void propagatePotentialResize() {
+ for (ComponentConnector needsSize : getNeedsSizeForLayout()) {
+ LayoutDependency layoutDependency = getDependency(needsSize,
+ direction);
+ layoutDependency.addLayoutBlocker(connector);
+ }
+ }
+
+ private Collection<ComponentConnector> getNeedsSizeForLayout() {
+ // Find all connectors that need the size of this connector for
+ // layouting
+
+ // Parent needs size if it isn't relative?
+ // Connector itself needs size if it isn't undefined?
+ // Children doesn't care?
+
+ ArrayList<ComponentConnector> needsSize = new ArrayList<ComponentConnector>();
+
+ if (!isUndefinedInDirection(connector, direction)) {
+ needsSize.add(connector);
+ }
+ if (!isRelativeInDirection(connector, direction)) {
+ ComponentConnector parent = connector.getParent();
+ if (parent != null) {
+ needsSize.add(parent);
+ }
+ }
+
+ return needsSize;
+ }
+
+ private void propagateNoUpcomingResize() {
+ for (ComponentConnector mightNeedLayout : getNeedsSizeForLayout()) {
+ LayoutDependency layoutDependency = getDependency(
+ mightNeedLayout, direction);
+ layoutDependency.removeLayoutBlocker(connector);
+ }
+ }
+
+ private void propagatePotentialLayout() {
+ for (ComponentConnector sizeMightChange : getResizedByLayout()) {
+ LayoutDependency layoutDependency = getDependency(
+ sizeMightChange, direction);
+ layoutDependency.addMeasureBlocker(connector);
+ }
+ }
+
+ private Collection<ComponentConnector> getResizedByLayout() {
+ // Components that might get resized by a layout of this component
+
+ // Parent never resized
+ // Connector itself resized if undefined
+ // Children resized if relative
+
+ ArrayList<ComponentConnector> resized = new ArrayList<ComponentConnector>();
+ if (isUndefinedInDirection(connector, direction)) {
+ resized.add(connector);
+ }
+
+ if (connector instanceof ComponentContainerConnector) {
+ ComponentContainerConnector container = (ComponentContainerConnector) connector;
+ for (ComponentConnector child : container.getChildren()) {
+ if (isRelativeInDirection(child, direction)) {
+ resized.add(child);
+ }
+ }
+ }
+
+ return resized;
+ }
+
+ private void propagateNoUpcomingLayout() {
+ for (ComponentConnector sizeMightChange : getResizedByLayout()) {
+ LayoutDependency layoutDependency = getDependency(
+ sizeMightChange, direction);
+ layoutDependency.removeMeasureBlocker(connector);
+ }
+ }
+
+ public void markSizeAsChanged() {
+ // When the size has changed, all that use that size should be
+ // layouted
+ for (ComponentConnector connector : getNeedsSizeForLayout()) {
+ LayoutDependency layoutDependency = getDependency(connector,
+ direction);
+ if (connector instanceof ManagedLayout) {
+ layoutDependency.setNeedsLayout(true);
+ } else {
+ // Should simulate setNeedsLayout(true) + markAsLayouted ->
+ // propagate needs measure
+ layoutDependency.propagatePostLayoutMeasure();
+ }
+ }
+
+ // Should also go through the hierarchy to discover appeared or
+ // disappeared scrollbars
+ ComponentConnector scrollingBoundary = getScrollingBoundary(connector);
+ if (scrollingBoundary != null) {
+ getDependency(scrollingBoundary, getOppositeDirection())
+ .setNeedsMeasure(true);
+ }
+
+ }
+
+ /**
+ * Go up the hierarchy to find a component whose size might have changed
+ * in the other direction because changes to this component causes
+ * scrollbars to appear or disappear.
+ *
+ * @return
+ */
+ private LayoutDependency findPotentiallyChangedScrollbar() {
+ ComponentConnector currentConnector = connector;
+ while (true) {
+ ComponentContainerConnector parent = currentConnector
+ .getParent();
+ if (parent == null) {
+ return null;
+ }
+ if (parent instanceof MayScrollChildren) {
+ return getDependency(currentConnector,
+ getOppositeDirection());
+ }
+ currentConnector = parent;
+ }
+ }
+
+ private int getOppositeDirection() {
+ return direction == HORIZONTAL ? VERTICAL : HORIZONTAL;
+ }
+
+ public void markAsLayouted() {
+ if (!layoutBlockers.isEmpty()) {
+ // Don't do anything if there are layout blockers (SimpleLayout
+ // gets layouted in both directions even if one direction is
+ // blocked)
+ return;
+ }
+ setNeedsLayout(false);
+ propagatePostLayoutMeasure();
+ }
+
+ private void propagatePostLayoutMeasure() {
+ for (ComponentConnector resized : getResizedByLayout()) {
+ LayoutDependency layoutDependency = getDependency(resized,
+ direction);
+ layoutDependency.setNeedsMeasure(true);
+ }
+
+ // Special case for e.g. wrapping texts
+ if (direction == HORIZONTAL && !connector.isUndefinedWidth()
+ && connector.isUndefinedHeight()) {
+ LayoutDependency dependency = getDependency(connector, VERTICAL);
+ dependency.setNeedsMeasure(true);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String s = getCompactConnectorString(connector) + "\n";
+ if (direction == VERTICAL) {
+ s += "Vertical";
+ } else {
+ s += "Horizontal";
+ }
+ ComponentState state = connector.getState();
+ s += " sizing: "
+ + getSizeDefinition(direction == VERTICAL ? state
+ .getHeight() : state.getWidth()) + "\n";
+
+ if (needsLayout) {
+ s += "Needs layout\n";
+ }
+ if (getLayoutQueue(direction).contains(connector)) {
+ s += "In layout queue\n";
+ }
+ s += "Layout blockers: " + blockersToString(layoutBlockers) + "\n";
+
+ if (needsMeasure) {
+ s += "Needs measure\n";
+ }
+ if (getMeasureQueue(direction).contains(connector)) {
+ s += "In measure queue\n";
+ }
+ s += "Measure blockers: " + blockersToString(measureBlockers);
+
+ return s;
+ }
+
+ public boolean noMoreChangesExpected() {
+ return !needsLayout && !needsMeasure && layoutBlockers.isEmpty()
+ && measureBlockers.isEmpty();
+ }
+
+ }
+
+ private static final int HORIZONTAL = 0;
+ private static final int VERTICAL = 1;
+
+ private final Map<?, ?>[] dependenciesInDirection = new Map<?, ?>[] {
+ new HashMap<ComponentConnector, LayoutDependency>(),
+ new HashMap<ComponentConnector, LayoutDependency>() };
+
+ private final Collection<?>[] measureQueueInDirection = new HashSet<?>[] {
+ new HashSet<ComponentConnector>(),
+ new HashSet<ComponentConnector>() };
+
+ private final Collection<?>[] layoutQueueInDirection = new HashSet<?>[] {
+ new HashSet<ComponentConnector>(),
+ new HashSet<ComponentConnector>() };
+
+ public void setNeedsMeasure(ComponentConnector connector,
+ boolean needsMeasure) {
+ setNeedsHorizontalMeasure(connector, needsMeasure);
+ setNeedsVerticalMeasure(connector, needsMeasure);
+ }
+
+ public void setNeedsHorizontalMeasure(ComponentConnector connector,
+ boolean needsMeasure) {
+ LayoutDependency dependency = getDependency(connector, HORIZONTAL);
+ dependency.setNeedsMeasure(needsMeasure);
+ }
+
+ public void setNeedsVerticalMeasure(ComponentConnector connector,
+ boolean needsMeasure) {
+ LayoutDependency dependency = getDependency(connector, VERTICAL);
+ dependency.setNeedsMeasure(needsMeasure);
+ }
+
+ private LayoutDependency getDependency(ComponentConnector connector,
+ int direction) {
+ @SuppressWarnings("unchecked")
+ Map<ComponentConnector, LayoutDependency> dependencies = (Map<ComponentConnector, LayoutDependency>) dependenciesInDirection[direction];
+ LayoutDependency dependency = dependencies.get(connector);
+ if (dependency == null) {
+ dependency = new LayoutDependency(connector, direction);
+ dependencies.put(connector, dependency);
+ }
+ return dependency;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection<ManagedLayout> getLayoutQueue(int direction) {
+ return (Collection<ManagedLayout>) layoutQueueInDirection[direction];
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection<ComponentConnector> getMeasureQueue(int direction) {
+ return (Collection<ComponentConnector>) measureQueueInDirection[direction];
+ }
+
+ public void setNeedsHorizontalLayout(ManagedLayout layout,
+ boolean needsLayout) {
+ LayoutDependency dependency = getDependency(layout, HORIZONTAL);
+ dependency.setNeedsLayout(needsLayout);
+ }
+
+ public void setNeedsVerticalLayout(ManagedLayout layout, boolean needsLayout) {
+ LayoutDependency dependency = getDependency(layout, VERTICAL);
+ dependency.setNeedsLayout(needsLayout);
+ }
+
+ public void markAsHorizontallyLayouted(ManagedLayout layout) {
+ LayoutDependency dependency = getDependency(layout, HORIZONTAL);
+ dependency.markAsLayouted();
+ }
+
+ public void markAsVerticallyLayouted(ManagedLayout layout) {
+ LayoutDependency dependency = getDependency(layout, VERTICAL);
+ dependency.markAsLayouted();
+ }
+
+ public void markHeightAsChanged(ComponentConnector connector) {
+ LayoutDependency dependency = getDependency(connector, VERTICAL);
+ dependency.markSizeAsChanged();
+ }
+
+ public void markWidthAsChanged(ComponentConnector connector) {
+ LayoutDependency dependency = getDependency(connector, HORIZONTAL);
+ dependency.markSizeAsChanged();
+ }
+
+ private static boolean isRelativeInDirection(ComponentConnector connector,
+ int direction) {
+ if (direction == HORIZONTAL) {
+ return connector.isRelativeWidth();
+ } else {
+ return connector.isRelativeHeight();
+ }
+ }
+
+ private static boolean isUndefinedInDirection(ComponentConnector connector,
+ int direction) {
+ if (direction == VERTICAL) {
+ return connector.isUndefinedHeight();
+ } else {
+ return connector.isUndefinedWidth();
+ }
+ }
+
+ private static String getCompactConnectorString(ComponentConnector connector) {
+ return Util.getSimpleName(connector) + " ("
+ + connector.getConnectorId() + ")";
+ }
+
+ private static String getSizeDefinition(String size) {
+ if (size == null || size.length() == 0) {
+ return "undefined";
+ } else if (size.endsWith("%")) {
+ return "relative";
+ } else {
+ return "fixed";
+ }
+ }
+
+ private static String blockersToString(
+ Collection<ComponentConnector> blockers) {
+ StringBuilder b = new StringBuilder("[");
+ for (ComponentConnector blocker : blockers) {
+ if (b.length() != 1) {
+ b.append(", ");
+ }
+ b.append(getCompactConnectorString(blocker));
+ }
+ b.append(']');
+ return b.toString();
+ }
+
+ public boolean hasConnectorsToMeasure() {
+ return !measureQueueInDirection[HORIZONTAL].isEmpty()
+ || !measureQueueInDirection[VERTICAL].isEmpty();
+ }
+
+ public boolean hasHorizontalConnectorToLayout() {
+ return !getLayoutQueue(HORIZONTAL).isEmpty();
+ }
+
+ public boolean hasVerticaConnectorToLayout() {
+ return !getLayoutQueue(VERTICAL).isEmpty();
+ }
+
+ public ManagedLayout[] getHorizontalLayoutTargets() {
+ Collection<ManagedLayout> queue = getLayoutQueue(HORIZONTAL);
+ return queue.toArray(new ManagedLayout[queue.size()]);
+ }
+
+ public ManagedLayout[] getVerticalLayoutTargets() {
+ Collection<ManagedLayout> queue = getLayoutQueue(VERTICAL);
+ return queue.toArray(new ManagedLayout[queue.size()]);
+ }
+
+ public Collection<ComponentConnector> getMeasureTargets() {
+ Collection<ComponentConnector> measureTargets = new HashSet<ComponentConnector>(
+ getMeasureQueue(HORIZONTAL));
+ measureTargets.addAll(getMeasureQueue(VERTICAL));
+ return measureTargets;
+ }
+
+ public void logDependencyStatus(ComponentConnector connector) {
+ VConsole.log("====");
+ VConsole.log(getDependency(connector, HORIZONTAL).toString());
+ VConsole.log(getDependency(connector, VERTICAL).toString());
+ }
+
+ public boolean noMoreChangesExpected(ComponentConnector connector) {
+ return getDependency(connector, HORIZONTAL).noMoreChangesExpected()
+ && getDependency(connector, VERTICAL).noMoreChangesExpected();
+ }
+
+ public ComponentConnector getScrollingBoundary(ComponentConnector connector) {
+ LayoutDependency dependency = getDependency(connector, HORIZONTAL);
+ if (!dependency.scrollingParentCached) {
+ ComponentContainerConnector parent = dependency.connector
+ .getParent();
+ if (parent instanceof MayScrollChildren) {
+ dependency.scrollingBoundary = connector;
+ } else if (parent != null) {
+ dependency.scrollingBoundary = getScrollingBoundary(parent);
+ } else {
+ // No scrolling parent
+ }
+
+ dependency.scrollingParentCached = true;
+ }
+ return dependency.scrollingBoundary;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java b/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java
new file mode 100644
index 0000000000..62c9937c4c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java
@@ -0,0 +1,10 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
+
+public interface MayScrollChildren extends ComponentContainerConnector {
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java b/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java
new file mode 100644
index 0000000000..034fe35649
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java
@@ -0,0 +1,287 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.layout;
+
+import com.google.gwt.dom.client.Document;
+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.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.AlignmentInfo;
+
+public abstract class VLayoutSlot {
+
+ private final Element wrapper = Document.get().createDivElement().cast();
+
+ private AlignmentInfo alignment;
+ private VCaption caption;
+ private final Widget widget;
+
+ private double expandRatio;
+
+ public VLayoutSlot(String baseClassName, Widget widget) {
+ this.widget = widget;
+
+ wrapper.setClassName(baseClassName + "-slot");
+ }
+
+ public VCaption getCaption() {
+ return caption;
+ }
+
+ public void setCaption(VCaption caption) {
+ if (this.caption != null) {
+ this.caption.removeFromParent();
+ }
+ this.caption = caption;
+ if (caption != null) {
+ // Physical attach.
+ DOM.insertBefore(wrapper, caption.getElement(), widget.getElement());
+ Style style = caption.getElement().getStyle();
+ style.setPosition(Position.ABSOLUTE);
+ style.setTop(0, Unit.PX);
+ }
+ }
+
+ public AlignmentInfo getAlignment() {
+ return alignment;
+ }
+
+ public Widget getWidget() {
+ return widget;
+ }
+
+ public void setAlignment(AlignmentInfo alignment) {
+ this.alignment = alignment;
+ }
+
+ public void positionHorizontally(double currentLocation,
+ double allocatedSpace, double marginRight) {
+ Style style = wrapper.getStyle();
+
+ double availableWidth = allocatedSpace;
+
+ VCaption caption = getCaption();
+ Style captionStyle = caption != null ? caption.getElement().getStyle()
+ : null;
+ int captionWidth = getCaptionWidth();
+
+ boolean captionAboveCompnent;
+ if (caption == null) {
+ captionAboveCompnent = false;
+ style.clearPaddingRight();
+ } else {
+ captionAboveCompnent = !caption.shouldBePlacedAfterComponent();
+ if (!captionAboveCompnent) {
+ availableWidth -= captionWidth;
+ captionStyle.clearLeft();
+ captionStyle.setRight(0, Unit.PX);
+ style.setPaddingRight(captionWidth, Unit.PX);
+ } else {
+ captionStyle.setLeft(0, Unit.PX);
+ captionStyle.clearRight();
+ style.clearPaddingRight();
+ }
+ }
+
+ if (marginRight > 0) {
+ style.setMarginRight(marginRight, Unit.PX);
+ } else {
+ style.clearMarginRight();
+ }
+
+ if (isRelativeWidth()) {
+ style.setPropertyPx("width", (int) availableWidth);
+ } else {
+ style.clearProperty("width");
+ }
+
+ double allocatedContentWidth = 0;
+ if (isRelativeWidth()) {
+ String percentWidth = getWidget().getElement().getStyle()
+ .getWidth();
+ double percentage = parsePercent(percentWidth);
+ allocatedContentWidth = availableWidth * (percentage / 100);
+ reportActualRelativeWidth(Math.round((float) allocatedContentWidth));
+ }
+
+ AlignmentInfo alignment = getAlignment();
+ if (!alignment.isLeft()) {
+ double usedWidth;
+ if (isRelativeWidth()) {
+ usedWidth = allocatedContentWidth;
+ } else {
+ usedWidth = getWidgetWidth();
+ }
+ if (alignment.isHorizontalCenter()) {
+ currentLocation += (allocatedSpace - usedWidth) / 2d;
+ if (captionAboveCompnent) {
+ captionStyle.setLeft(
+ Math.round(usedWidth - captionWidth) / 2, Unit.PX);
+ }
+ } else {
+ currentLocation += (allocatedSpace - usedWidth);
+ if (captionAboveCompnent) {
+ captionStyle.setLeft(Math.round(usedWidth - captionWidth),
+ Unit.PX);
+ }
+ }
+ } else {
+ if (captionAboveCompnent) {
+ captionStyle.setLeft(0, Unit.PX);
+ }
+ }
+
+ style.setLeft(Math.round(currentLocation), Unit.PX);
+ }
+
+ private double parsePercent(String size) {
+ return Double.parseDouble(size.replaceAll("%", ""));
+ }
+
+ public void positionVertically(double currentLocation,
+ double allocatedSpace, double marginBottom) {
+ Style style = wrapper.getStyle();
+
+ double contentHeight = allocatedSpace;
+
+ int captionHeight;
+ VCaption caption = getCaption();
+ if (caption == null || caption.shouldBePlacedAfterComponent()) {
+ style.clearPaddingTop();
+ captionHeight = 0;
+ } else {
+ captionHeight = getCaptionHeight();
+ contentHeight -= captionHeight;
+ if (contentHeight < 0) {
+ contentHeight = 0;
+ }
+ style.setPaddingTop(captionHeight, Unit.PX);
+ }
+
+ if (marginBottom > 0) {
+ style.setMarginBottom(marginBottom, Unit.PX);
+ } else {
+ style.clearMarginBottom();
+ }
+
+ if (isRelativeHeight()) {
+ style.setHeight(contentHeight, Unit.PX);
+ } else {
+ style.clearHeight();
+ }
+
+ double allocatedContentHeight = 0;
+ if (isRelativeHeight()) {
+ String height = getWidget().getElement().getStyle().getHeight();
+ double percentage = parsePercent(height);
+ allocatedContentHeight = contentHeight * (percentage / 100);
+ reportActualRelativeHeight(Math
+ .round((float) allocatedContentHeight));
+ }
+
+ AlignmentInfo alignment = getAlignment();
+ if (!alignment.isTop()) {
+ double usedHeight;
+ if (isRelativeHeight()) {
+ usedHeight = captionHeight + allocatedContentHeight;
+ } else {
+ usedHeight = getUsedHeight();
+ }
+ if (alignment.isVerticalCenter()) {
+ currentLocation += (allocatedSpace - usedHeight) / 2d;
+ } else {
+ currentLocation += (allocatedSpace - usedHeight);
+ }
+ }
+
+ style.setTop(currentLocation, Unit.PX);
+ }
+
+ protected void reportActualRelativeHeight(int allocatedHeight) {
+ // Default implementation does nothing
+ }
+
+ protected void reportActualRelativeWidth(int allocatedWidth) {
+ // Default implementation does nothing
+ }
+
+ public void positionInDirection(double currentLocation,
+ double allocatedSpace, double endingMargin, boolean isVertical) {
+ if (isVertical) {
+ positionVertically(currentLocation, allocatedSpace, endingMargin);
+ } else {
+ positionHorizontally(currentLocation, allocatedSpace, endingMargin);
+ }
+ }
+
+ public int getWidgetSizeInDirection(boolean isVertical) {
+ return isVertical ? getWidgetHeight() : getWidgetWidth();
+ }
+
+ public int getUsedWidth() {
+ int widgetWidth = getWidgetWidth();
+ if (caption == null) {
+ return widgetWidth;
+ } else if (caption.shouldBePlacedAfterComponent()) {
+ return widgetWidth + getCaptionWidth();
+ } else {
+ return Math.max(widgetWidth, getCaptionWidth());
+ }
+ }
+
+ public int getUsedHeight() {
+ int widgetHeight = getWidgetHeight();
+ if (caption == null) {
+ return widgetHeight;
+ } else if (caption.shouldBePlacedAfterComponent()) {
+ return Math.max(widgetHeight, getCaptionHeight());
+ } else {
+ return widgetHeight + getCaptionHeight();
+ }
+ }
+
+ public int getUsedSizeInDirection(boolean isVertical) {
+ return isVertical ? getUsedHeight() : getUsedWidth();
+ }
+
+ protected abstract int getCaptionHeight();
+
+ protected abstract int getCaptionWidth();
+
+ public abstract int getWidgetHeight();
+
+ public abstract int getWidgetWidth();
+
+ public abstract boolean isUndefinedHeight();
+
+ public abstract boolean isUndefinedWidth();
+
+ public boolean isUndefinedInDirection(boolean isVertical) {
+ return isVertical ? isUndefinedHeight() : isUndefinedWidth();
+ }
+
+ public abstract boolean isRelativeHeight();
+
+ public abstract boolean isRelativeWidth();
+
+ public boolean isRelativeInDirection(boolean isVertical) {
+ return isVertical ? isRelativeHeight() : isRelativeWidth();
+ }
+
+ public Element getWrapperElement() {
+ return wrapper;
+ }
+
+ public void setExpandRatio(double expandRatio) {
+ this.expandRatio = expandRatio;
+ }
+
+ public double getExpandRatio() {
+ return expandRatio;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/link/LinkConnector.java b/src/com/vaadin/terminal/gwt/client/ui/link/LinkConnector.java
new file mode 100644
index 0000000000..f74a851d03
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/link/LinkConnector.java
@@ -0,0 +1,99 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.link;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.ui.Link;
+
+@Connect(Link.class)
+public class LinkConnector extends AbstractComponentConnector implements
+ Paintable {
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().client = client;
+
+ getWidget().enabled = isEnabled();
+
+ if (uidl.hasAttribute("name")) {
+ getWidget().target = uidl.getStringAttribute("name");
+ getWidget().anchor.setAttribute("target", getWidget().target);
+ }
+ if (uidl.hasAttribute("src")) {
+ getWidget().src = client.translateVaadinUri(uidl
+ .getStringAttribute("src"));
+ getWidget().anchor.setAttribute("href", getWidget().src);
+ }
+
+ if (uidl.hasAttribute("border")) {
+ if ("none".equals(uidl.getStringAttribute("border"))) {
+ getWidget().borderStyle = VLink.BORDER_STYLE_NONE;
+ } else {
+ getWidget().borderStyle = VLink.BORDER_STYLE_MINIMAL;
+ }
+ } else {
+ getWidget().borderStyle = VLink.BORDER_STYLE_DEFAULT;
+ }
+
+ getWidget().targetHeight = uidl.hasAttribute("targetHeight") ? uidl
+ .getIntAttribute("targetHeight") : -1;
+ getWidget().targetWidth = uidl.hasAttribute("targetWidth") ? uidl
+ .getIntAttribute("targetWidth") : -1;
+
+ // Set link caption
+ getWidget().captionElement.setInnerText(getState().getCaption());
+
+ // handle error
+ if (null != getState().getErrorMessage()) {
+ if (getWidget().errorIndicatorElement == null) {
+ getWidget().errorIndicatorElement = DOM.createDiv();
+ DOM.setElementProperty(getWidget().errorIndicatorElement,
+ "className", "v-errorindicator");
+ }
+ DOM.insertChild(getWidget().getElement(),
+ getWidget().errorIndicatorElement, 0);
+ } else if (getWidget().errorIndicatorElement != null) {
+ DOM.setStyleAttribute(getWidget().errorIndicatorElement, "display",
+ "none");
+ }
+
+ if (getState().getIcon() != null) {
+ if (getWidget().icon == null) {
+ getWidget().icon = new Icon(client);
+ getWidget().anchor.insertBefore(getWidget().icon.getElement(),
+ getWidget().captionElement);
+ }
+ getWidget().icon.setUri(getState().getIcon().getURL());
+ }
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VLink.class);
+ }
+
+ @Override
+ public VLink getWidget() {
+ return (VLink) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/link/VLink.java b/src/com/vaadin/terminal/gwt/client/ui/link/VLink.java
new file mode 100644
index 0000000000..68fe5d9292
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/link/VLink.java
@@ -0,0 +1,117 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.link;
+
+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.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+
+public class VLink extends HTML implements ClickHandler {
+
+ public static final String CLASSNAME = "v-link";
+
+ protected static final int BORDER_STYLE_DEFAULT = 0;
+ protected static final int BORDER_STYLE_MINIMAL = 1;
+ protected static final int BORDER_STYLE_NONE = 2;
+
+ protected String src;
+
+ protected String target;
+
+ protected int borderStyle = BORDER_STYLE_DEFAULT;
+
+ protected boolean enabled;
+
+ protected int targetWidth;
+
+ protected int targetHeight;
+
+ protected Element errorIndicatorElement;
+
+ protected final Element anchor = DOM.createAnchor();
+
+ protected final Element captionElement = DOM.createSpan();
+
+ protected Icon icon;
+
+ protected ApplicationConnection client;
+
+ public VLink() {
+ super();
+ getElement().appendChild(anchor);
+ anchor.appendChild(captionElement);
+ addClickHandler(this);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ setStyleName(CLASSNAME);
+ }
+
+ public void onClick(ClickEvent event) {
+ if (enabled) {
+ if (target == null) {
+ target = "_self";
+ }
+ String features;
+ switch (borderStyle) {
+ case BORDER_STYLE_NONE:
+ features = "menubar=no,location=no,status=no";
+ break;
+ case BORDER_STYLE_MINIMAL:
+ features = "menubar=yes,location=no,status=no";
+ break;
+ default:
+ features = "";
+ break;
+ }
+
+ if (targetWidth > 0) {
+ features += (features.length() > 0 ? "," : "") + "width="
+ + targetWidth;
+ }
+ if (targetHeight > 0) {
+ features += (features.length() > 0 ? "," : "") + "height="
+ + targetHeight;
+ }
+
+ if (features.length() > 0) {
+ // if 'special features' are set, use window.open(), unless
+ // a modifier key is held (ctrl to open in new tab etc)
+ Event e = DOM.eventGetCurrentEvent();
+ if (!e.getCtrlKey() && !e.getAltKey() && !e.getShiftKey()
+ && !e.getMetaKey()) {
+ Window.open(src, target, features);
+ e.preventDefault();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ final Element target = DOM.eventGetTarget(event);
+ if (event.getTypeInt() == Event.ONLOAD) {
+ Util.notifyParentOfSizeChange(this, true);
+ }
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
+ if (target == captionElement || target == anchor
+ || (icon != null && target == icon.getElement())) {
+ super.onBrowserEvent(event);
+ }
+ if (!enabled) {
+ event.preventDefault();
+ }
+
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/listselect/ListSelectConnector.java b/src/com/vaadin/terminal/gwt/client/ui/listselect/ListSelectConnector.java
new file mode 100644
index 0000000000..ddaa959560
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/listselect/ListSelectConnector.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.listselect;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.OptionGroupBaseConnector;
+import com.vaadin.ui.ListSelect;
+
+@Connect(ListSelect.class)
+public class ListSelectConnector extends OptionGroupBaseConnector {
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VListSelect.class);
+ }
+
+ @Override
+ public VListSelect getWidget() {
+ return (VListSelect) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/listselect/TooltipListBox.java b/src/com/vaadin/terminal/gwt/client/ui/listselect/TooltipListBox.java
new file mode 100644
index 0000000000..abecd844da
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/listselect/TooltipListBox.java
@@ -0,0 +1,41 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.listselect;
+
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.VTooltip;
+
+/**
+ * Extended ListBox to listen tooltip events and forward them to generic
+ * handler.
+ */
+public class TooltipListBox extends ListBox {
+ private ApplicationConnection client;
+ private Widget widget;
+
+ public TooltipListBox(boolean isMultiselect) {
+ super(isMultiselect);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ }
+
+ public void setClient(ApplicationConnection client) {
+ this.client = client;
+ }
+
+ public void setSelect(Widget widget) {
+ this.widget = widget;
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ if (client != null) {
+ client.handleTooltipEvent(event, widget);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VListSelect.java b/src/com/vaadin/terminal/gwt/client/ui/listselect/VListSelect.java
index cebc4600a2..e338897841 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VListSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/listselect/VListSelect.java
@@ -2,18 +2,14 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.listselect;
import java.util.ArrayList;
import java.util.Iterator;
import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.ListBox;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.VOptionGroupBase;
public class VListSelect extends VOptionGroupBase {
@@ -80,11 +76,11 @@ public class VListSelect extends VOptionGroupBase {
} else {
lastSelectedIndex = si;
if (isMultiselect()) {
- client.updateVariable(id, "selected", getSelectedItems(),
- isImmediate());
+ client.updateVariable(paintableId, "selected",
+ getSelectedItems(), isImmediate());
} else {
- client.updateVariable(id, "selected", new String[] { ""
- + getSelectedItem() }, isImmediate());
+ client.updateVariable(paintableId, "selected",
+ new String[] { "" + getSelectedItem() }, isImmediate());
}
}
}
@@ -109,35 +105,4 @@ public class VListSelect extends VOptionGroupBase {
public void focus() {
select.setFocus(true);
}
-
-}
-
-/**
- * Extended ListBox to listen tooltip events and forward them to generic
- * handler.
- */
-class TooltipListBox extends ListBox {
- private ApplicationConnection client;
- private Paintable pntbl;
-
- TooltipListBox(boolean isMultiselect) {
- super(isMultiselect);
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
- }
-
- public void setClient(ApplicationConnection client) {
- this.client = client;
- }
-
- public void setSelect(Paintable s) {
- pntbl = s;
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (client != null) {
- client.handleTooltipEvent(event, pntbl);
- }
- }
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBar.java
index f0857f48c1..7bee870387 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBar.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.menubar;
/*
* Copyright 2007 Google Inc.
@@ -32,6 +32,7 @@ import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.PopupListener;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
/**
* A standard menu bar widget. A menu bar can contain any number of menu items,
@@ -294,7 +295,7 @@ public class MenuBar extends Widget implements PopupListener {
* @return a list containing the <code>MenuItem</code> objects in the menu
* bar
*/
- protected List<MenuItem> getItems() {
+ public List<MenuItem> getItems() {
return items;
}
@@ -306,7 +307,7 @@ public class MenuBar extends Widget implements PopupListener {
* @return the <code>MenuItem</code> that is currently selected, or
* <code>null</code> if no items are currently selected
*/
- protected MenuItem getSelectedItem() {
+ public MenuItem getSelectedItem() {
return selectedItem;
}
@@ -347,7 +348,7 @@ public class MenuBar extends Widget implements PopupListener {
* <code>true</code> if the item's command should be fired,
* <code>false</code> otherwise.
*/
- void doItemAction(final MenuItem item, boolean fireCommand) {
+ protected void doItemAction(final MenuItem item, boolean fireCommand) {
// If the given item is already showing its menu, we're done.
if ((shownChildMenu != null) && (item.getSubMenu() == shownChildMenu)) {
return;
@@ -451,7 +452,7 @@ public class MenuBar extends Widget implements PopupListener {
}
}
- void selectItem(MenuItem item) {
+ public void selectItem(MenuItem item) {
if (item == selectedItem) {
return;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBarConnector.java b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBarConnector.java
new file mode 100644
index 0000000000..d063c89ca9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuBarConnector.java
@@ -0,0 +1,169 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.menubar;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.menubar.VMenuBar.CustomMenuItem;
+
+@Connect(value = com.vaadin.ui.MenuBar.class, loadStyle = LoadStyle.LAZY)
+public class MenuBarConnector extends AbstractComponentConnector implements
+ Paintable, SimpleManagedLayout {
+ /**
+ * This method must be implemented to update the client-side component from
+ * UIDL data received from server.
+ *
+ * This method is called when the page is loaded for the first time, and
+ * every time UI changes in the component are received from the server.
+ */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().htmlContentAllowed = uidl
+ .hasAttribute(VMenuBar.HTML_CONTENT_ALLOWED);
+
+ getWidget().openRootOnHover = uidl
+ .getBooleanAttribute(VMenuBar.OPEN_ROOT_MENU_ON_HOWER);
+
+ getWidget().enabled = isEnabled();
+
+ // For future connections
+ getWidget().client = client;
+ getWidget().uidlId = uidl.getId();
+
+ // Empty the menu every time it receives new information
+ if (!getWidget().getItems().isEmpty()) {
+ getWidget().clearItems();
+ }
+
+ UIDL options = uidl.getChildUIDL(0);
+
+ if (null != getState() && !getState().isUndefinedWidth()) {
+ UIDL moreItemUIDL = options.getChildUIDL(0);
+ StringBuffer itemHTML = new StringBuffer();
+
+ if (moreItemUIDL.hasAttribute("icon")) {
+ itemHTML.append("<img src=\""
+ + Util.escapeAttribute(client
+ .translateVaadinUri(moreItemUIDL
+ .getStringAttribute("icon")))
+ + "\" class=\"" + Icon.CLASSNAME + "\" alt=\"\" />");
+ }
+
+ String moreItemText = moreItemUIDL.getStringAttribute("text");
+ if ("".equals(moreItemText)) {
+ moreItemText = "&#x25BA;";
+ }
+ itemHTML.append(moreItemText);
+
+ getWidget().moreItem = GWT.create(CustomMenuItem.class);
+ getWidget().moreItem.setHTML(itemHTML.toString());
+ getWidget().moreItem.setCommand(VMenuBar.emptyCommand);
+
+ getWidget().collapsedRootItems = new VMenuBar(true, getWidget());
+ getWidget().moreItem.setSubMenu(getWidget().collapsedRootItems);
+ getWidget().moreItem.addStyleName(VMenuBar.CLASSNAME
+ + "-more-menuitem");
+ }
+
+ UIDL uidlItems = uidl.getChildUIDL(1);
+ Iterator<Object> itr = uidlItems.getChildIterator();
+ Stack<Iterator<Object>> iteratorStack = new Stack<Iterator<Object>>();
+ Stack<VMenuBar> menuStack = new Stack<VMenuBar>();
+ VMenuBar currentMenu = getWidget();
+
+ while (itr.hasNext()) {
+ UIDL item = (UIDL) itr.next();
+ CustomMenuItem currentItem = null;
+
+ final int itemId = item.getIntAttribute("id");
+
+ boolean itemHasCommand = item.hasAttribute("command");
+ boolean itemIsCheckable = item
+ .hasAttribute(VMenuBar.ATTRIBUTE_CHECKED);
+
+ String itemHTML = getWidget().buildItemHTML(item);
+
+ Command cmd = null;
+ if (!item.hasAttribute("separator")) {
+ if (itemHasCommand || itemIsCheckable) {
+ // Construct a command that fires onMenuClick(int) with the
+ // item's id-number
+ cmd = new Command() {
+ public void execute() {
+ getWidget().hostReference.onMenuClick(itemId);
+ }
+ };
+ }
+ }
+
+ currentItem = currentMenu.addItem(itemHTML.toString(), cmd);
+ currentItem.updateFromUIDL(item, client);
+
+ if (item.getChildCount() > 0) {
+ menuStack.push(currentMenu);
+ iteratorStack.push(itr);
+ itr = item.getChildIterator();
+ currentMenu = new VMenuBar(true, currentMenu);
+ // this is the top-level style that also propagates to items -
+ // any item specific styles are set above in
+ // currentItem.updateFromUIDL(item, client)
+ if (getState().hasStyles()) {
+ for (String style : getState().getStyles()) {
+ currentMenu.addStyleDependentName(style);
+ }
+ }
+ currentItem.setSubMenu(currentMenu);
+ }
+
+ while (!itr.hasNext() && !iteratorStack.empty()) {
+ boolean hasCheckableItem = false;
+ for (CustomMenuItem menuItem : currentMenu.getItems()) {
+ hasCheckableItem = hasCheckableItem
+ || menuItem.isCheckable();
+ }
+ if (hasCheckableItem) {
+ currentMenu.addStyleDependentName("check-column");
+ } else {
+ currentMenu.removeStyleDependentName("check-column");
+ }
+
+ itr = iteratorStack.pop();
+ currentMenu = menuStack.pop();
+ }
+ }// while
+
+ getLayoutManager().setNeedsHorizontalLayout(this);
+
+ }// updateFromUIDL
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VMenuBar.class);
+ }
+
+ @Override
+ public VMenuBar getWidget() {
+ return (VMenuBar) super.getWidget();
+ }
+
+ public void layout() {
+ getWidget().iLayout();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/MenuItem.java b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuItem.java
index ec02db1c70..af79ba7c5e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/MenuItem.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/menubar/MenuItem.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.menubar;
/*
* Copyright 2007 Google Inc.
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/menubar/VMenuBar.java
index 12678c9515..e48483cb02 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/menubar/VMenuBar.java
@@ -1,17 +1,14 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.menubar;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import java.util.Stack;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
@@ -35,16 +32,20 @@ import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.ContainerResizedListener;
-import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.LayoutManager;
import com.vaadin.terminal.gwt.client.TooltipInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.SimpleFocusablePanel;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
-public class VMenuBar extends SimpleFocusablePanel implements Paintable,
- CloseHandler<PopupPanel>, ContainerResizedListener, KeyPressHandler,
- KeyDownHandler, FocusHandler, SubPartAware {
+public class VMenuBar extends SimpleFocusablePanel implements
+ CloseHandler<PopupPanel>, KeyPressHandler, KeyDownHandler,
+ FocusHandler, SubPartAware {
// The hierarchy of VMenuBar is a bit weird as VMenuBar is the Paintable,
// used for the root menu but also used for the sub menus.
@@ -57,7 +58,6 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
protected ApplicationConnection client;
protected final VMenuBar hostReference = this;
- protected String submenuIcon = null;
protected CustomMenuItem moreItem = null;
// Only used by the root menu bar
@@ -70,6 +70,10 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
public static final String OPEN_ROOT_MENU_ON_HOWER = "ormoh";
public static final String ATTRIBUTE_CHECKED = "checked";
+ public static final String ATTRIBUTE_ITEM_DESCRIPTION = "description";
+ public static final String ATTRIBUTE_ITEM_ICON = "icon";
+ public static final String ATTRIBUTE_ITEM_DISABLED = "disabled";
+ public static final String ATTRIBUTE_ITEM_STYLE = "style";
public static final String HTML_CONTENT_ALLOWED = "usehtml";
@@ -83,7 +87,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
protected VMenuBar parentMenu;
protected CustomMenuItem selected;
- private boolean enabled = true;
+ boolean enabled = true;
private String width = "notinited";
@@ -95,9 +99,9 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
}
});
- private boolean openRootOnHover;
+ boolean openRootOnHover;
- private boolean htmlContentAllowed;
+ boolean htmlContentAllowed;
public VMenuBar() {
// Create an empty horizontal menubar
@@ -150,31 +154,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
}
}
- @Override
- public void setWidth(String width) {
- if (Util.equals(this.width, width)) {
- return;
- }
-
- this.width = width;
- if (BrowserInfo.get().isIE6() && width.endsWith("px")) {
- // IE6 sometimes measures wrong using
- // Util.setWidthExcludingPaddingAndBorder so this is extracted to a
- // special case that uses another method. Really should fix the
- // Util.setWidthExcludingPaddingAndBorder method but that will
- // probably break additional cases
- int requestedPixelWidth = Integer.parseInt(width.substring(0,
- width.length() - 2));
- int paddingBorder = Util.measureHorizontalPaddingAndBorder(
- getElement(), 0);
- int w = requestedPixelWidth - paddingBorder;
- if (w < 0) {
- w = 0;
- }
- getElement().getStyle().setWidth(w, Unit.PX);
- } else {
- Util.setWidthExcludingPaddingAndBorder(this, width, 0);
- }
+ void updateSize() {
+ // Take from setWidth
if (!subMenu) {
// Only needed for root level menu
hideChildren();
@@ -184,141 +165,6 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
}
/**
- * This method must be implemented to update the client-side component from
- * UIDL data received from server.
- *
- * This method is called when the page is loaded for the first time, and
- * every time UI changes in the component are received from the server.
- */
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // This call should be made first. Ensure correct implementation,
- // and let the containing layout manage caption, etc.
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- htmlContentAllowed = uidl.hasAttribute(HTML_CONTENT_ALLOWED);
-
- openRootOnHover = uidl.getBooleanAttribute(OPEN_ROOT_MENU_ON_HOWER);
-
- enabled = !uidl.getBooleanAttribute("disabled");
-
- // For future connections
- this.client = client;
- uidlId = uidl.getId();
-
- // Empty the menu every time it receives new information
- if (!getItems().isEmpty()) {
- clearItems();
- }
-
- UIDL options = uidl.getChildUIDL(0);
-
- // FIXME remove in version 7
- if (options.hasAttribute("submenuIcon")) {
- submenuIcon = client.translateVaadinUri(uidl.getChildUIDL(0)
- .getStringAttribute("submenuIcon"));
- } else {
- submenuIcon = null;
- }
-
- if (uidl.hasAttribute("width")) {
- UIDL moreItemUIDL = options.getChildUIDL(0);
- StringBuffer itemHTML = new StringBuffer();
-
- if (moreItemUIDL.hasAttribute("icon")) {
- itemHTML.append("<img src=\""
- + Util.escapeAttribute(client
- .translateVaadinUri(moreItemUIDL
- .getStringAttribute("icon")))
- + "\" class=\"" + Icon.CLASSNAME + "\" alt=\"\" />");
- }
-
- String moreItemText = moreItemUIDL.getStringAttribute("text");
- if ("".equals(moreItemText)) {
- moreItemText = "&#x25BA;";
- }
- itemHTML.append(moreItemText);
-
- moreItem = GWT.create(CustomMenuItem.class);
- moreItem.setHTML(itemHTML.toString());
- moreItem.setCommand(emptyCommand);
-
- collapsedRootItems = new VMenuBar(true,
- (VMenuBar) client.getPaintable(uidlId));
- moreItem.setSubMenu(collapsedRootItems);
- moreItem.addStyleName(CLASSNAME + "-more-menuitem");
- }
-
- UIDL uidlItems = uidl.getChildUIDL(1);
- Iterator<Object> itr = uidlItems.getChildIterator();
- Stack<Iterator<Object>> iteratorStack = new Stack<Iterator<Object>>();
- Stack<VMenuBar> menuStack = new Stack<VMenuBar>();
- VMenuBar currentMenu = this;
-
- while (itr.hasNext()) {
- UIDL item = (UIDL) itr.next();
- CustomMenuItem currentItem = null;
-
- final int itemId = item.getIntAttribute("id");
-
- boolean itemHasCommand = item.hasAttribute("command");
- boolean itemIsCheckable = item.hasAttribute(ATTRIBUTE_CHECKED);
-
- String itemHTML = buildItemHTML(item);
-
- Command cmd = null;
- if (!item.hasAttribute("separator")) {
- if (itemHasCommand || itemIsCheckable) {
- // Construct a command that fires onMenuClick(int) with the
- // item's id-number
- cmd = new Command() {
- public void execute() {
- hostReference.onMenuClick(itemId);
- }
- };
- }
- }
-
- currentItem = currentMenu.addItem(itemHTML.toString(), cmd);
- currentItem.updateFromUIDL(item, client);
-
- if (item.getChildCount() > 0) {
- menuStack.push(currentMenu);
- iteratorStack.push(itr);
- itr = item.getChildIterator();
- currentMenu = new VMenuBar(true, currentMenu);
- if (uidl.hasAttribute("style")) {
- for (String style : uidl.getStringAttribute("style").split(
- " ")) {
- currentMenu.addStyleDependentName(style);
- }
- }
- currentItem.setSubMenu(currentMenu);
- }
-
- while (!itr.hasNext() && !iteratorStack.empty()) {
- boolean hasCheckableItem = false;
- for (CustomMenuItem menuItem : currentMenu.getItems()) {
- hasCheckableItem = hasCheckableItem
- || menuItem.isCheckable();
- }
- if (hasCheckableItem) {
- currentMenu.addStyleDependentName("check-column");
- } else {
- currentMenu.removeStyleDependentName("check-column");
- }
-
- itr = iteratorStack.pop();
- currentMenu = menuStack.pop();
- }
- }// while
-
- iLayout(false);
-
- }// updateFromUIDL
-
- /**
* Build the HTML content for a menu item.
*
* @param item
@@ -332,13 +178,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
} else {
// Add submenu indicator
if (item.getChildCount() > 0) {
- // FIXME For compatibility reasons: remove in version 7
String bgStyle = "";
- if (submenuIcon != null) {
- bgStyle = " style=\"background-image: url("
- + Util.escapeAttribute(submenuIcon)
- + "); text-indent: -999px; width: 1em;\"";
- }
itemHTML.append("<span class=\"" + CLASSNAME
+ "-submenu-indicator\"" + bgStyle + ">&#x25BA;</span>");
}
@@ -478,9 +318,6 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
// Handle onload events (icon loaded, size changes)
if (DOM.eventGetType(e) == Event.ONLOAD) {
- if (BrowserInfo.get().isIE6()) {
- Util.doIE6PngFix((Element) Element.as(e.getEventTarget()));
- }
VMenuBar parent = getParentMenu();
if (parent != null) {
// The onload event for an image in a popup should be sent to
@@ -733,27 +570,6 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
popup.setPopupPosition(left, top);
- // IE7 really tests one's patience sometimes
- // Part of a fix to correct #3850
- if (BrowserInfo.get().isIE7()) {
- popup.getElement().getStyle().setProperty("zoom", "");
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- if (popup == null) {
- // The child menu can be hidden before this command is
- // run.
- return;
- }
-
- if (popup.getElement().getStyle().getProperty("width") == null
- || popup.getElement().getStyle()
- .getProperty("width") == "") {
- popup.setWidth(popup.getOffsetWidth() + "px");
- }
- popup.getElement().getStyle().setProperty("zoom", "1");
- }
- });
- }
}
private int adjustPopupHeight(int top, final int shadowSpace) {
@@ -780,19 +596,10 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
style.setHeight(availableHeight, Unit.PX);
style.setOverflowY(Overflow.SCROLL);
- // Make room for the scroll bar
- if (BrowserInfo.get().isIE6()) {
- // IE6 renders the sub menu arrow icons on the scroll bar
- // unless we add some padding
- style.setPaddingRight(Util.getNativeScrollbarSize(),
- Unit.PX);
- } else {
- // For other browsers, adjusting the width of the popup is
- // enough
- style.setWidth(
- contentWidth + Util.getNativeScrollbarSize(),
- Unit.PX);
- }
+ // Make room for the scroll bar by adjusting the width of the
+ // popup
+ style.setWidth(contentWidth + Util.getNativeScrollbarSize(),
+ Unit.PX);
popup.updateShadowSizeAndPosition();
}
}
@@ -962,6 +769,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
addStyleDependentName("selected");
// needed for IE6 to have a single style name to match for an
// element
+ // TODO Can be optimized now that IE6 is not supported any more
if (checkable) {
if (checked) {
removeStyleDependentName("selected-unchecked");
@@ -1092,7 +900,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
this.client = client;
setSeparator(uidl.hasAttribute("separator"));
- setEnabled(!uidl.hasAttribute("disabled"));
+ setEnabled(!uidl.hasAttribute(ATTRIBUTE_ITEM_DISABLED));
if (!isSeparator() && uidl.hasAttribute(ATTRIBUTE_CHECKED)) {
// if the selected attribute is present (either true or false),
@@ -1103,13 +911,15 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
setCheckable(false);
}
- if (uidl.hasAttribute("style")) {
- String itemStyle = uidl.getStringAttribute("style");
+ if (uidl.hasAttribute(ATTRIBUTE_ITEM_STYLE)) {
+ String itemStyle = uidl
+ .getStringAttribute(ATTRIBUTE_ITEM_STYLE);
addStyleDependentName(itemStyle);
}
- if (uidl.hasAttribute("description")) {
- String description = uidl.getStringAttribute("description");
+ if (uidl.hasAttribute(ATTRIBUTE_ITEM_DESCRIPTION)) {
+ String description = uidl
+ .getStringAttribute(ATTRIBUTE_ITEM_DESCRIPTION);
TooltipInfo info = new TooltipInfo(description);
VMenuBar root = findRootMenu();
@@ -1150,10 +960,9 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
/**
* @author Jouni Koivuviita / Vaadin Ltd.
*/
- private int paddingWidth = -1;
-
public void iLayout() {
iLayout(false);
+ updateSize();
}
public void iLayout(boolean iconLoadEvent) {
@@ -1172,28 +981,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
removeItem(moreItem);
}
- // Measure available space
- if (paddingWidth == -1) {
- int widthBefore = getElement().getClientWidth();
- getElement().getStyle().setProperty("padding", "0");
- paddingWidth = widthBefore - getElement().getClientWidth();
- getElement().getStyle().setProperty("padding", "");
- }
- String overflow = "";
- if (BrowserInfo.get().isIE6()) {
- // IE6 cannot measure available width correctly without
- // overflow:hidden
- overflow = getElement().getStyle().getProperty("overflow");
- getElement().getStyle().setProperty("overflow", "hidden");
- }
-
- int availableWidth = getElement().getClientWidth() - paddingWidth;
-
- if (BrowserInfo.get().isIE6()) {
- // IE6 cannot measure available width correctly without
- // overflow:hidden
- getElement().getStyle().setProperty("overflow", overflow);
- }
+ int availableWidth = LayoutManager.get(client).getInnerWidth(
+ getElement());
// Used width includes the "more" item if present
int usedWidth = getConsumedWidth();
@@ -1237,17 +1026,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
removeItem(expand);
collapsedRootItems.addItem(expand, 0);
} else {
- widthAvailable = diff;
- }
-
- if (BrowserInfo.get().isIE6()) {
- /*
- * Handle transparency for IE6 here as we cannot
- * implement it in CustomMenuItem.onAttach because
- * onAttach is never called for CustomMenuItem due to an
- * invalid component hierarchy (#6203)...
- */
- reloadImages(expand.getElement());
+ widthAvailable = diff + moreItemWidth;
}
}
}
@@ -1656,31 +1435,4 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable,
return null;
}
- @Override
- protected void onLoad() {
- super.onLoad();
- if (BrowserInfo.get().isIE6()) {
- reloadImages(getElement());
- }
- }
-
- /**
- * Force a new onload event for all images. Used only for IE6 to deal with
- * PNG transparency.
- */
- private void reloadImages(Element root) {
-
- NodeList<com.google.gwt.dom.client.Element> imgElements = root
- .getElementsByTagName("img");
- for (int i = 0; i < imgElements.getLength(); i++) {
- Element e = (Element) imgElements.getItem(i);
-
- // IE6 fires onload events for the icons before the listener
- // is attached (or never). Updating the src force another
- // onload event
- String src = e.getAttribute("src");
- e.setAttribute("src", src);
- }
- }
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java
new file mode 100644
index 0000000000..801c405826
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java
@@ -0,0 +1,125 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.nativebutton;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.EventHelper;
+import com.vaadin.terminal.gwt.client.communication.FieldRpc.FocusAndBlurServerRpc;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonServerRpc;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonState;
+import com.vaadin.ui.NativeButton;
+
+@Connect(NativeButton.class)
+public class NativeButtonConnector extends AbstractComponentConnector implements
+ BlurHandler, FocusHandler {
+
+ private HandlerRegistration focusHandlerRegistration;
+ private HandlerRegistration blurHandlerRegistration;
+
+ private FocusAndBlurServerRpc focusBlurRpc = RpcProxy.create(
+ FocusAndBlurServerRpc.class, this);
+
+ @Override
+ public void init() {
+ super.init();
+
+ getWidget().buttonRpcProxy = RpcProxy.create(ButtonServerRpc.class,
+ this);
+ getWidget().client = getConnection();
+ getWidget().paintableId = getConnectorId();
+ }
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ getWidget().disableOnClick = getState().isDisableOnClick();
+ focusHandlerRegistration = EventHelper.updateFocusHandler(this,
+ focusHandlerRegistration);
+ blurHandlerRegistration = EventHelper.updateBlurHandler(this,
+ blurHandlerRegistration);
+
+ // Set text
+ getWidget().setText(getState().getCaption());
+
+ // handle error
+ if (null != getState().getErrorMessage()) {
+ if (getWidget().errorIndicatorElement == null) {
+ getWidget().errorIndicatorElement = DOM.createSpan();
+ getWidget().errorIndicatorElement
+ .setClassName("v-errorindicator");
+ }
+ getWidget().getElement().insertBefore(
+ getWidget().errorIndicatorElement,
+ getWidget().captionElement);
+
+ } else if (getWidget().errorIndicatorElement != null) {
+ getWidget().getElement().removeChild(
+ getWidget().errorIndicatorElement);
+ getWidget().errorIndicatorElement = null;
+ }
+
+ if (getState().getIcon() != null) {
+ if (getWidget().icon == null) {
+ getWidget().icon = new Icon(getConnection());
+ getWidget().getElement().insertBefore(
+ getWidget().icon.getElement(),
+ getWidget().captionElement);
+ }
+ getWidget().icon.setUri(getState().getIcon().getURL());
+ } else {
+ if (getWidget().icon != null) {
+ getWidget().getElement().removeChild(
+ getWidget().icon.getElement());
+ getWidget().icon = null;
+ }
+ }
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VNativeButton.class);
+ }
+
+ @Override
+ public VNativeButton getWidget() {
+ return (VNativeButton) super.getWidget();
+ }
+
+ @Override
+ public ButtonState getState() {
+ return (ButtonState) super.getState();
+ }
+
+ public void onFocus(FocusEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurRpc.focus();
+ }
+
+ public void onBlur(BlurEvent event) {
+ // EventHelper.updateFocusHandler ensures that this is called only when
+ // there is a listener on server side
+ focusBlurRpc.blur();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java
new file mode 100644
index 0000000000..d0b8f73eb1
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java
@@ -0,0 +1,132 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.nativebutton;
+
+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.Event;
+import com.google.gwt.user.client.ui.Button;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonServerRpc;
+
+public class VNativeButton extends Button implements ClickHandler {
+
+ public static final String CLASSNAME = "v-nativebutton";
+
+ protected String width = null;
+
+ protected String paintableId;
+
+ protected ApplicationConnection client;
+
+ ButtonServerRpc buttonRpcProxy;
+
+ protected Element errorIndicatorElement;
+
+ protected final Element captionElement = DOM.createSpan();
+
+ protected Icon icon;
+
+ /**
+ * Helper flag to handle special-case where the button is moved from under
+ * mouse while clicking it. In this case mouse leaves the button without
+ * moving.
+ */
+ private boolean clickPending;
+
+ protected boolean disableOnClick = false;
+
+ public VNativeButton() {
+ setStyleName(CLASSNAME);
+
+ getElement().appendChild(captionElement);
+ captionElement.setClassName(getStyleName() + "-caption");
+
+ addClickHandler(this);
+
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+ sinkEvents(Event.ONMOUSEDOWN);
+ sinkEvents(Event.ONMOUSEUP);
+ }
+
+ @Override
+ public void setText(String text) {
+ captionElement.setInnerText(text);
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+
+ if (DOM.eventGetType(event) == Event.ONLOAD) {
+ Util.notifyParentOfSizeChange(this, true);
+
+ } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN
+ && event.getButton() == Event.BUTTON_LEFT) {
+ clickPending = true;
+ } else if (DOM.eventGetType(event) == Event.ONMOUSEMOVE) {
+ clickPending = false;
+ } else if (DOM.eventGetType(event) == Event.ONMOUSEOUT) {
+ if (clickPending) {
+ click();
+ }
+ clickPending = false;
+ }
+
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
+ }
+
+ @Override
+ public void setWidth(String width) {
+ this.width = width;
+ super.setWidth(width);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
+ * .dom.client.ClickEvent)
+ */
+ public void onClick(ClickEvent event) {
+ if (paintableId == null || client == null) {
+ return;
+ }
+
+ if (BrowserInfo.get().isSafari()) {
+ VNativeButton.this.setFocus(true);
+ }
+ if (disableOnClick) {
+ setEnabled(false);
+ buttonRpcProxy.disableOnClick();
+ }
+
+ // Add mouse details
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event.getNativeEvent(), getElement());
+ buttonRpcProxy.click(details);
+
+ clickPending = false;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (isEnabled() != enabled) {
+ super.setEnabled(enabled);
+ setStyleName(ApplicationConnection.DISABLED_CLASSNAME, !enabled);
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativeselect/NativeSelectConnector.java b/src/com/vaadin/terminal/gwt/client/ui/nativeselect/NativeSelectConnector.java
new file mode 100644
index 0000000000..e6264e492f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/nativeselect/NativeSelectConnector.java
@@ -0,0 +1,25 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.nativeselect;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.OptionGroupBaseConnector;
+import com.vaadin.ui.NativeSelect;
+
+@Connect(NativeSelect.class)
+public class NativeSelectConnector extends OptionGroupBaseConnector {
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VNativeSelect.class);
+ }
+
+ @Override
+ public VNativeSelect getWidget() {
+ return (VNativeSelect) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java b/src/com/vaadin/terminal/gwt/client/ui/nativeselect/VNativeSelect.java
index d1b2bd76ae..54f5e9aff5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/nativeselect/VNativeSelect.java
@@ -2,15 +2,16 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.nativeselect;
import java.util.ArrayList;
import java.util.Iterator;
import com.google.gwt.event.dom.client.ChangeEvent;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.listselect.TooltipListBox;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.VOptionGroupBase;
public class VNativeSelect extends VOptionGroupBase implements Field {
@@ -58,11 +59,6 @@ public class VNativeSelect extends VOptionGroupBase implements Field {
select.setItemSelected(0, true);
firstValueIsTemporaryNullItem = true;
}
- if (BrowserInfo.get().isIE6()) {
- // lazy size change - IE6 uses naive dropdown that does not have a
- // proper size yet
- Util.notifyParentOfSizeChange(this, true);
- }
}
@Override
@@ -80,10 +76,10 @@ public class VNativeSelect extends VOptionGroupBase implements Field {
public void onChange(ChangeEvent event) {
if (select.isMultipleSelect()) {
- client.updateVariable(id, "selected", getSelectedItems(),
+ client.updateVariable(paintableId, "selected", getSelectedItems(),
isImmediate());
} else {
- client.updateVariable(id, "selected", new String[] { ""
+ client.updateVariable(paintableId, "selected", new String[] { ""
+ getSelectedItem() }, isImmediate());
}
if (firstValueIsTemporaryNullItem) {
@@ -113,5 +109,4 @@ public class VNativeSelect extends VOptionGroupBase implements Field {
public void focus() {
select.setFocus(true);
}
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNotification.java b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java
index 27407c55aa..eb97160f52 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VNotification.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.notification;
import java.util.ArrayList;
import java.util.Date;
@@ -21,6 +21,8 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.root.VRoot;
public class VNotification extends VOverlay {
@@ -56,6 +58,13 @@ public class VNotification extends VOverlay {
private ArrayList<EventListener> listeners;
private static final int TOUCH_DEVICE_IDLE_DELAY = 1000;
+ public static final String ATTRIBUTE_NOTIFICATION_STYLE = "style";
+ public static final String ATTRIBUTE_NOTIFICATION_CAPTION = "caption";
+ public static final String ATTRIBUTE_NOTIFICATION_MESSAGE = "message";
+ public static final String ATTRIBUTE_NOTIFICATION_ICON = "icon";
+ public static final String ATTRIBUTE_NOTIFICATION_POSITION = "position";
+ public static final String ATTRIBUTE_NOTIFICATION_DELAY = "delay";
+
/**
* Default constructor. You should use GWT.create instead.
*/
@@ -357,23 +366,25 @@ public class VNotification extends VOverlay {
public static void showNotification(ApplicationConnection client,
final UIDL notification) {
boolean onlyPlainText = notification
- .hasAttribute(VView.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED);
+ .hasAttribute(VRoot.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED);
String html = "";
- if (notification.hasAttribute("icon")) {
+ if (notification.hasAttribute(ATTRIBUTE_NOTIFICATION_ICON)) {
final String parsedUri = client.translateVaadinUri(notification
- .getStringAttribute("icon"));
+ .getStringAttribute(ATTRIBUTE_NOTIFICATION_ICON));
html += "<img src=\"" + Util.escapeAttribute(parsedUri) + "\" />";
}
- if (notification.hasAttribute("caption")) {
- String caption = notification.getStringAttribute("caption");
+ if (notification.hasAttribute(ATTRIBUTE_NOTIFICATION_CAPTION)) {
+ String caption = notification
+ .getStringAttribute(ATTRIBUTE_NOTIFICATION_CAPTION);
if (onlyPlainText) {
caption = Util.escapeHTML(caption);
caption = caption.replaceAll("\\n", "<br />");
}
html += "<h1>" + caption + "</h1>";
}
- if (notification.hasAttribute("message")) {
- String message = notification.getStringAttribute("message");
+ if (notification.hasAttribute(ATTRIBUTE_NOTIFICATION_MESSAGE)) {
+ String message = notification
+ .getStringAttribute(ATTRIBUTE_NOTIFICATION_MESSAGE);
if (onlyPlainText) {
message = Util.escapeHTML(message);
message = message.replaceAll("\\n", "<br />");
@@ -381,10 +392,13 @@ public class VNotification extends VOverlay {
html += "<p>" + message + "</p>";
}
- final String style = notification.hasAttribute("style") ? notification
- .getStringAttribute("style") : null;
- final int position = notification.getIntAttribute("position");
- final int delay = notification.getIntAttribute("delay");
+ final String style = notification
+ .hasAttribute(ATTRIBUTE_NOTIFICATION_STYLE) ? notification
+ .getStringAttribute(ATTRIBUTE_NOTIFICATION_STYLE) : null;
+ final int position = notification
+ .getIntAttribute(ATTRIBUTE_NOTIFICATION_POSITION);
+ final int delay = notification
+ .getIntAttribute(ATTRIBUTE_NOTIFICATION_DELAY);
createNotification(delay).show(html, position, style);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupBaseConnector.java b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupBaseConnector.java
new file mode 100644
index 0000000000..3658126a97
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupBaseConnector.java
@@ -0,0 +1,93 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.optiongroup;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.nativebutton.VNativeButton;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
+
+public abstract class OptionGroupBaseConnector extends AbstractFieldConnector
+ implements Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+ // Save details
+ getWidget().client = client;
+ getWidget().paintableId = uidl.getId();
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().selectedKeys = uidl.getStringArrayVariableAsSet("selected");
+
+ getWidget().readonly = isReadOnly();
+ getWidget().disabled = !isEnabled();
+ getWidget().multiselect = "multi".equals(uidl
+ .getStringAttribute("selectmode"));
+ getWidget().immediate = getState().isImmediate();
+ getWidget().nullSelectionAllowed = uidl
+ .getBooleanAttribute("nullselect");
+ getWidget().nullSelectionItemAvailable = uidl
+ .getBooleanAttribute("nullselectitem");
+
+ if (uidl.hasAttribute("cols")) {
+ getWidget().cols = uidl.getIntAttribute("cols");
+ }
+ if (uidl.hasAttribute("rows")) {
+ getWidget().rows = uidl.getIntAttribute("rows");
+ }
+
+ final UIDL ops = uidl.getChildUIDL(0);
+
+ if (getWidget().getColumns() > 0) {
+ getWidget().container.setWidth(getWidget().getColumns() + "em");
+ if (getWidget().container != getWidget().optionsContainer) {
+ getWidget().optionsContainer.setWidth("100%");
+ }
+ }
+
+ getWidget().buildOptions(ops);
+
+ if (uidl.getBooleanAttribute("allownewitem")) {
+ if (getWidget().newItemField == null) {
+ getWidget().newItemButton = new VNativeButton();
+ getWidget().newItemButton.setText("+");
+ getWidget().newItemButton.addClickHandler(getWidget());
+ getWidget().newItemField = new VTextField();
+ getWidget().newItemField.addKeyPressHandler(getWidget());
+ }
+ getWidget().newItemField.setEnabled(!getWidget().disabled
+ && !getWidget().readonly);
+ getWidget().newItemButton.setEnabled(!getWidget().disabled
+ && !getWidget().readonly);
+
+ if (getWidget().newItemField == null
+ || getWidget().newItemField.getParent() != getWidget().container) {
+ getWidget().container.add(getWidget().newItemField);
+ getWidget().container.add(getWidget().newItemButton);
+ final int w = getWidget().container.getOffsetWidth()
+ - getWidget().newItemButton.getOffsetWidth();
+ getWidget().newItemField.setWidth(Math.max(w, 0) + "px");
+ }
+ } else if (getWidget().newItemField != null) {
+ getWidget().container.remove(getWidget().newItemField);
+ getWidget().container.remove(getWidget().newItemButton);
+ }
+
+ getWidget().setTabIndex(
+ uidl.hasAttribute("tabindex") ? uidl
+ .getIntAttribute("tabindex") : 0);
+
+ }
+
+ @Override
+ public VOptionGroupBase getWidget() {
+ return (VOptionGroupBase) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupConnector.java b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupConnector.java
new file mode 100644
index 0000000000..06552a2812
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/OptionGroupConnector.java
@@ -0,0 +1,73 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.optiongroup;
+
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.EventId;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.OptionGroup;
+
+@Connect(OptionGroup.class)
+public class OptionGroupConnector extends OptionGroupBaseConnector {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().htmlContentAllowed = uidl
+ .hasAttribute(VOptionGroup.HTML_CONTENT_ALLOWED);
+
+ super.updateFromUIDL(uidl, client);
+
+ getWidget().sendFocusEvents = client.hasEventListeners(this,
+ EventId.FOCUS);
+ getWidget().sendBlurEvents = client.hasEventListeners(this,
+ EventId.BLUR);
+
+ if (getWidget().focusHandlers != null) {
+ for (HandlerRegistration reg : getWidget().focusHandlers) {
+ reg.removeHandler();
+ }
+ getWidget().focusHandlers.clear();
+ getWidget().focusHandlers = null;
+
+ for (HandlerRegistration reg : getWidget().blurHandlers) {
+ reg.removeHandler();
+ }
+ getWidget().blurHandlers.clear();
+ getWidget().blurHandlers = null;
+ }
+
+ if (getWidget().sendFocusEvents || getWidget().sendBlurEvents) {
+ getWidget().focusHandlers = new ArrayList<HandlerRegistration>();
+ getWidget().blurHandlers = new ArrayList<HandlerRegistration>();
+
+ // add focus and blur handlers to checkboxes / radio buttons
+ for (Widget wid : getWidget().panel) {
+ if (wid instanceof CheckBox) {
+ getWidget().focusHandlers.add(((CheckBox) wid)
+ .addFocusHandler(getWidget()));
+ getWidget().blurHandlers.add(((CheckBox) wid)
+ .addBlurHandler(getWidget()));
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VOptionGroup.class);
+ }
+
+ @Override
+ public VOptionGroup getWidget() {
+ return (VOptionGroup) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroup.java
index 662f195fcd..d6e6949242 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroup.java
@@ -2,9 +2,8 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.optiongroup;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -20,7 +19,6 @@ import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Focusable;
@@ -28,10 +26,11 @@ import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.checkbox.VCheckBox;
public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
BlurHandler {
@@ -40,21 +39,19 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
public static final String CLASSNAME = "v-select-optiongroup";
- private final Panel panel;
+ public static final String ATTRIBUTE_OPTION_DISABLED = "disabled";
+
+ protected final Panel panel;
private final Map<CheckBox, String> optionsToKeys;
- private boolean sendFocusEvents = false;
- private boolean sendBlurEvents = false;
- private List<HandlerRegistration> focusHandlers = null;
- private List<HandlerRegistration> blurHandlers = null;
+ protected boolean sendFocusEvents = false;
+ protected boolean sendBlurEvents = false;
+ protected List<HandlerRegistration> focusHandlers = null;
+ protected List<HandlerRegistration> blurHandlers = null;
private final LoadHandler iconLoadHandler = new LoadHandler() {
public void onLoad(LoadEvent event) {
- if (BrowserInfo.get().isIE6()) {
- Util.doIE6PngFix((Element) Element.as(event.getNativeEvent()
- .getEventTarget()));
- }
Util.notifyParentOfSizeChange(VOptionGroup.this, true);
}
};
@@ -68,7 +65,7 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
*/
private boolean blurOccured = false;
- private boolean htmlContentAllowed = false;
+ protected boolean htmlContentAllowed = false;
public VOptionGroup() {
super(CLASSNAME);
@@ -76,43 +73,6 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
optionsToKeys = new HashMap<CheckBox, String>();
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- htmlContentAllowed = uidl.hasAttribute(HTML_CONTENT_ALLOWED);
-
- super.updateFromUIDL(uidl, client);
-
- sendFocusEvents = client.hasEventListeners(this, EventId.FOCUS);
- sendBlurEvents = client.hasEventListeners(this, EventId.BLUR);
-
- if (focusHandlers != null) {
- for (HandlerRegistration reg : focusHandlers) {
- reg.removeHandler();
- }
- focusHandlers.clear();
- focusHandlers = null;
-
- for (HandlerRegistration reg : blurHandlers) {
- reg.removeHandler();
- }
- blurHandlers.clear();
- blurHandlers = null;
- }
-
- if (sendFocusEvents || sendBlurEvents) {
- focusHandlers = new ArrayList<HandlerRegistration>();
- blurHandlers = new ArrayList<HandlerRegistration>();
-
- // add focus and blur handlers to checkboxes / radio buttons
- for (Widget wid : panel) {
- if (wid instanceof CheckBox) {
- focusHandlers.add(((CheckBox) wid).addFocusHandler(this));
- blurHandlers.add(((CheckBox) wid).addBlurHandler(this));
- }
- }
- }
- }
-
/*
* Return true if no elements were changed, false otherwise.
*/
@@ -139,7 +99,7 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
op = new VCheckBox();
op.setHTML(itemHtml);
} else {
- op = new RadioButton(id, itemHtml, true);
+ op = new RadioButton(paintableId, itemHtml, true);
op.setStyleName("v-radiobutton");
}
@@ -150,7 +110,8 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
op.addStyleName(CLASSNAME_OPTION);
op.setValue(opUidl.getBooleanAttribute("selected"));
- boolean enabled = !opUidl.getBooleanAttribute("disabled")
+ boolean enabled = !opUidl
+ .getBooleanAttribute(ATTRIBUTE_OPTION_DISABLED)
&& !isReadonly() && !isDisabled();
op.setEnabled(enabled);
setStyleName(op.getElement(),
@@ -180,7 +141,7 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
} else {
selectedKeys.remove(key);
}
- client.updateVariable(id, "selected", getSelectedItems(),
+ client.updateVariable(paintableId, "selected", getSelectedItems(),
isImmediate());
}
}
@@ -206,7 +167,7 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
// panel was blurred => fire the event to the server side if
// requested by server side
if (sendFocusEvents) {
- client.updateVariable(id, EventId.FOCUS, "", true);
+ client.updateVariable(paintableId, EventId.FOCUS, "", true);
}
} else {
// blur occured before this focus event
@@ -225,7 +186,8 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler,
// check whether blurOccured still is true and then send the
// event out to the server
if (blurOccured) {
- client.updateVariable(id, EventId.BLUR, "", true);
+ client.updateVariable(paintableId, EventId.BLUR, "",
+ true);
blurOccured = false;
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroupBase.java
index 50a9f8cb98..a512f024b8 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/optiongroup/VOptionGroupBase.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.optiongroup;
import java.util.Set;
@@ -19,35 +19,37 @@ import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.nativebutton.VNativeButton;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
-abstract class VOptionGroupBase extends Composite implements Paintable, Field,
+public abstract class VOptionGroupBase extends Composite implements Field,
ClickHandler, ChangeHandler, KeyPressHandler, Focusable {
public static final String CLASSNAME_OPTION = "v-select-option";
protected ApplicationConnection client;
- protected String id;
+ protected String paintableId;
protected Set<String> selectedKeys;
- private boolean immediate;
+ protected boolean immediate;
- private boolean multiselect;
+ protected boolean multiselect;
- private boolean disabled;
+ protected boolean disabled;
- private boolean readonly;
+ protected boolean readonly;
- private int cols = 0;
+ protected int cols = 0;
- private int rows = 0;
+ protected int rows = 0;
- private boolean nullSelectionAllowed = true;
+ protected boolean nullSelectionAllowed = true;
- private boolean nullSelectionItemAvailable = false;
+ protected boolean nullSelectionItemAvailable = false;
/**
* Widget holding the different options (e.g. ListBox or Panel for radio
@@ -58,11 +60,11 @@ abstract class VOptionGroupBase extends Composite implements Paintable, Field,
/**
* Panel containing the component
*/
- private final Panel container;
+ protected final Panel container;
- private VTextField newItemField;
+ protected VTextField newItemField;
- private VNativeButton newItemButton;
+ protected VNativeButton newItemButton;
public VOptionGroupBase(String classname) {
container = new FlowPanel();
@@ -122,84 +124,23 @@ abstract class VOptionGroupBase extends Composite implements Paintable, Field,
return rows;
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- id = uidl.getId();
-
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- selectedKeys = uidl.getStringArrayVariableAsSet("selected");
-
- readonly = uidl.getBooleanAttribute("readonly");
- disabled = uidl.getBooleanAttribute("disabled");
- multiselect = "multi".equals(uidl.getStringAttribute("selectmode"));
- immediate = uidl.getBooleanAttribute("immediate");
- nullSelectionAllowed = uidl.getBooleanAttribute("nullselect");
- nullSelectionItemAvailable = uidl.getBooleanAttribute("nullselectitem");
-
- if (uidl.hasAttribute("cols")) {
- cols = uidl.getIntAttribute("cols");
- }
- if (uidl.hasAttribute("rows")) {
- rows = uidl.getIntAttribute("rows");
- }
-
- final UIDL ops = uidl.getChildUIDL(0);
-
- if (getColumns() > 0) {
- container.setWidth(getColumns() + "em");
- if (container != optionsContainer) {
- optionsContainer.setWidth("100%");
- }
- }
-
- buildOptions(ops);
-
- if (uidl.getBooleanAttribute("allownewitem")) {
- if (newItemField == null) {
- newItemButton = new VNativeButton();
- newItemButton.setText("+");
- newItemButton.addClickHandler(this);
- newItemField = new VTextField();
- newItemField.addKeyPressHandler(this);
- }
- newItemField.setEnabled(!disabled && !readonly);
- newItemButton.setEnabled(!disabled && !readonly);
-
- if (newItemField == null || newItemField.getParent() != container) {
- container.add(newItemField);
- container.add(newItemButton);
- final int w = container.getOffsetWidth()
- - newItemButton.getOffsetWidth();
- newItemField.setWidth(Math.max(w, 0) + "px");
- }
- } else if (newItemField != null) {
- container.remove(newItemField);
- container.remove(newItemButton);
- }
-
- setTabIndex(uidl.hasAttribute("tabindex") ? uidl
- .getIntAttribute("tabindex") : 0);
-
- }
-
abstract protected void setTabIndex(int tabIndex);
public void onClick(ClickEvent event) {
if (event.getSource() == newItemButton
&& !newItemField.getText().equals("")) {
- client.updateVariable(id, "newitem", newItemField.getText(), true);
+ client.updateVariable(paintableId, "newitem",
+ newItemField.getText(), true);
newItemField.setText("");
}
}
public void onChange(ChangeEvent event) {
if (multiselect) {
- client.updateVariable(id, "selected", getSelectedItems(), immediate);
+ client.updateVariable(paintableId, "selected", getSelectedItems(),
+ immediate);
} else {
- client.updateVariable(id, "selected", new String[] { ""
+ client.updateVariable(paintableId, "selected", new String[] { ""
+ getSelectedItem() }, immediate);
}
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
new file mode 100644
index 0000000000..174da61bd3
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
@@ -0,0 +1,331 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import java.util.List;
+
+import com.google.gwt.dom.client.Style;
+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.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ValueMap;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector;
+import com.vaadin.terminal.gwt.client.ui.AlignmentInfo;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.layout.ComponentConnectorLayoutSlot;
+import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot;
+
+public abstract class AbstractOrderedLayoutConnector extends
+ AbstractLayoutConnector implements Paintable, DirectionalManagedLayout {
+
+ AbstractOrderedLayoutServerRpc rpc;
+
+ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
+ this) {
+
+ @Override
+ protected ComponentConnector getChildComponent(Element element) {
+ return Util.getConnectorForElement(getConnection(), getWidget(),
+ element);
+ }
+
+ @Override
+ protected LayoutClickRpc getLayoutClickRPC() {
+ return rpc;
+ };
+
+ };
+
+ @Override
+ public void init() {
+ rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this);
+ getLayoutManager().registerDependency(this,
+ getWidget().spacingMeasureElement);
+ }
+
+ @Override
+ public void onUnregister() {
+ LayoutManager lm = getLayoutManager();
+
+ VMeasuringOrderedLayout layout = getWidget();
+ lm.unregisterDependency(this, layout.spacingMeasureElement);
+
+ // Unregister child caption listeners
+ for (ComponentConnector child : getChildren()) {
+ VLayoutSlot slot = layout.getSlotForChild(child.getWidget());
+ slot.setCaption(null);
+ }
+ }
+
+ @Override
+ public AbstractOrderedLayoutState getState() {
+ return (AbstractOrderedLayoutState) super.getState();
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ VMeasuringOrderedLayout layout = getWidget();
+ if (VCaption.isNeeded(component.getState())) {
+ VLayoutSlot layoutSlot = layout.getSlotForChild(component
+ .getWidget());
+ VCaption caption = layoutSlot.getCaption();
+ if (caption == null) {
+ caption = new VCaption(component, getConnection());
+
+ Widget widget = component.getWidget();
+
+ layout.setCaption(widget, caption);
+ }
+ caption.updateCaption();
+ } else {
+ layout.setCaption(component.getWidget(), null);
+ getLayoutManager().setNeedsLayout(this);
+ }
+ }
+
+ @Override
+ public VMeasuringOrderedLayout getWidget() {
+ return (VMeasuringOrderedLayout) super.getWidget();
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+ clickEventHandler.handleEventHandlerRegistration();
+
+ VMeasuringOrderedLayout layout = getWidget();
+
+ ValueMap expandRatios = uidl.getMapAttribute("expandRatios");
+ ValueMap alignments = uidl.getMapAttribute("alignments");
+
+ for (ComponentConnector child : getChildren()) {
+ VLayoutSlot slot = layout.getSlotForChild(child.getWidget());
+ String pid = child.getConnectorId();
+
+ AlignmentInfo alignment;
+ if (alignments.containsKey(pid)) {
+ alignment = new AlignmentInfo(alignments.getInt(pid));
+ } else {
+ alignment = AlignmentInfo.TOP_LEFT;
+ }
+ slot.setAlignment(alignment);
+
+ double expandRatio;
+ if (expandRatios.containsKey(pid)) {
+ expandRatio = expandRatios.getRawNumber(pid);
+ } else {
+ expandRatio = 0;
+ }
+ slot.setExpandRatio(expandRatio);
+ }
+
+ layout.updateMarginStyleNames(new VMarginInfo(getState()
+ .getMarginsBitmask()));
+
+ layout.updateSpacingStyleName(getState().isSpacing());
+
+ getLayoutManager().setNeedsLayout(this);
+ }
+
+ private int getSizeForInnerSize(int size, boolean isVertical) {
+ LayoutManager layoutManager = getLayoutManager();
+ Element element = getWidget().getElement();
+ if (isVertical) {
+ return size + layoutManager.getBorderHeight(element)
+ + layoutManager.getPaddingHeight(element);
+ } else {
+ return size + layoutManager.getBorderWidth(element)
+ + layoutManager.getPaddingWidth(element);
+ }
+ }
+
+ private static String getSizeProperty(boolean isVertical) {
+ return isVertical ? "height" : "width";
+ }
+
+ private boolean isUndefinedInDirection(boolean isVertical) {
+ if (isVertical) {
+ return isUndefinedHeight();
+ } else {
+ return isUndefinedWidth();
+ }
+ }
+
+ private int getInnerSizeInDirection(boolean isVertical) {
+ if (isVertical) {
+ return getLayoutManager().getInnerHeight(getWidget().getElement());
+ } else {
+ return getLayoutManager().getInnerWidth(getWidget().getElement());
+ }
+ }
+
+ private void layoutPrimaryDirection() {
+ VMeasuringOrderedLayout layout = getWidget();
+ boolean isVertical = layout.isVertical;
+ boolean isUndefined = isUndefinedInDirection(isVertical);
+
+ int startPadding = getStartPadding(isVertical);
+ int endPadding = getEndPadding(isVertical);
+ int spacingSize = getSpacingInDirection(isVertical);
+ int allocatedSize;
+
+ if (isUndefined) {
+ allocatedSize = -1;
+ } else {
+ allocatedSize = getInnerSizeInDirection(isVertical);
+ }
+
+ allocatedSize = layout.layoutPrimaryDirection(spacingSize,
+ allocatedSize, startPadding, endPadding);
+
+ Style ownStyle = getWidget().getElement().getStyle();
+ if (isUndefined) {
+ int outerSize = getSizeForInnerSize(allocatedSize, isVertical);
+ ownStyle.setPropertyPx(getSizeProperty(isVertical), outerSize);
+ reportUndefinedSize(outerSize, isVertical);
+ } else {
+ ownStyle.setProperty(getSizeProperty(isVertical),
+ getDefinedSize(isVertical));
+ }
+ }
+
+ private void reportUndefinedSize(int outerSize, boolean isVertical) {
+ if (isVertical) {
+ getLayoutManager().reportOuterHeight(this, outerSize);
+ } else {
+ getLayoutManager().reportOuterWidth(this, outerSize);
+ }
+ }
+
+ private int getSpacingInDirection(boolean isVertical) {
+ if (isVertical) {
+ return getLayoutManager().getOuterHeight(
+ getWidget().spacingMeasureElement);
+ } else {
+ return getLayoutManager().getOuterWidth(
+ getWidget().spacingMeasureElement);
+ }
+ }
+
+ private void layoutSecondaryDirection() {
+ VMeasuringOrderedLayout layout = getWidget();
+ boolean isVertical = layout.isVertical;
+ boolean isUndefined = isUndefinedInDirection(!isVertical);
+
+ int startPadding = getStartPadding(!isVertical);
+ int endPadding = getEndPadding(!isVertical);
+
+ int allocatedSize;
+ if (isUndefined) {
+ allocatedSize = -1;
+ } else {
+ allocatedSize = getInnerSizeInDirection(!isVertical);
+ }
+
+ allocatedSize = layout.layoutSecondaryDirection(allocatedSize,
+ startPadding, endPadding);
+
+ Style ownStyle = getWidget().getElement().getStyle();
+
+ if (isUndefined) {
+ int outerSize = getSizeForInnerSize(allocatedSize,
+ !getWidget().isVertical);
+ ownStyle.setPropertyPx(getSizeProperty(!getWidget().isVertical),
+ outerSize);
+ reportUndefinedSize(outerSize, !isVertical);
+ } else {
+ ownStyle.setProperty(getSizeProperty(!getWidget().isVertical),
+ getDefinedSize(!getWidget().isVertical));
+ }
+ }
+
+ private String getDefinedSize(boolean isVertical) {
+ if (isVertical) {
+ return getState().getHeight();
+ } else {
+ return getState().getWidth();
+ }
+ }
+
+ private int getStartPadding(boolean isVertical) {
+ if (isVertical) {
+ return getLayoutManager().getPaddingTop(getWidget().getElement());
+ } else {
+ return getLayoutManager().getPaddingLeft(getWidget().getElement());
+ }
+ }
+
+ private int getEndPadding(boolean isVertical) {
+ if (isVertical) {
+ return getLayoutManager()
+ .getPaddingBottom(getWidget().getElement());
+ } else {
+ return getLayoutManager().getPaddingRight(getWidget().getElement());
+ }
+ }
+
+ public void layoutHorizontally() {
+ if (getWidget().isVertical) {
+ layoutSecondaryDirection();
+ } else {
+ layoutPrimaryDirection();
+ }
+ }
+
+ public void layoutVertically() {
+ if (getWidget().isVertical) {
+ layoutPrimaryDirection();
+ } else {
+ layoutSecondaryDirection();
+ }
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+ List<ComponentConnector> previousChildren = event.getOldChildren();
+ int currentIndex = 0;
+ VMeasuringOrderedLayout layout = getWidget();
+
+ for (ComponentConnector child : getChildren()) {
+ Widget childWidget = child.getWidget();
+ VLayoutSlot slot = layout.getSlotForChild(childWidget);
+
+ if (childWidget.getParent() != layout) {
+ // If the child widget was previously attached to another
+ // AbstractOrderedLayout a slot might be found that belongs to
+ // another AbstractOrderedLayout. In this case we discard it and
+ // create a new slot.
+ slot = new ComponentConnectorLayoutSlot(getWidget()
+ .getStylePrimaryName(), child, this);
+ }
+ layout.addOrMove(slot, currentIndex++);
+ if (child.isRelativeWidth()) {
+ slot.getWrapperElement().getStyle().setWidth(100, Unit.PCT);
+ }
+ }
+
+ for (ComponentConnector child : previousChildren) {
+ if (child.getParent() != this) {
+ // Remove slot if the connector is no longer a child of this
+ // layout
+ layout.removeSlotForWidget(child.getWidget());
+ }
+ }
+
+ };
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java
new file mode 100644
index 0000000000..5a29eacada
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java
@@ -0,0 +1,12 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc;
+
+public interface AbstractOrderedLayoutServerRpc extends LayoutClickRpc,
+ ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutState.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutState.java
new file mode 100644
index 0000000000..bf542d3951
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutState.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
+
+public class AbstractOrderedLayoutState extends AbstractLayoutState {
+ private boolean spacing = false;
+
+ public boolean isSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(boolean spacing) {
+ this.spacing = spacing;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/HorizontalLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/HorizontalLayoutConnector.java
new file mode 100644
index 0000000000..a12b41ade7
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/HorizontalLayoutConnector.java
@@ -0,0 +1,24 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.HorizontalLayout;
+
+@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
+public class HorizontalLayoutConnector extends AbstractOrderedLayoutConnector {
+
+ @Override
+ public VHorizontalLayout getWidget() {
+ return (VHorizontalLayout) super.getWidget();
+ }
+
+ @Override
+ protected VHorizontalLayout createWidget() {
+ return GWT.create(VHorizontalLayout.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VHorizontalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VHorizontalLayout.java
new file mode 100644
index 0000000000..5bf377642e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VHorizontalLayout.java
@@ -0,0 +1,14 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+public class VHorizontalLayout extends VMeasuringOrderedLayout {
+
+ public static final String CLASSNAME = "v-horizontallayout";
+
+ public VHorizontalLayout() {
+ super(CLASSNAME, false);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VMeasuringOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VMeasuringOrderedLayout.java
new file mode 100644
index 0000000000..de55ca98e6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VMeasuringOrderedLayout.java
@@ -0,0 +1,241 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Position;
+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.google.gwt.user.client.ui.WidgetCollection;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.VMarginInfo;
+import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot;
+
+public class VMeasuringOrderedLayout extends ComplexPanel {
+
+ final boolean isVertical;
+
+ final DivElement spacingMeasureElement;
+
+ private Map<Widget, VLayoutSlot> widgetToSlot = new HashMap<Widget, VLayoutSlot>();
+
+ protected VMeasuringOrderedLayout(String className, boolean isVertical) {
+ DivElement element = Document.get().createDivElement();
+ setElement(element);
+
+ spacingMeasureElement = Document.get().createDivElement();
+ Style spacingStyle = spacingMeasureElement.getStyle();
+ spacingStyle.setPosition(Position.ABSOLUTE);
+ getElement().appendChild(spacingMeasureElement);
+
+ setStyleName(className);
+ this.isVertical = isVertical;
+ }
+
+ public void addOrMove(VLayoutSlot layoutSlot, int index) {
+ Widget widget = layoutSlot.getWidget();
+ Element wrapperElement = layoutSlot.getWrapperElement();
+
+ Element containerElement = getElement();
+ Node childAtIndex = containerElement.getChild(index);
+ if (childAtIndex != wrapperElement) {
+ // Insert at correct location not attached or at wrong location
+ containerElement.insertBefore(wrapperElement, childAtIndex);
+ insert(widget, wrapperElement, index, false);
+ }
+
+ widgetToSlot.put(widget, layoutSlot);
+ }
+
+ private void togglePrefixedStyleName(String name, boolean enabled) {
+ if (enabled) {
+ addStyleDependentName(name);
+ } else {
+ removeStyleDependentName(name);
+ }
+ }
+
+ void updateMarginStyleNames(VMarginInfo marginInfo) {
+ togglePrefixedStyleName("margin-top", marginInfo.hasTop());
+ togglePrefixedStyleName("margin-right", marginInfo.hasRight());
+ togglePrefixedStyleName("margin-bottom", marginInfo.hasBottom());
+ togglePrefixedStyleName("margin-left", marginInfo.hasLeft());
+ }
+
+ void updateSpacingStyleName(boolean spacingEnabled) {
+ String styleName = getStylePrimaryName();
+ if (spacingEnabled) {
+ spacingMeasureElement.addClassName(styleName + "-spacing-on");
+ spacingMeasureElement.removeClassName(styleName + "-spacing-off");
+ } else {
+ spacingMeasureElement.removeClassName(styleName + "-spacing-on");
+ spacingMeasureElement.addClassName(styleName + "-spacing-off");
+ }
+ }
+
+ public void removeSlotForWidget(Widget widget) {
+ VLayoutSlot slot = getSlotForChild(widget);
+ VCaption caption = slot.getCaption();
+ if (caption != null) {
+ // Must remove using setCaption to ensure dependencies (layout ->
+ // caption) are unregistered
+ slot.setCaption(null);
+ }
+
+ remove(slot.getWidget());
+ getElement().removeChild(slot.getWrapperElement());
+ widgetToSlot.remove(widget);
+ }
+
+ public VLayoutSlot getSlotForChild(Widget widget) {
+ return widgetToSlot.get(widget);
+ }
+
+ public void setCaption(Widget child, VCaption caption) {
+ VLayoutSlot slot = getSlotForChild(child);
+
+ if (caption != null) {
+ // Logical attach.
+ getChildren().add(caption);
+ }
+
+ // Physical attach if not null, also removes old caption
+ slot.setCaption(caption);
+
+ if (caption != null) {
+ // Adopt.
+ adopt(caption);
+ }
+ }
+
+ public int layoutPrimaryDirection(int spacingSize, int allocatedSize,
+ int startPadding, int endPadding) {
+ int actuallyAllocated = 0;
+ double totalExpand = 0;
+
+ int childCount = 0;
+ for (Widget child : this) {
+ if (child instanceof VCaption) {
+ continue;
+ }
+ childCount++;
+
+ VLayoutSlot slot = getSlotForChild(child);
+ totalExpand += slot.getExpandRatio();
+
+ if (!slot.isRelativeInDirection(isVertical)) {
+ actuallyAllocated += slot.getUsedSizeInDirection(isVertical);
+ }
+ }
+
+ actuallyAllocated += spacingSize * (childCount - 1);
+
+ if (allocatedSize == -1) {
+ allocatedSize = actuallyAllocated;
+ }
+
+ double unallocatedSpace = Math
+ .max(0, allocatedSize - actuallyAllocated);
+
+ double currentLocation = startPadding;
+
+ WidgetCollection children = getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ Widget child = children.get(i);
+ if (child instanceof VCaption) {
+ continue;
+ }
+
+ VLayoutSlot slot = getSlotForChild(child);
+
+ double childExpandRatio;
+ if (totalExpand == 0) {
+ childExpandRatio = 1d / childCount;
+ } else {
+ childExpandRatio = slot.getExpandRatio() / totalExpand;
+ }
+
+ double extraPixels = unallocatedSpace * childExpandRatio;
+ double endLocation = currentLocation + extraPixels;
+ if (!slot.isRelativeInDirection(isVertical)) {
+ endLocation += slot.getUsedSizeInDirection(isVertical);
+ }
+
+ /*
+ * currentLocation and allocatedSpace are used with full precision
+ * to avoid missing pixels in the end. The pixel dimensions passed
+ * to the DOM are still rounded. Otherwise e.g. 10.5px start
+ * position + 10.5px space might be cause the component to go 1px
+ * beyond the edge as the effect of the browser's rounding may cause
+ * something similar to 11px + 11px.
+ *
+ * It's most efficient to use doubles all the way because native
+ * javascript emulates other number types using doubles.
+ */
+ double roundedLocation = Math.round(currentLocation);
+
+ /*
+ * Space is calculated as the difference between rounded start and
+ * end locations. Just rounding the space would cause e.g. 10.5px +
+ * 10.5px = 21px -> 11px + 11px = 22px but in this way we get 11px +
+ * 10px = 21px.
+ */
+ double roundedSpace = Math.round(endLocation) - roundedLocation;
+
+ // Reserve room for the padding if we're at the end
+ double slotEndMargin;
+ if (i == children.size() - 1) {
+ slotEndMargin = endPadding;
+ } else {
+ slotEndMargin = 0;
+ }
+
+ slot.positionInDirection(roundedLocation, roundedSpace,
+ slotEndMargin, isVertical);
+
+ currentLocation = endLocation + spacingSize;
+ }
+
+ return allocatedSize;
+ }
+
+ public int layoutSecondaryDirection(int allocatedSize, int startPadding,
+ int endPadding) {
+ int maxSize = 0;
+ for (Widget child : this) {
+ if (child instanceof VCaption) {
+ continue;
+ }
+
+ VLayoutSlot slot = getSlotForChild(child);
+ if (!slot.isRelativeInDirection(!isVertical)) {
+ maxSize = Math.max(maxSize,
+ slot.getUsedSizeInDirection(!isVertical));
+ }
+ }
+
+ if (allocatedSize == -1) {
+ allocatedSize = maxSize;
+ }
+
+ for (Widget child : this) {
+ if (child instanceof VCaption) {
+ continue;
+ }
+
+ VLayoutSlot slot = getSlotForChild(child);
+ slot.positionInDirection(startPadding, allocatedSize, endPadding,
+ !isVertical);
+ }
+
+ return allocatedSize;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VVerticalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VVerticalLayout.java
new file mode 100644
index 0000000000..e44c576941
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VVerticalLayout.java
@@ -0,0 +1,14 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+public class VVerticalLayout extends VMeasuringOrderedLayout {
+
+ public static final String CLASSNAME = "v-verticallayout";
+
+ public VVerticalLayout() {
+ super(CLASSNAME, true);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VerticalLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VerticalLayoutConnector.java
new file mode 100644
index 0000000000..1e5651ce38
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/VerticalLayoutConnector.java
@@ -0,0 +1,24 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.orderedlayout;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.VerticalLayout;
+
+@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
+public class VerticalLayoutConnector extends AbstractOrderedLayoutConnector {
+
+ @Override
+ public VVerticalLayout getWidget() {
+ return (VVerticalLayout) super.getWidget();
+ }
+
+ @Override
+ protected VVerticalLayout createWidget() {
+ return GWT.create(VVerticalLayout.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java
new file mode 100644
index 0000000000..9555c38a36
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java
@@ -0,0 +1,243 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.panel;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+import com.vaadin.ui.Panel;
+
+@Connect(Panel.class)
+public class PanelConnector extends AbstractComponentContainerConnector
+ implements Paintable, SimpleManagedLayout, PostLayoutListener,
+ MayScrollChildren {
+
+ private Integer uidlScrollTop;
+
+ private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
+
+ @Override
+ protected void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails) {
+ rpc.click(mouseDetails);
+ }
+ };
+
+ private Integer uidlScrollLeft;
+
+ private PanelServerRpc rpc;
+
+ @Override
+ public void init() {
+ rpc = RpcProxy.create(PanelServerRpc.class, this);
+ VPanel panel = getWidget();
+ LayoutManager layoutManager = getLayoutManager();
+
+ layoutManager.registerDependency(this, panel.captionNode);
+ layoutManager.registerDependency(this, panel.bottomDecoration);
+ layoutManager.registerDependency(this, panel.contentNode);
+ }
+
+ @Override
+ public void onUnregister() {
+ VPanel panel = getWidget();
+ LayoutManager layoutManager = getLayoutManager();
+
+ layoutManager.unregisterDependency(this, panel.captionNode);
+ layoutManager.unregisterDependency(this, panel.bottomDecoration);
+ layoutManager.unregisterDependency(this, panel.contentNode);
+ }
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (isRealUpdate(uidl)) {
+
+ // Handle caption displaying and style names, prior generics.
+ // Affects size calculations
+
+ // Restore default stylenames
+ getWidget().contentNode.setClassName(VPanel.CLASSNAME + "-content");
+ getWidget().bottomDecoration.setClassName(VPanel.CLASSNAME
+ + "-deco");
+ getWidget().captionNode.setClassName(VPanel.CLASSNAME + "-caption");
+ boolean hasCaption = false;
+ if (getState().getCaption() != null
+ && !"".equals(getState().getCaption())) {
+ getWidget().setCaption(getState().getCaption());
+ hasCaption = true;
+ } else {
+ getWidget().setCaption("");
+ getWidget().captionNode.setClassName(VPanel.CLASSNAME
+ + "-nocaption");
+ }
+
+ // Add proper stylenames for all elements. This way we can prevent
+ // unwanted CSS selector inheritance.
+ final String captionBaseClass = VPanel.CLASSNAME
+ + (hasCaption ? "-caption" : "-nocaption");
+ final String contentBaseClass = VPanel.CLASSNAME + "-content";
+ final String decoBaseClass = VPanel.CLASSNAME + "-deco";
+ String captionClass = captionBaseClass;
+ String contentClass = contentBaseClass;
+ String decoClass = decoBaseClass;
+ if (getState().hasStyles()) {
+ for (String style : getState().getStyles()) {
+ captionClass += " " + captionBaseClass + "-" + style;
+ contentClass += " " + contentBaseClass + "-" + style;
+ decoClass += " " + decoBaseClass + "-" + style;
+ }
+ }
+ getWidget().captionNode.setClassName(captionClass);
+ getWidget().contentNode.setClassName(contentClass);
+ getWidget().bottomDecoration.setClassName(decoClass);
+ }
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ getWidget().client = client;
+ getWidget().id = uidl.getId();
+
+ if (getState().getIcon() != null) {
+ getWidget().setIconUri(getState().getIcon().getURL(), client);
+ } else {
+ getWidget().setIconUri(null, client);
+ }
+
+ getWidget().setErrorIndicatorVisible(
+ null != getState().getErrorMessage());
+
+ // We may have actions attached to this panel
+ if (uidl.getChildCount() > 0) {
+ final int cnt = uidl.getChildCount();
+ for (int i = 0; i < cnt; i++) {
+ UIDL childUidl = uidl.getChildUIDL(i);
+ if (childUidl.getTag().equals("actions")) {
+ if (getWidget().shortcutHandler == null) {
+ getWidget().shortcutHandler = new ShortcutActionHandler(
+ getConnectorId(), client);
+ }
+ getWidget().shortcutHandler.updateActionMap(childUidl);
+ }
+ }
+ }
+
+ if (getState().getScrollTop() != getWidget().scrollTop) {
+ // Sizes are not yet up to date, so changing the scroll position
+ // is deferred to after the layout phase
+ uidlScrollTop = getState().getScrollTop();
+ }
+
+ if (getState().getScrollLeft() != getWidget().scrollLeft) {
+ // Sizes are not yet up to date, so changing the scroll position
+ // is deferred to after the layout phase
+ uidlScrollLeft = getState().getScrollLeft();
+ }
+
+ // And apply tab index
+ getWidget().contentNode.setTabIndex(getState().getTabIndex());
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP: layouts caption, errors etc not rendered in Panel
+ }
+
+ @Override
+ public VPanel getWidget() {
+ return (VPanel) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VPanel.class);
+ }
+
+ public void layout() {
+ updateSizes();
+ }
+
+ void updateSizes() {
+ VPanel panel = getWidget();
+
+ LayoutManager layoutManager = getLayoutManager();
+ int top = layoutManager.getOuterHeight(panel.captionNode);
+ int bottom = layoutManager.getInnerHeight(panel.bottomDecoration);
+
+ Style style = panel.getElement().getStyle();
+ panel.captionNode.getParentElement().getStyle()
+ .setMarginTop(-top, Unit.PX);
+ panel.bottomDecoration.getStyle().setMarginBottom(-bottom, Unit.PX);
+ style.setPaddingTop(top, Unit.PX);
+ style.setPaddingBottom(bottom, Unit.PX);
+
+ // Update scroll positions
+ panel.contentNode.setScrollTop(panel.scrollTop);
+ panel.contentNode.setScrollLeft(panel.scrollLeft);
+ // Read actual value back to ensure update logic is correct
+ panel.scrollTop = panel.contentNode.getScrollTop();
+ panel.scrollLeft = panel.contentNode.getScrollLeft();
+ }
+
+ public void postLayout() {
+ VPanel panel = getWidget();
+ if (uidlScrollTop != null) {
+ panel.contentNode.setScrollTop(uidlScrollTop.intValue());
+ // Read actual value back to ensure update logic is correct
+ // TODO Does this trigger reflows?
+ panel.scrollTop = panel.contentNode.getScrollTop();
+ uidlScrollTop = null;
+ }
+
+ if (uidlScrollLeft != null) {
+ panel.contentNode.setScrollLeft(uidlScrollLeft.intValue());
+ // Read actual value back to ensure update logic is correct
+ // TODO Does this trigger reflows?
+ panel.scrollLeft = panel.contentNode.getScrollLeft();
+ uidlScrollLeft = null;
+ }
+ }
+
+ @Override
+ public PanelState getState() {
+ return (PanelState) super.getState();
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+ // We always have 1 child, unless the child is hidden
+ Widget newChildWidget = null;
+ if (getChildren().size() == 1) {
+ ComponentConnector newChild = getChildren().get(0);
+ newChildWidget = newChild.getWidget();
+ }
+
+ getWidget().setWidget(newChildWidget);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java
new file mode 100644
index 0000000000..9b59344aec
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.panel;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.ClickRpc;
+
+public interface PanelServerRpc extends ClickRpc, ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelState.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelState.java
new file mode 100644
index 0000000000..fc7921825f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelState.java
@@ -0,0 +1,36 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.panel;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+
+public class PanelState extends ComponentState {
+ private int tabIndex;
+ private int scrollLeft, scrollTop;
+
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+ }
+
+ public int getScrollLeft() {
+ return scrollLeft;
+ }
+
+ public void setScrollLeft(int scrollLeft) {
+ this.scrollLeft = scrollLeft;
+ }
+
+ public int getScrollTop() {
+ return scrollTop;
+ }
+
+ public void setScrollTop(int scrollTop) {
+ this.scrollTop = scrollTop;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java
new file mode 100644
index 0000000000..e2d3d443a0
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/panel/VPanel.java
@@ -0,0 +1,189 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.panel;
+
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.event.dom.client.TouchStartEvent;
+import com.google.gwt.event.dom.client.TouchStartHandler;
+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.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Focusable;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+
+public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
+ Focusable {
+
+ public static final String CLASSNAME = "v-panel";
+
+ ApplicationConnection client;
+
+ String id;
+
+ final Element captionNode = DOM.createDiv();
+
+ private final Element captionText = DOM.createSpan();
+
+ private Icon icon;
+
+ final Element bottomDecoration = DOM.createDiv();
+
+ final Element contentNode = DOM.createDiv();
+
+ private Element errorIndicatorElement;
+
+ ShortcutActionHandler shortcutHandler;
+
+ int scrollTop;
+
+ int scrollLeft;
+
+ private TouchScrollDelegate touchScrollDelegate;
+
+ public VPanel() {
+ super();
+ DivElement captionWrap = Document.get().createDivElement();
+ captionWrap.appendChild(captionNode);
+ captionNode.appendChild(captionText);
+
+ captionWrap.setClassName(CLASSNAME + "-captionwrap");
+ captionNode.setClassName(CLASSNAME + "-caption");
+ contentNode.setClassName(CLASSNAME + "-content");
+ bottomDecoration.setClassName(CLASSNAME + "-deco");
+
+ getElement().appendChild(captionWrap);
+
+ /*
+ * Make contentNode focusable only by using the setFocus() method. This
+ * behaviour can be changed by invoking setTabIndex() in the serverside
+ * implementation
+ */
+ contentNode.setTabIndex(-1);
+
+ getElement().appendChild(contentNode);
+
+ getElement().appendChild(bottomDecoration);
+ setStyleName(CLASSNAME);
+ DOM.sinkEvents(getElement(), Event.ONKEYDOWN);
+ DOM.sinkEvents(contentNode, Event.ONSCROLL | Event.TOUCHEVENTS);
+ addHandler(new TouchStartHandler() {
+ public void onTouchStart(TouchStartEvent event) {
+ getTouchScrollDelegate().onTouchStart(event);
+ }
+ }, TouchStartEvent.getType());
+ }
+
+ /**
+ * Sets the keyboard focus on the Panel
+ *
+ * @param focus
+ * Should the panel have focus or not.
+ */
+ public void setFocus(boolean focus) {
+ if (focus) {
+ getContainerElement().focus();
+ } else {
+ getContainerElement().blur();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Focusable#focus()
+ */
+ public void focus() {
+ setFocus(true);
+
+ }
+
+ @Override
+ protected Element getContainerElement() {
+ return contentNode;
+ }
+
+ void setCaption(String text) {
+ DOM.setInnerHTML(captionText, text);
+ }
+
+ void setErrorIndicatorVisible(boolean showError) {
+ if (showError) {
+ if (errorIndicatorElement == null) {
+ errorIndicatorElement = DOM.createSpan();
+ DOM.setElementProperty(errorIndicatorElement, "className",
+ "v-errorindicator");
+ DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS);
+ sinkEvents(Event.MOUSEEVENTS);
+ }
+ DOM.insertBefore(captionNode, errorIndicatorElement, captionText);
+ } else if (errorIndicatorElement != null) {
+ DOM.removeChild(captionNode, errorIndicatorElement);
+ errorIndicatorElement = null;
+ }
+ }
+
+ 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);
+ }
+ }
+
+ @Override
+ 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);
+ return;
+ }
+ if (type == Event.ONSCROLL) {
+ int newscrollTop = DOM.getElementPropertyInt(contentNode,
+ "scrollTop");
+ int newscrollLeft = DOM.getElementPropertyInt(contentNode,
+ "scrollLeft");
+ if (client != null
+ && (newscrollLeft != scrollLeft || newscrollTop != scrollTop)) {
+ scrollLeft = newscrollLeft;
+ scrollTop = newscrollTop;
+ client.updateVariable(id, "scrollTop", scrollTop, false);
+ client.updateVariable(id, "scrollLeft", scrollLeft, false);
+ }
+ } else if (captionNode.isOrHasChild(target)) {
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
+ }
+ }
+
+ protected TouchScrollDelegate getTouchScrollDelegate() {
+ if (touchScrollDelegate == null) {
+ touchScrollDelegate = new TouchScrollDelegate(contentNode);
+ }
+ return touchScrollDelegate;
+
+ }
+
+ public ShortcutActionHandler getShortcutActionHandler() {
+ return shortcutHandler;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/passwordfield/PasswordFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/passwordfield/PasswordFieldConnector.java
new file mode 100644
index 0000000000..1d1ec58fbb
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/passwordfield/PasswordFieldConnector.java
@@ -0,0 +1,32 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.passwordfield;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.textfield.TextFieldConnector;
+import com.vaadin.ui.PasswordField;
+
+@Connect(PasswordField.class)
+public class PasswordFieldConnector extends TextFieldConnector {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ super.updateFromUIDL(uidl, client);
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VPasswordField.class);
+ }
+
+ @Override
+ public VPasswordField getWidget() {
+ return (VPasswordField) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java b/src/com/vaadin/terminal/gwt/client/ui/passwordfield/VPasswordField.java
index 5182457067..c160322de5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/passwordfield/VPasswordField.java
@@ -2,9 +2,10 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.passwordfield;
import com.google.gwt.user.client.DOM;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
/**
* This class represents a password field.
diff --git a/src/com/vaadin/terminal/gwt/client/ui/popupview/PopupViewConnector.java b/src/com/vaadin/terminal/gwt/client/ui/popupview/PopupViewConnector.java
new file mode 100644
index 0000000000..4faa6cec86
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/popupview/PopupViewConnector.java
@@ -0,0 +1,121 @@
+/*
+ @VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.popupview;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.VCaptionWrapper;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.ui.PopupView;
+
+@Connect(PopupView.class)
+public class PopupViewConnector extends AbstractComponentContainerConnector
+ implements Paintable, PostLayoutListener {
+
+ private boolean centerAfterLayout = false;
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @see com.vaadin.terminal.gwt.client.ComponentConnector#updateFromUIDL(com.vaadin.terminal.gwt.client.UIDL,
+ * com.vaadin.terminal.gwt.client.ApplicationConnection)
+ */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+ // These are for future server connections
+ getWidget().client = client;
+ getWidget().uidlId = uidl.getId();
+
+ getWidget().hostPopupVisible = uidl
+ .getBooleanVariable("popupVisibility");
+
+ getWidget().setHTML(uidl.getStringAttribute("html"));
+
+ if (uidl.hasAttribute("hideOnMouseOut")) {
+ getWidget().popup.setHideOnMouseOut(uidl
+ .getBooleanAttribute("hideOnMouseOut"));
+ }
+
+ // Render the popup if visible and show it.
+ if (getWidget().hostPopupVisible) {
+ UIDL popupUIDL = uidl.getChildUIDL(0);
+
+ // showPopupOnTop(popup, hostReference);
+ getWidget().preparePopup(getWidget().popup);
+ getWidget().popup.updateFromUIDL(popupUIDL, client);
+ if (getState().hasStyles()) {
+ final StringBuffer styleBuf = new StringBuffer();
+ final String primaryName = getWidget().popup
+ .getStylePrimaryName();
+ styleBuf.append(primaryName);
+ for (String style : getState().getStyles()) {
+ styleBuf.append(" ");
+ styleBuf.append(primaryName);
+ styleBuf.append("-");
+ styleBuf.append(style);
+ }
+ getWidget().popup.setStyleName(styleBuf.toString());
+ } else {
+ getWidget().popup.setStyleName(getWidget().popup
+ .getStylePrimaryName());
+ }
+ getWidget().showPopup(getWidget().popup);
+ centerAfterLayout = true;
+
+ // The popup shouldn't be visible, try to hide it.
+ } else {
+ getWidget().popup.hide();
+ }
+ }// updateFromUIDL
+
+ public void updateCaption(ComponentConnector component) {
+ if (VCaption.isNeeded(component.getState())) {
+ if (getWidget().popup.captionWrapper != null) {
+ getWidget().popup.captionWrapper.updateCaption();
+ } else {
+ getWidget().popup.captionWrapper = new VCaptionWrapper(
+ component, getConnection());
+ getWidget().popup.setWidget(getWidget().popup.captionWrapper);
+ getWidget().popup.captionWrapper.updateCaption();
+ }
+ } else {
+ if (getWidget().popup.captionWrapper != null) {
+ getWidget().popup
+ .setWidget(getWidget().popup.popupComponentWidget);
+ }
+ }
+ }
+
+ @Override
+ public VPopupView getWidget() {
+ return (VPopupView) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VPopupView.class);
+ }
+
+ public void postLayout() {
+ if (centerAfterLayout) {
+ centerAfterLayout = false;
+ getWidget().center();
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupView.java b/src/com/vaadin/terminal/gwt/client/ui/popupview/VPopupView.java
index 907e11ac2d..da48975726 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VPopupView.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/popupview/VPopupView.java
@@ -1,11 +1,10 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.popupview;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.NoSuchElementException;
import java.util.Set;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -24,29 +23,25 @@ import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation.Size;
-import com.vaadin.terminal.gwt.client.RenderSpace;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
import com.vaadin.terminal.gwt.client.UIDL;
-import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.VCaption;
import com.vaadin.terminal.gwt.client.VCaptionWrapper;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea;
-public class VPopupView extends HTML implements Container, Iterable<Widget> {
+public class VPopupView extends HTML {
public static final String CLASSNAME = "v-popupview";
/** For server-client communication */
- private String uidlId;
- private ApplicationConnection client;
+ String uidlId;
+ ApplicationConnection client;
/** This variable helps to communicate popup visibility to the server */
- private boolean hostPopupVisible;
+ boolean hostPopupVisible;
- private final CustomPopup popup;
+ final CustomPopup popup;
private final Label loading = new Label();
/**
@@ -82,61 +77,6 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
}
/**
- *
- *
- * @see com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal.gwt.client.UIDL,
- * com.vaadin.terminal.gwt.client.ApplicationConnection)
- */
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // This call should be made first. Ensure correct implementation,
- // and don't let the containing layout manage caption.
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
- // These are for future server connections
- this.client = client;
- uidlId = uidl.getId();
-
- hostPopupVisible = uidl.getBooleanVariable("popupVisibility");
-
- setHTML(uidl.getStringAttribute("html"));
-
- if (uidl.hasAttribute("hideOnMouseOut")) {
- popup.setHideOnMouseOut(uidl.getBooleanAttribute("hideOnMouseOut"));
- }
-
- // Render the popup if visible and show it.
- if (hostPopupVisible) {
- UIDL popupUIDL = uidl.getChildUIDL(0);
-
- // showPopupOnTop(popup, hostReference);
- preparePopup(popup);
- popup.updateFromUIDL(popupUIDL, client);
- if (uidl.hasAttribute("style")) {
- final String[] styles = uidl.getStringAttribute("style").split(
- " ");
- final StringBuffer styleBuf = new StringBuffer();
- final String primaryName = popup.getStylePrimaryName();
- styleBuf.append(primaryName);
- for (int i = 0; i < styles.length; i++) {
- styleBuf.append(" ");
- styleBuf.append(primaryName);
- styleBuf.append("-");
- styleBuf.append(styles[i]);
- }
- popup.setStyleName(styleBuf.toString());
- } else {
- popup.setStyleName(popup.getStylePrimaryName());
- }
- showPopup(popup);
-
- // The popup shouldn't be visible, try to hide it.
- } else {
- popup.hide();
- }
- }// updateFromUIDL
-
- /**
* Update popup visibility to server
*
* @param visibility
@@ -149,7 +89,7 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
}
}
- private void preparePopup(final CustomPopup popup) {
+ void preparePopup(final CustomPopup popup) {
popup.setVisible(false);
popup.show();
}
@@ -166,6 +106,12 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
* @param popup
*/
protected void showPopup(final CustomPopup popup) {
+ popup.setPopupPosition(0, 0);
+
+ popup.setVisible(true);
+ }
+
+ void center() {
int windowTop = RootPanel.get().getAbsoluteTop();
int windowLeft = RootPanel.get().getAbsoluteLeft();
int windowRight = windowLeft + RootPanel.get().getOffsetWidth();
@@ -200,8 +146,6 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
}
popup.setPopupPosition(left, top);
-
- popup.setVisible(true);
}
/**
@@ -230,9 +174,9 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
*/
protected class CustomPopup extends VOverlay {
- private Paintable popupComponentPaintable = null;
- private Widget popupComponentWidget = null;
- private VCaptionWrapper captionWrapper = null;
+ private ComponentConnector popupComponentPaintable = null;
+ Widget popupComponentWidget = null;
+ VCaptionWrapper captionWrapper = null;
private boolean hasHadMouseOver = false;
private boolean hideOnMouseOut = true;
@@ -285,7 +229,6 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
public void hide(boolean autoClosed) {
hiding = true;
syncChildren();
- unregisterPaintables();
if (popupComponentWidget != null && popupComponentWidget != loading) {
remove(popupComponentWidget);
}
@@ -346,26 +289,16 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- Paintable newPopupComponent = client.getPaintable(uidl
+ ComponentConnector newPopupComponent = client.getPaintable(uidl
.getChildUIDL(0));
if (newPopupComponent != popupComponentPaintable) {
-
- setWidget((Widget) newPopupComponent);
-
- popupComponentWidget = (Widget) newPopupComponent;
-
+ Widget newWidget = newPopupComponent.getWidget();
+ setWidget(newWidget);
+ popupComponentWidget = newWidget;
popupComponentPaintable = newPopupComponent;
}
- popupComponentPaintable
- .updateFromUIDL(uidl.getChildUIDL(0), client);
- }
-
- public void unregisterPaintables() {
- if (popupComponentPaintable != null) {
- client.unregisterPaintable(popupComponentPaintable);
- }
}
public void setHideOnMouseOut(boolean hideOnMouseOut) {
@@ -404,69 +337,6 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
}// class CustomPopup
- // Container methods
-
- public RenderSpace getAllocatedSpace(Widget child) {
- Size popupExtra = calculatePopupExtra();
-
- return new RenderSpace(RootPanel.get().getOffsetWidth()
- - popupExtra.getWidth(), RootPanel.get().getOffsetHeight()
- - popupExtra.getHeight());
- }
-
- /**
- * Calculate extra space taken by the popup decorations
- *
- * @return
- */
- protected Size calculatePopupExtra() {
- Element pe = popup.getElement();
- Element ipe = popup.getContainerElement();
-
- // border + padding
- int width = Util.getRequiredWidth(pe) - Util.getRequiredWidth(ipe);
- int height = Util.getRequiredHeight(pe) - Util.getRequiredHeight(ipe);
-
- return new Size(width, height);
- }
-
- public boolean hasChildComponent(Widget component) {
- if (popup.popupComponentWidget != null) {
- return popup.popupComponentWidget == component;
- } else {
- return false;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- popup.setWidget(newComponent);
- popup.popupComponentWidget = newComponent;
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- popup.updateShadowSizeAndPosition();
- return true;
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- if (VCaption.isNeeded(uidl)) {
- if (popup.captionWrapper != null) {
- popup.captionWrapper.updateCaption(uidl);
- } else {
- popup.captionWrapper = new VCaptionWrapper(component, client);
- popup.setWidget(popup.captionWrapper);
- popup.captionWrapper.updateCaption(uidl);
- }
- } else {
- if (popup.captionWrapper != null) {
- popup.setWidget(popup.popupComponentWidget);
- }
- }
-
- popup.popupComponentWidget = (Widget) component;
- popup.popupComponentPaintable = component;
- }
-
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
@@ -475,30 +345,4 @@ public class VPopupView extends HTML implements Container, Iterable<Widget> {
}
}
- public Iterator<Widget> iterator() {
- return new Iterator<Widget>() {
-
- int pos = 0;
-
- public boolean hasNext() {
- // There is a child widget only if next() has not been called.
- return (pos == 0);
- }
-
- public Widget next() {
- // Next can be called only once to return the popup.
- if (pos != 0) {
- throw new NoSuchElementException();
- }
- pos++;
- return popup;
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- };
- }
-
}// class VPopupView
diff --git a/src/com/vaadin/terminal/gwt/client/ui/progressindicator/ProgressIndicatorConnector.java b/src/com/vaadin/terminal/gwt/client/ui/progressindicator/ProgressIndicatorConnector.java
new file mode 100644
index 0000000000..09fa5fb44e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/progressindicator/ProgressIndicatorConnector.java
@@ -0,0 +1,66 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.progressindicator;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.ProgressIndicator;
+
+@Connect(ProgressIndicator.class)
+public class ProgressIndicatorConnector extends AbstractFieldConnector
+ implements Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // Save details
+ getWidget().client = client;
+
+ getWidget().indeterminate = uidl.getBooleanAttribute("indeterminate");
+
+ if (getWidget().indeterminate) {
+ String basename = VProgressIndicator.CLASSNAME + "-indeterminate";
+ getWidget().addStyleName(basename);
+ if (!isEnabled()) {
+ getWidget().addStyleName(basename + "-disabled");
+ } else {
+ getWidget().removeStyleName(basename + "-disabled");
+ }
+ } else {
+ try {
+ final float f = Float.parseFloat(uidl
+ .getStringAttribute("state"));
+ final int size = Math.round(100 * f);
+ DOM.setStyleAttribute(getWidget().indicator, "width", size
+ + "%");
+ } catch (final Exception e) {
+ }
+ }
+
+ if (isEnabled()) {
+ getWidget().interval = uidl.getIntAttribute("pollinginterval");
+ getWidget().poller.scheduleRepeating(getWidget().interval);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VProgressIndicator.class);
+ }
+
+ @Override
+ public VProgressIndicator getWidget() {
+ return (VProgressIndicator) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java b/src/com/vaadin/terminal/gwt/client/ui/progressindicator/VProgressIndicator.java
index d5eac590ad..bc64efb60a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/progressindicator/VProgressIndicator.java
@@ -2,27 +2,25 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.progressindicator;
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.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
-public class VProgressIndicator extends Widget implements Paintable {
+public class VProgressIndicator extends Widget {
- private static final String CLASSNAME = "v-progressindicator";
+ public static final String CLASSNAME = "v-progressindicator";
Element wrapper = DOM.createDiv();
Element indicator = DOM.createDiv();
- private ApplicationConnection client;
- private final Poller poller;
- private boolean indeterminate = false;
+ protected ApplicationConnection client;
+ protected final Poller poller;
+ protected boolean indeterminate = false;
private boolean pollerSuspendedDueDetach;
- private int interval;
+ protected int interval;
public VProgressIndicator() {
setElement(DOM.createDiv());
@@ -34,38 +32,6 @@ public class VProgressIndicator extends Widget implements Paintable {
poller = new Poller();
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- if (!uidl.getBooleanAttribute("cached")) {
- poller.cancel();
- }
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- indeterminate = uidl.getBooleanAttribute("indeterminate");
-
- if (indeterminate) {
- String basename = CLASSNAME + "-indeterminate";
- VProgressIndicator.setStyleName(getElement(), basename, true);
- VProgressIndicator.setStyleName(getElement(), basename
- + "-disabled", uidl.getBooleanAttribute("disabled"));
- } else {
- try {
- final float f = Float.parseFloat(uidl
- .getStringAttribute("state"));
- final int size = Math.round(100 * f);
- DOM.setStyleAttribute(indicator, "width", size + "%");
- } catch (final Exception e) {
- }
- }
-
- if (!uidl.getBooleanAttribute("disabled")) {
- interval = uidl.getIntAttribute("pollinginterval");
- poller.scheduleRepeating(interval);
- }
- }
-
@Override
protected void onAttach() {
super.onAttach();
@@ -102,5 +68,4 @@ public class VProgressIndicator extends Widget implements Paintable {
}
}
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/RichTextAreaConnector.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/RichTextAreaConnector.java
new file mode 100644
index 0000000000..01e79bc1c3
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/RichTextAreaConnector.java
@@ -0,0 +1,78 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.richtextarea;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
+import com.vaadin.ui.RichTextArea;
+
+@Connect(value = RichTextArea.class, loadStyle = LoadStyle.LAZY)
+public class RichTextAreaConnector extends AbstractFieldConnector implements
+ Paintable, BeforeShortcutActionListener {
+
+ public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
+ getWidget().client = client;
+ getWidget().id = uidl.getId();
+
+ if (uidl.hasVariable("text")) {
+ getWidget().currentValue = uidl.getStringVariable("text");
+ if (getWidget().rta.isAttached()) {
+ getWidget().rta.setHTML(getWidget().currentValue);
+ } else {
+ getWidget().html.setHTML(getWidget().currentValue);
+ }
+ }
+ if (isRealUpdate(uidl)) {
+ getWidget().setEnabled(isEnabled());
+ }
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().setReadOnly(isReadOnly());
+ getWidget().immediate = getState().isImmediate();
+ int newMaxLength = uidl.hasAttribute("maxLength") ? uidl
+ .getIntAttribute("maxLength") : -1;
+ if (newMaxLength >= 0) {
+ if (getWidget().maxLength == -1) {
+ getWidget().keyPressHandler = getWidget().rta
+ .addKeyPressHandler(getWidget());
+ }
+ getWidget().maxLength = newMaxLength;
+ } else if (getWidget().maxLength != -1) {
+ getWidget().getElement().setAttribute("maxlength", "");
+ getWidget().maxLength = -1;
+ getWidget().keyPressHandler.removeHandler();
+ }
+
+ if (uidl.hasAttribute("selectAll")) {
+ getWidget().selectAll();
+ }
+
+ }
+
+ public void onBeforeShortcutAction(Event e) {
+ getWidget().synchronizeContentToServer();
+ }
+
+ @Override
+ public VRichTextArea getWidget() {
+ return (VRichTextArea) super.getWidget();
+ };
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VRichTextArea.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java
index bf0a423474..eb062a3799 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java
@@ -17,7 +17,6 @@ import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
@@ -27,12 +26,10 @@ import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.ui.Field;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
-import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
/**
@@ -41,9 +38,8 @@ import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHan
* @author Vaadin Ltd.
*
*/
-public class VRichTextArea extends Composite implements Paintable, Field,
- ChangeHandler, BlurHandler, KeyPressHandler, KeyDownHandler,
- BeforeShortcutActionListener, Focusable {
+public class VRichTextArea extends Composite implements Field, ChangeHandler,
+ BlurHandler, KeyPressHandler, KeyDownHandler, Focusable {
/**
* The input node CSS classname.
@@ -54,13 +50,13 @@ public class VRichTextArea extends Composite implements Paintable, Field,
protected ApplicationConnection client;
- private boolean immediate = false;
+ boolean immediate = false;
- private RichTextArea rta;
+ RichTextArea rta;
private VRichTextToolbar formatter;
- private HTML html = new HTML();
+ HTML html = new HTML();
private final FlowPanel fp = new FlowPanel();
@@ -69,15 +65,15 @@ public class VRichTextArea extends Composite implements Paintable, Field,
private int extraHorizontalPixels = -1;
private int extraVerticalPixels = -1;
- private int maxLength = -1;
+ int maxLength = -1;
private int toolbarNaturalWidth = 500;
- private HandlerRegistration keyPressHandler;
+ HandlerRegistration keyPressHandler;
private ShortcutActionHandlerOwner hasShortcutActionHandler;
- private String currentValue = "";
+ String currentValue = "";
private boolean readOnly = false;
@@ -127,48 +123,7 @@ public class VRichTextArea extends Composite implements Paintable, Field,
}
}
- public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
- this.client = client;
- id = uidl.getId();
-
- if (uidl.hasVariable("text")) {
- currentValue = uidl.getStringVariable("text");
- if (rta.isAttached()) {
- rta.setHTML(currentValue);
- } else {
- html.setHTML(currentValue);
- }
- }
- if (!uidl.hasAttribute("cached")) {
- setEnabled(!uidl.getBooleanAttribute("disabled"));
- }
-
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- setReadOnly(uidl.getBooleanAttribute("readonly"));
- immediate = uidl.getBooleanAttribute("immediate");
- int newMaxLength = uidl.hasAttribute("maxLength") ? uidl
- .getIntAttribute("maxLength") : -1;
- if (newMaxLength >= 0) {
- if (maxLength == -1) {
- keyPressHandler = rta.addKeyPressHandler(this);
- }
- maxLength = newMaxLength;
- } else if (maxLength != -1) {
- getElement().setAttribute("maxlength", "");
- maxLength = -1;
- keyPressHandler.removeHandler();
- }
-
- if (uidl.hasAttribute("selectAll")) {
- selectAll();
- }
-
- }
-
- private void selectAll() {
+ void selectAll() {
/*
* There is a timing issue if trying to select all immediately on first
* render. Simple deferred command is not enough. Using Timer with
@@ -190,7 +145,7 @@ public class VRichTextArea extends Composite implements Paintable, Field,
}.schedule(320);
}
- private void setReadOnly(boolean b) {
+ void setReadOnly(boolean b) {
if (isReadOnly() != b) {
swapEditableArea();
readOnly = b;
@@ -346,7 +301,8 @@ public class VRichTextArea extends Composite implements Paintable, Field,
if (shortcutHandler != null) {
shortcutHandler
.handleKeyboardEvent(com.google.gwt.user.client.Event
- .as(event.getNativeEvent()), this);
+ .as(event.getNativeEvent()),
+ ConnectorMap.get(client).getConnector(this));
}
}
@@ -364,10 +320,6 @@ public class VRichTextArea extends Composite implements Paintable, Field,
return hasShortcutActionHandler;
}
- public void onBeforeShortcutAction(Event e) {
- synchronizeContentToServer();
- }
-
public int getTabIndex() {
return rta.getTabIndex();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java
new file mode 100644
index 0000000000..46f82d60b7
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java
@@ -0,0 +1,423 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.root;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+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.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.Focusable;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+import com.vaadin.terminal.gwt.client.ui.window.WindowConnector;
+import com.vaadin.ui.Root;
+
+@Connect(value = Root.class, loadStyle = LoadStyle.EAGER)
+public class RootConnector extends AbstractComponentContainerConnector
+ implements Paintable, MayScrollChildren {
+
+ private RootServerRpc rpc = RpcProxy.create(RootServerRpc.class, this);
+
+ private HandlerRegistration childStateChangeHandlerRegistration;
+
+ private final StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ // TODO Should use a more specific handler that only reacts to
+ // size changes
+ onChildSizeChange();
+ }
+ };
+
+ @Override
+ protected void init() {
+ super.init();
+ }
+
+ public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
+ ConnectorMap paintableMap = ConnectorMap.get(getConnection());
+ getWidget().rendering = true;
+ getWidget().id = getConnectorId();
+ boolean firstPaint = getWidget().connection == null;
+ getWidget().connection = client;
+
+ getWidget().immediate = getState().isImmediate();
+ getWidget().resizeLazy = uidl.hasAttribute(VRoot.RESIZE_LAZY);
+ String newTheme = uidl.getStringAttribute("theme");
+ if (getWidget().theme != null && !newTheme.equals(getWidget().theme)) {
+ // Complete page refresh is needed due css can affect layout
+ // calculations etc
+ getWidget().reloadHostPage();
+ } else {
+ getWidget().theme = newTheme;
+ }
+ // this also implicitly removes old styles
+ String styles = "";
+ styles += getWidget().getStylePrimaryName() + " ";
+ if (getState().hasStyles()) {
+ for (String style : getState().getStyles()) {
+ styles += style + " ";
+ }
+ }
+ if (!client.getConfiguration().isStandalone()) {
+ styles += getWidget().getStylePrimaryName() + "-embedded";
+ }
+ getWidget().setStyleName(styles.trim());
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ if (!getWidget().isEmbedded() && getState().getCaption() != null) {
+ // only change window title if we're in charge of the whole page
+ com.google.gwt.user.client.Window.setTitle(getState().getCaption());
+ }
+
+ // Process children
+ int childIndex = 0;
+
+ // Open URL:s
+ boolean isClosed = false; // was this window closed?
+ while (childIndex < uidl.getChildCount()
+ && "open".equals(uidl.getChildUIDL(childIndex).getTag())) {
+ final UIDL open = uidl.getChildUIDL(childIndex);
+ final String url = client.translateVaadinUri(open
+ .getStringAttribute("src"));
+ final String target = open.getStringAttribute("name");
+ if (target == null) {
+ // source will be opened to this browser window, but we may have
+ // to finish rendering this window in case this is a download
+ // (and window stays open).
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ VRoot.goTo(url);
+ }
+ });
+ } else if ("_self".equals(target)) {
+ // This window is closing (for sure). Only other opens are
+ // relevant in this change. See #3558, #2144
+ isClosed = true;
+ VRoot.goTo(url);
+ } else {
+ String options;
+ if (open.hasAttribute("border")) {
+ if (open.getStringAttribute("border").equals("minimal")) {
+ options = "menubar=yes,location=no,status=no";
+ } else {
+ options = "menubar=no,location=no,status=no";
+ }
+
+ } else {
+ options = "resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes";
+ }
+
+ if (open.hasAttribute("width")) {
+ int w = open.getIntAttribute("width");
+ options += ",width=" + w;
+ }
+ if (open.hasAttribute("height")) {
+ int h = open.getIntAttribute("height");
+ options += ",height=" + h;
+ }
+
+ Window.open(url, target, options);
+ }
+ childIndex++;
+ }
+ if (isClosed) {
+ // don't render the content, something else will be opened to this
+ // browser view
+ getWidget().rendering = false;
+ return;
+ }
+
+ // Handle other UIDL children
+ UIDL childUidl;
+ while ((childUidl = uidl.getChildUIDL(childIndex++)) != null) {
+ String tag = childUidl.getTag().intern();
+ if (tag == "actions") {
+ if (getWidget().actionHandler == null) {
+ getWidget().actionHandler = new ShortcutActionHandler(
+ getWidget().id, client);
+ }
+ getWidget().actionHandler.updateActionMap(childUidl);
+ } else if (tag == "execJS") {
+ String script = childUidl.getStringAttribute("script");
+ VRoot.eval(script);
+ } else if (tag == "notifications") {
+ for (final Iterator<?> it = childUidl.getChildIterator(); it
+ .hasNext();) {
+ final UIDL notification = (UIDL) it.next();
+ VNotification.showNotification(client, notification);
+ }
+ }
+ }
+
+ if (uidl.hasAttribute("focused")) {
+ // set focused component when render phase is finished
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ ComponentConnector paintable = (ComponentConnector) uidl
+ .getPaintableAttribute("focused", getConnection());
+
+ final Widget toBeFocused = paintable.getWidget();
+ /*
+ * Two types of Widgets can be focused, either implementing
+ * GWT HasFocus of a thinner Vaadin specific Focusable
+ * interface.
+ */
+ if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) {
+ final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused;
+ toBeFocusedWidget.setFocus(true);
+ } else if (toBeFocused instanceof Focusable) {
+ ((Focusable) toBeFocused).focus();
+ } else {
+ VConsole.log("Could not focus component");
+ }
+ }
+ });
+ }
+
+ // Add window listeners on first paint, to prevent premature
+ // variablechanges
+ if (firstPaint) {
+ Window.addWindowClosingHandler(getWidget());
+ Window.addResizeHandler(getWidget());
+ }
+
+ // finally set scroll position from UIDL
+ if (uidl.hasVariable("scrollTop")) {
+ getWidget().scrollable = true;
+ getWidget().scrollTop = uidl.getIntVariable("scrollTop");
+ DOM.setElementPropertyInt(getWidget().getElement(), "scrollTop",
+ getWidget().scrollTop);
+ getWidget().scrollLeft = uidl.getIntVariable("scrollLeft");
+ DOM.setElementPropertyInt(getWidget().getElement(), "scrollLeft",
+ getWidget().scrollLeft);
+ } else {
+ getWidget().scrollable = false;
+ }
+
+ if (uidl.hasAttribute("scrollTo")) {
+ final ComponentConnector connector = (ComponentConnector) uidl
+ .getPaintableAttribute("scrollTo", getConnection());
+ scrollIntoView(connector);
+ }
+
+ if (uidl.hasAttribute(VRoot.FRAGMENT_VARIABLE)) {
+ getWidget().currentFragment = uidl
+ .getStringAttribute(VRoot.FRAGMENT_VARIABLE);
+ if (!getWidget().currentFragment.equals(History.getToken())) {
+ History.newItem(getWidget().currentFragment, true);
+ }
+ } else {
+ // Initial request for which the server doesn't yet have a fragment
+ // (and haven't shown any interest in getting one)
+ getWidget().currentFragment = History.getToken();
+
+ // Include current fragment in the next request
+ client.updateVariable(getWidget().id, VRoot.FRAGMENT_VARIABLE,
+ getWidget().currentFragment, false);
+ }
+
+ getWidget().rendering = false;
+ }
+
+ public void init(String rootPanelId,
+ ApplicationConnection applicationConnection) {
+ DOM.sinkEvents(getWidget().getElement(), Event.ONKEYDOWN
+ | Event.ONSCROLL);
+
+ // iview is focused when created so element needs tabIndex
+ // 1 due 0 is at the end of natural tabbing order
+ DOM.setElementProperty(getWidget().getElement(), "tabIndex", "1");
+
+ RootPanel root = RootPanel.get(rootPanelId);
+
+ // Remove the v-app-loading or any splash screen added inside the div by
+ // the user
+ root.getElement().setInnerHTML("");
+
+ root.addStyleName("v-theme-"
+ + applicationConnection.getConfiguration().getThemeName());
+
+ root.add(getWidget());
+
+ if (applicationConnection.getConfiguration().isStandalone()) {
+ // set focus to iview element by default to listen possible keyboard
+ // shortcuts. For embedded applications this is unacceptable as we
+ // don't want to steal focus from the main page nor we don't want
+ // side-effects from focusing (scrollIntoView).
+ getWidget().getElement().focus();
+ }
+ }
+
+ private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
+
+ @Override
+ protected void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails) {
+ rpc.click(mouseDetails);
+ }
+
+ };
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP The main view never draws caption for its layout
+ }
+
+ @Override
+ public VRoot getWidget() {
+ return (VRoot) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VRoot.class);
+ }
+
+ protected ComponentConnector getContent() {
+ return (ComponentConnector) getState().getContent();
+ }
+
+ protected void onChildSizeChange() {
+ ComponentConnector child = getContent();
+ Style childStyle = child.getWidget().getElement().getStyle();
+ /*
+ * Must set absolute position if the child has relative height and
+ * there's a chance of horizontal scrolling as some browsers will
+ * otherwise not take the scrollbar into account when calculating the
+ * height. Assuming v-view does not have an undefined width for now, see
+ * #8460.
+ */
+ if (child.isRelativeHeight() && !BrowserInfo.get().isIE9()) {
+ childStyle.setPosition(Position.ABSOLUTE);
+ } else {
+ childStyle.clearPosition();
+ }
+ }
+
+ /**
+ * Checks if the given sub window is a child of this Root Connector
+ *
+ * @deprecated Should be replaced by a more generic mechanism for getting
+ * non-ComponentConnector children
+ * @param wc
+ * @return
+ */
+ @Deprecated
+ public boolean hasSubWindow(WindowConnector wc) {
+ return getChildren().contains(wc);
+ }
+
+ /**
+ * Return an iterator for current subwindows. This method is meant for
+ * testing purposes only.
+ *
+ * @return
+ */
+ public List<WindowConnector> getSubWindows() {
+ ArrayList<WindowConnector> windows = new ArrayList<WindowConnector>();
+ for (ComponentConnector child : getChildren()) {
+ if (child instanceof WindowConnector) {
+ windows.add((WindowConnector) child);
+ }
+ }
+ return windows;
+ }
+
+ @Override
+ public RootState getState() {
+ return (RootState) super.getState();
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ ComponentConnector oldChild = null;
+ ComponentConnector newChild = getContent();
+
+ for (ComponentConnector c : event.getOldChildren()) {
+ if (!(c instanceof WindowConnector)) {
+ oldChild = c;
+ break;
+ }
+ }
+
+ if (oldChild != newChild) {
+ if (childStateChangeHandlerRegistration != null) {
+ childStateChangeHandlerRegistration.removeHandler();
+ childStateChangeHandlerRegistration = null;
+ }
+ getWidget().setWidget(newChild.getWidget());
+ childStateChangeHandlerRegistration = newChild
+ .addStateChangeHandler(childStateChangeHandler);
+ // Must handle new child here as state change events are already
+ // fired
+ onChildSizeChange();
+ }
+
+ for (ComponentConnector c : getChildren()) {
+ if (c instanceof WindowConnector) {
+ WindowConnector wc = (WindowConnector) c;
+ wc.setWindowOrderAndPosition();
+ }
+ }
+
+ // Close removed sub windows
+ for (ComponentConnector c : event.getOldChildren()) {
+ if (c.getParent() != this && c instanceof WindowConnector) {
+ ((WindowConnector) c).getWidget().hide();
+ }
+ }
+ }
+
+ /**
+ * Tries to scroll the viewport so that the given connector is in view.
+ *
+ * @param componentConnector
+ * The connector which should be visible
+ *
+ */
+ public void scrollIntoView(final ComponentConnector componentConnector) {
+ if (componentConnector == null) {
+ return;
+ }
+
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ componentConnector.getWidget().getElement().scrollIntoView();
+ }
+ });
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java
new file mode 100644
index 0000000000..389500949d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java
@@ -0,0 +1,11 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.root;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.ClickRpc;
+
+public interface RootServerRpc extends ClickRpc, ServerRpc {
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootState.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootState.java
new file mode 100644
index 0000000000..85d5e45022
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootState.java
@@ -0,0 +1,20 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.root;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Connector;
+
+public class RootState extends ComponentState {
+ private Connector content;
+
+ public Connector getContent() {
+ return content;
+ }
+
+ public void setContent(Connector content) {
+ this.content = content;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java
new file mode 100644
index 0000000000..13fc55d7ea
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java
@@ -0,0 +1,321 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.root;
+
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.Focusable;
+import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
+
+/**
+ *
+ */
+public class VRoot extends SimplePanel implements ResizeHandler,
+ Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable {
+
+ public static final String FRAGMENT_VARIABLE = "fragment";
+
+ private static final String CLASSNAME = "v-view";
+
+ public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain";
+
+ String theme;
+
+ String id;
+
+ ShortcutActionHandler actionHandler;
+
+ /** stored width for IE resize optimization */
+ private int width;
+
+ /** stored height for IE resize optimization */
+ private int height;
+
+ ApplicationConnection connection;
+
+ /** Identifies the click event */
+ public static final String CLICK_EVENT_ID = "click";
+
+ /**
+ * We are postponing resize process with IE. IE bugs with scrollbars in some
+ * situations, that causes false onWindowResized calls. With Timer we will
+ * give IE some time to decide if it really wants to keep current size
+ * (scrollbars).
+ */
+ private Timer resizeTimer;
+
+ int scrollTop;
+
+ int scrollLeft;
+
+ boolean rendering;
+
+ boolean scrollable;
+
+ boolean immediate;
+
+ boolean resizeLazy = false;
+
+ /**
+ * Attribute name for the lazy resize setting .
+ */
+ public static final String RESIZE_LAZY = "rL";
+
+ private HandlerRegistration historyHandlerRegistration;
+
+ /**
+ * The current URI fragment, used to avoid sending updates if nothing has
+ * changed.
+ */
+ String currentFragment;
+
+ /**
+ * Listener for URI fragment changes. Notifies the server of the new value
+ * whenever the value changes.
+ */
+ private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {
+ public void onValueChange(ValueChangeEvent<String> event) {
+ String newFragment = event.getValue();
+
+ // Send the new fragment to the server if it has changed
+ if (!newFragment.equals(currentFragment) && connection != null) {
+ currentFragment = newFragment;
+ connection.updateVariable(id, FRAGMENT_VARIABLE, newFragment,
+ true);
+ }
+ }
+ };
+
+ private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
+ new ScheduledCommand() {
+ public void execute() {
+ windowSizeMaybeChanged(Window.getClientWidth(),
+ Window.getClientHeight());
+ }
+
+ });
+
+ public VRoot() {
+ super();
+ setStyleName(CLASSNAME);
+
+ // Allow focusing the view by using the focus() method, the view
+ // should not be in the document focus flow
+ getElement().setTabIndex(-1);
+ }
+
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+ historyHandlerRegistration = History
+ .addValueChangeHandler(historyChangeHandler);
+ currentFragment = History.getToken();
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+ historyHandlerRegistration.removeHandler();
+ historyHandlerRegistration = null;
+ }
+
+ /**
+ * Called when the window might have been resized.
+ *
+ * @param newWidth
+ * The new width of the window
+ * @param newHeight
+ * The new height of the window
+ */
+ protected void windowSizeMaybeChanged(int newWidth, int newHeight) {
+ boolean changed = false;
+ ComponentConnector connector = ConnectorMap.get(connection)
+ .getConnector(this);
+ if (width != newWidth) {
+ width = newWidth;
+ changed = true;
+ connector.getLayoutManager().reportOuterWidth(connector, newWidth);
+ VConsole.log("New window width: " + width);
+ }
+ if (height != newHeight) {
+ height = newHeight;
+ changed = true;
+ connector.getLayoutManager()
+ .reportOuterHeight(connector, newHeight);
+ VConsole.log("New window height: " + height);
+ }
+ if (changed) {
+ VConsole.log("Running layout functions due to window resize");
+
+ sendClientResized();
+
+ connector.getLayoutManager().layoutNow();
+ }
+ }
+
+ public String getTheme() {
+ return theme;
+ }
+
+ /**
+ * Used to reload host page on theme changes.
+ */
+ static native void reloadHostPage()
+ /*-{
+ $wnd.location.reload();
+ }-*/;
+
+ /**
+ * Evaluate the given script in the browser document.
+ *
+ * @param script
+ * Script to be executed.
+ */
+ static native void eval(String script)
+ /*-{
+ try {
+ if (script == null) return;
+ $wnd.eval(script);
+ } catch (e) {
+ }
+ }-*/;
+
+ /**
+ * Returns true if the body is NOT generated, i.e if someone else has made
+ * the page that we're running in. Otherwise we're in charge of the whole
+ * page.
+ *
+ * @return true if we're running embedded
+ */
+ public boolean isEmbedded() {
+ return !getElement().getOwnerDocument().getBody().getClassName()
+ .contains(ApplicationConnection.GENERATED_BODY_CLASSNAME);
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ int type = DOM.eventGetType(event);
+ if (type == Event.ONKEYDOWN && actionHandler != null) {
+ actionHandler.handleKeyboardEvent(event);
+ return;
+ } else if (scrollable && type == Event.ONSCROLL) {
+ updateScrollPosition();
+ }
+ }
+
+ /**
+ * Updates scroll position from DOM and saves variables to server.
+ */
+ private void updateScrollPosition() {
+ int oldTop = scrollTop;
+ int oldLeft = scrollLeft;
+ scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop");
+ scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft");
+ if (connection != null && !rendering) {
+ if (oldTop != scrollTop) {
+ connection.updateVariable(id, "scrollTop", scrollTop, false);
+ }
+ if (oldLeft != scrollLeft) {
+ connection.updateVariable(id, "scrollLeft", scrollLeft, false);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
+ * .gwt.event.logical.shared.ResizeEvent)
+ */
+ public void onResize(ResizeEvent event) {
+ onResize();
+ }
+
+ /**
+ * Called when a resize event is received.
+ */
+ void onResize() {
+ /*
+ * IE (pre IE9 at least) will give us some false resize events due to
+ * problems with scrollbars. Firefox 3 might also produce some extra
+ * events. We postpone both the re-layouting and the server side event
+ * for a while to deal with these issues.
+ *
+ * We may also postpone these events to avoid slowness when resizing the
+ * browser window. Constantly recalculating the layout causes the resize
+ * operation to be really slow with complex layouts.
+ */
+ boolean lazy = resizeLazy || BrowserInfo.get().isIE8();
+
+ if (lazy) {
+ delayedResizeExecutor.trigger();
+ } else {
+ windowSizeMaybeChanged(Window.getClientWidth(),
+ Window.getClientHeight());
+ }
+ }
+
+ /**
+ * Send new dimensions to the server.
+ */
+ private void sendClientResized() {
+ connection.updateVariable(id, "height", height, false);
+ connection.updateVariable(id, "width", width, immediate);
+ }
+
+ public native static void goTo(String url)
+ /*-{
+ $wnd.location = url;
+ }-*/;
+
+ public void onWindowClosing(Window.ClosingEvent event) {
+ // Change focus on this window in order to ensure that all state is
+ // collected from textfields
+ // TODO this is a naive hack, that only works with text fields and may
+ // cause some odd issues. Should be replaced with a decent solution, see
+ // also related BeforeShortcutActionListener interface. Same interface
+ // might be usable here.
+ VTextField.flushChangesFromFocusedTextField();
+ }
+
+ private native static void loadAppIdListFromDOM(ArrayList<String> list)
+ /*-{
+ var j;
+ for(j in $wnd.vaadin.vaadinConfigurations) {
+ list.@java.util.Collection::add(Ljava/lang/Object;)(j);
+ }
+ }-*/;
+
+ public ShortcutActionHandler getShortcutActionHandler() {
+ return actionHandler;
+ }
+
+ public void focus() {
+ getElement().focus();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/slider/SliderConnector.java b/src/com/vaadin/terminal/gwt/client/ui/slider/SliderConnector.java
new file mode 100644
index 0000000000..9cd3c35fee
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/slider/SliderConnector.java
@@ -0,0 +1,77 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.slider;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.Slider;
+
+@Connect(Slider.class)
+public class SliderConnector extends AbstractFieldConnector implements
+ Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+ getWidget().client = client;
+ getWidget().id = uidl.getId();
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().immediate = getState().isImmediate();
+ getWidget().disabled = !isEnabled();
+ getWidget().readonly = isReadOnly();
+
+ getWidget().vertical = uidl.hasAttribute("vertical");
+
+ // TODO should style names be used?
+
+ if (getWidget().vertical) {
+ getWidget().addStyleName(VSlider.CLASSNAME + "-vertical");
+ } else {
+ getWidget().removeStyleName(VSlider.CLASSNAME + "-vertical");
+ }
+
+ getWidget().min = uidl.getDoubleAttribute("min");
+ getWidget().max = uidl.getDoubleAttribute("max");
+ getWidget().resolution = uidl.getIntAttribute("resolution");
+ getWidget().value = new Double(uidl.getDoubleVariable("value"));
+
+ getWidget().setFeedbackValue(getWidget().value);
+
+ getWidget().buildBase();
+
+ if (!getWidget().vertical) {
+ // Draw handle with a delay to allow base to gain maximum width
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ getWidget().buildHandle();
+ getWidget().setValue(getWidget().value, false);
+ }
+ });
+ } else {
+ getWidget().buildHandle();
+ getWidget().setValue(getWidget().value, false);
+ }
+ }
+
+ @Override
+ public VSlider getWidget() {
+ return (VSlider) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VSlider.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSlider.java b/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java
index 4a46346613..9ff614252d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VSlider.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/slider/VSlider.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
//
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.slider;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -16,12 +16,14 @@ import com.google.gwt.user.client.ui.HTML;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.ContainerResizedListener;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.Field;
+import com.vaadin.terminal.gwt.client.ui.SimpleFocusablePanel;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
-public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
+public class VSlider extends SimpleFocusablePanel implements Field,
ContainerResizedListener {
public static final String CLASSNAME = "v-slider";
@@ -36,19 +38,16 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
String id;
- private boolean immediate;
- private boolean disabled;
- private boolean readonly;
- private boolean scrollbarStyle;
+ boolean immediate;
+ boolean disabled;
+ boolean readonly;
private int acceleration = 1;
- private int handleSize;
- private double min;
- private double max;
- private int resolution;
- private Double value;
- private boolean vertical;
- private boolean arrows;
+ double min;
+ double max;
+ int resolution;
+ Double value;
+ boolean vertical;
private final HTML feedback = new HTML("", false);
private final VOverlay feedbackPopup = new VOverlay(true, false, true) {
@@ -115,67 +114,7 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
feedbackPopup.setWidget(feedback);
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- this.client = client;
- id = uidl.getId();
-
- // Ensure correct implementation
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- immediate = uidl.getBooleanAttribute("immediate");
- disabled = uidl.getBooleanAttribute("disabled");
- readonly = uidl.getBooleanAttribute("readonly");
-
- vertical = uidl.hasAttribute("vertical");
- arrows = uidl.hasAttribute("arrows");
-
- String style = "";
- if (uidl.hasAttribute("style")) {
- style = uidl.getStringAttribute("style");
- }
-
- scrollbarStyle = style.indexOf("scrollbar") > -1;
-
- if (arrows) {
- DOM.setStyleAttribute(smaller, "display", "block");
- DOM.setStyleAttribute(bigger, "display", "block");
- }
-
- if (vertical) {
- addStyleName(CLASSNAME + "-vertical");
- } else {
- removeStyleName(CLASSNAME + "-vertical");
- }
-
- min = uidl.getDoubleAttribute("min");
- max = uidl.getDoubleAttribute("max");
- resolution = uidl.getIntAttribute("resolution");
- value = new Double(uidl.getDoubleVariable("value"));
-
- setFeedbackValue(value);
-
- handleSize = uidl.getIntAttribute("hsize");
-
- buildBase();
-
- if (!vertical) {
- // Draw handle with a delay to allow base to gain maximum width
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- buildHandle();
- setValue(value, false);
- }
- });
- } else {
- buildHandle();
- setValue(value, false);
- }
- }
-
- private void setFeedbackValue(double value) {
+ void setFeedbackValue(double value) {
String currentValue = "" + value;
if (resolution == 0) {
currentValue = "" + new Double(value).intValue();
@@ -198,7 +137,7 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
}
}
- private void buildBase() {
+ void buildBase() {
final String styleAttribute = vertical ? "height" : "width";
final String domProperty = vertical ? "offsetHeight" : "offsetWidth";
@@ -232,37 +171,17 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
// TODO attach listeners for focusing and arrow keys
}
- private void buildHandle() {
- final String styleAttribute = vertical ? "height" : "width";
+ void buildHandle() {
final String handleAttribute = vertical ? "marginTop" : "marginLeft";
- final String domProperty = vertical ? "offsetHeight" : "offsetWidth";
DOM.setStyleAttribute(handle, handleAttribute, "0");
- if (scrollbarStyle) {
- // Only stretch the handle if scrollbar style is set.
- int s = (int) (Double.parseDouble(DOM.getElementProperty(base,
- domProperty)) / 100 * handleSize);
- if (handleSize == -1) {
- final int baseS = Integer.parseInt(DOM.getElementProperty(base,
- domProperty));
- final double range = (max - min) * (resolution + 1) * 3;
- s = (int) (baseS - range);
- }
- if (s < 3) {
- s = 3;
- }
- DOM.setStyleAttribute(handle, styleAttribute, s + "px");
- } else {
- DOM.setStyleAttribute(handle, styleAttribute, "");
- }
-
// Restore visibility
DOM.setStyleAttribute(handle, "visibility", "visible");
}
- private void setValue(Double value, boolean updateToServer) {
+ void setValue(Double value, boolean updateToServer) {
if (value == null) {
return;
}
@@ -300,9 +219,7 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
p = 0;
}
if (vertical) {
- // IE6 rounding behaves a little unstable, reduce one pixel so the
- // containing element (base) won't expand without limits
- p = range - p - (BrowserInfo.get().isIE6() ? 1 : 0);
+ p = range - p;
}
final double pos = p;
@@ -356,7 +273,7 @@ public class VSlider extends SimpleFocusablePanel implements Paintable, Field,
} else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN) {
feedbackPopup.show();
}
- if(Util.isTouchEvent(event)) {
+ if (Util.isTouchEvent(event)) {
event.preventDefault(); // avoid simulated events
event.stopPropagation();
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java
new file mode 100644
index 0000000000..b3921204dc
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java
@@ -0,0 +1,206 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+import java.util.LinkedList;
+import java.util.List;
+
+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.Event;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelState.SplitterState;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.VAbstractSplitPanel.SplitterMoveHandler;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.VAbstractSplitPanel.SplitterMoveHandler.SplitterMoveEvent;
+
+public abstract class AbstractSplitPanelConnector extends
+ AbstractComponentContainerConnector implements SimpleManagedLayout {
+
+ private AbstractSplitPanelRpc rpc;
+
+ @Override
+ protected void init() {
+ super.init();
+ rpc = RpcProxy.create(AbstractSplitPanelRpc.class, this);
+ // TODO Remove
+ getWidget().client = getConnection();
+
+ getWidget().addHandler(new SplitterMoveHandler() {
+
+ public void splitterMoved(SplitterMoveEvent event) {
+ String position = getWidget().getSplitterPosition();
+ float pos = 0;
+ if (position.indexOf("%") > 0) {
+ // Send % values as a fraction to avoid that the splitter
+ // "jumps" when server responds with the integer pct value
+ // (e.g. dragged 16.6% -> should not jump to 17%)
+ pos = Float.valueOf(position.substring(0,
+ position.length() - 1));
+ } else {
+ pos = Integer.parseInt(position.substring(0,
+ position.length() - 2));
+ }
+
+ rpc.setSplitterPosition(pos);
+ }
+
+ }, SplitterMoveEvent.TYPE);
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // TODO Implement caption handling
+ }
+
+ ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
+
+ @Override
+ protected <H extends EventHandler> HandlerRegistration registerHandler(
+ H handler, Type<H> type) {
+ if ((Event.getEventsSunk(getWidget().splitter) & Event
+ .getTypeInt(type.getName())) != 0) {
+ // If we are already sinking the event for the splitter we do
+ // not want to additionally sink it for the root element
+ return getWidget().addHandler(handler, type);
+ } else {
+ return getWidget().addDomHandler(handler, type);
+ }
+ }
+
+ @Override
+ protected boolean shouldFireEvent(DomEvent<?> event) {
+ Element target = event.getNativeEvent().getEventTarget().cast();
+ if (!getWidget().splitter.isOrHasChild(target)) {
+ return false;
+ }
+
+ return super.shouldFireEvent(event);
+ };
+
+ @Override
+ protected Element getRelativeToElement() {
+ return getWidget().splitter;
+ };
+
+ @Override
+ protected void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails) {
+ rpc.splitterClick(mouseDetails);
+ }
+
+ };
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().setEnabled(isEnabled());
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ if (getState().hasStyles()) {
+ getWidget().componentStyleNames = getState().getStyles();
+ } else {
+ getWidget().componentStyleNames = new LinkedList<String>();
+ }
+
+ // Splitter updates
+ SplitterState splitterState = getState().getSplitterState();
+
+ getWidget().setLocked(splitterState.isLocked());
+ getWidget().setPositionReversed(splitterState.isPositionReversed());
+
+ getWidget().setStylenames();
+
+ getWidget().position = splitterState.getPosition()
+ + splitterState.getPositionUnit();
+
+ // This is needed at least for cases like #3458 to take
+ // appearing/disappearing scrollbars into account.
+ getConnection().runDescendentsLayout(getWidget());
+
+ getLayoutManager().setNeedsLayout(this);
+
+ }
+
+ public void layout() {
+ VAbstractSplitPanel splitPanel = getWidget();
+ splitPanel.setSplitPosition(splitPanel.position);
+ splitPanel.updateSizes();
+ // Report relative sizes in other direction for quicker propagation
+ List<ComponentConnector> children = getChildren();
+ for (ComponentConnector child : children) {
+ reportOtherDimension(child);
+ }
+ }
+
+ private void reportOtherDimension(ComponentConnector child) {
+ LayoutManager layoutManager = getLayoutManager();
+ if (this instanceof HorizontalSplitPanelConnector) {
+ if (child.isRelativeHeight()) {
+ int height = layoutManager.getInnerHeight(getWidget()
+ .getElement());
+ layoutManager.reportHeightAssignedToRelative(child, height);
+ }
+ } else {
+ if (child.isRelativeWidth()) {
+ int width = layoutManager.getInnerWidth(getWidget()
+ .getElement());
+ layoutManager.reportWidthAssignedToRelative(child, width);
+ }
+ }
+ }
+
+ @Override
+ public VAbstractSplitPanel getWidget() {
+ return (VAbstractSplitPanel) super.getWidget();
+ }
+
+ @Override
+ protected abstract VAbstractSplitPanel createWidget();
+
+ @Override
+ public AbstractSplitPanelState getState() {
+ return (AbstractSplitPanelState) super.getState();
+ }
+
+ private ComponentConnector getFirstChild() {
+ return (ComponentConnector) getState().getFirstChild();
+ }
+
+ private ComponentConnector getSecondChild() {
+ return (ComponentConnector) getState().getSecondChild();
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ Widget newFirstChildWidget = null;
+ if (getFirstChild() != null) {
+ newFirstChildWidget = getFirstChild().getWidget();
+ }
+ getWidget().setFirstWidget(newFirstChildWidget);
+
+ Widget newSecondChildWidget = null;
+ if (getSecondChild() != null) {
+ newSecondChildWidget = getSecondChild().getWidget();
+ }
+ getWidget().setSecondWidget(newSecondChildWidget);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java
new file mode 100644
index 0000000000..cc043838ff
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java
@@ -0,0 +1,28 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public interface AbstractSplitPanelRpc extends ServerRpc {
+
+ /**
+ * Called when the position has been updated by the user.
+ *
+ * @param position
+ * The new position in % if the current unit is %, in px
+ * otherwise
+ */
+ public void setSplitterPosition(float position);
+
+ /**
+ * Called when a click event has occurred on the splitter.
+ *
+ * @param mouseDetails
+ * Details about the mouse when the event took place
+ */
+ public void splitterClick(MouseEventDetails mouseDetails);
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java
new file mode 100644
index 0000000000..8b80eed840
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java
@@ -0,0 +1,88 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Connector;
+
+public class AbstractSplitPanelState extends ComponentState {
+
+ private Connector firstChild = null;
+ private Connector secondChild = null;
+ private SplitterState splitterState = new SplitterState();
+
+ public boolean hasFirstChild() {
+ return firstChild != null;
+ }
+
+ public boolean hasSecondChild() {
+ return secondChild != null;
+ }
+
+ public Connector getFirstChild() {
+ return firstChild;
+ }
+
+ public void setFirstChild(Connector firstChild) {
+ this.firstChild = firstChild;
+ }
+
+ public Connector getSecondChild() {
+ return secondChild;
+ }
+
+ public void setSecondChild(Connector secondChild) {
+ this.secondChild = secondChild;
+ }
+
+ public SplitterState getSplitterState() {
+ return splitterState;
+ }
+
+ public void setSplitterState(SplitterState splitterState) {
+ this.splitterState = splitterState;
+ }
+
+ public static class SplitterState implements Serializable {
+ private float position;
+ private String positionUnit;
+ private boolean positionReversed = false;
+ private boolean locked = false;
+
+ public float getPosition() {
+ return position;
+ }
+
+ public void setPosition(float position) {
+ this.position = position;
+ }
+
+ public String getPositionUnit() {
+ return positionUnit;
+ }
+
+ public void setPositionUnit(String positionUnit) {
+ this.positionUnit = positionUnit;
+ }
+
+ public boolean isPositionReversed() {
+ return positionReversed;
+ }
+
+ public void setPositionReversed(boolean positionReversed) {
+ this.positionReversed = positionReversed;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/HorizontalSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/HorizontalSplitPanelConnector.java
new file mode 100644
index 0000000000..7814ab8e13
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/HorizontalSplitPanelConnector.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.HorizontalSplitPanel;
+
+@Connect(value = HorizontalSplitPanel.class, loadStyle = LoadStyle.EAGER)
+public class HorizontalSplitPanelConnector extends AbstractSplitPanelConnector {
+
+ @Override
+ protected VAbstractSplitPanel createWidget() {
+ return GWT.create(VSplitPanelHorizontal.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java
index 51e378cc0c..166e79e92e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java
@@ -2,14 +2,12 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
-import java.util.Set;
+import java.util.List;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
-import com.google.gwt.event.dom.client.DomEvent.Type;
+import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchCancelHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
@@ -19,8 +17,7 @@ import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.EventHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Command;
+import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
@@ -28,64 +25,21 @@ import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
-import com.vaadin.terminal.gwt.client.ContainerResizedListener;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.LayoutManager;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.VAbstractSplitPanel.SplitterMoveHandler.SplitterMoveEvent;
-public class VSplitPanel extends ComplexPanel implements Container,
- ContainerResizedListener {
+public class VAbstractSplitPanel extends ComplexPanel {
private boolean enabled = false;
public static final String CLASSNAME = "v-splitpanel";
- public static final String SPLITTER_CLICK_EVENT_IDENTIFIER = "sp_click";
-
- private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
- SPLITTER_CLICK_EVENT_IDENTIFIER) {
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- if ((Event.getEventsSunk(splitter) & Event.getTypeInt(type
- .getName())) != 0) {
- // If we are already sinking the event for the splitter we do
- // not want to additionally sink it for the root element
- return addHandler(handler, type);
- } else {
- return addDomHandler(handler, type);
- }
- }
-
- @Override
- public void onContextMenu(
- com.google.gwt.event.dom.client.ContextMenuEvent event) {
- Element target = event.getNativeEvent().getEventTarget().cast();
- if (splitter.isOrHasChild(target)) {
- super.onContextMenu(event);
- }
- };
-
- @Override
- protected void fireClick(NativeEvent event) {
- Element target = event.getEventTarget().cast();
- if (splitter.isOrHasChild(target)) {
- super.fireClick(event);
- }
- }
-
- @Override
- protected Element getRelativeToElement() {
- return null;
- }
-
- };
-
public static final int ORIENTATION_HORIZONTAL = 0;
public static final int ORIENTATION_VERTICAL = 1;
@@ -94,9 +48,9 @@ public class VSplitPanel extends ComplexPanel implements Container,
private int orientation = ORIENTATION_HORIZONTAL;
- private Widget firstChild;
+ Widget firstChild;
- private Widget secondChild;
+ Widget secondChild;
private final Element wrapper = DOM.createDiv();
@@ -104,7 +58,7 @@ public class VSplitPanel extends ComplexPanel implements Container,
private final Element secondContainer = DOM.createDiv();
- private final Element splitter = DOM.createDiv();
+ final Element splitter = DOM.createDiv();
private boolean resizing;
@@ -122,29 +76,16 @@ public class VSplitPanel extends ComplexPanel implements Container,
private boolean positionReversed = false;
- private String[] componentStyleNames;
+ List<String> componentStyleNames;
private Element draggingCurtain;
- private ApplicationConnection client;
-
- private String width = "";
-
- private String height = "";
-
- private RenderSpace firstRenderSpace = new RenderSpace(0, 0, true);
- private RenderSpace secondRenderSpace = new RenderSpace(0, 0, true);
+ ApplicationConnection client;
- RenderInformation renderInformation = new RenderInformation();
-
- private String id;
-
- private boolean immediate;
-
- private boolean rendering = false;
+ boolean immediate;
/* The current position of the split handle in either percentages or pixels */
- private String position;
+ String position;
protected Element scrolledContainer;
@@ -152,11 +93,11 @@ public class VSplitPanel extends ComplexPanel implements Container,
private TouchScrollDelegate touchScrollDelegate;
- public VSplitPanel() {
+ public VAbstractSplitPanel() {
this(ORIENTATION_HORIZONTAL);
}
- public VSplitPanel(int orientation) {
+ public VAbstractSplitPanel(int orientation) {
setElement(DOM.createDiv());
switch (orientation) {
case ORIENTATION_HORIZONTAL:
@@ -256,73 +197,6 @@ public class VSplitPanel extends ComplexPanel implements Container,
+ "-second-container");
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- id = uidl.getId();
- rendering = true;
-
- immediate = uidl.hasAttribute("immediate");
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
- setEnabled(!uidl.getBooleanAttribute("disabled"));
-
- clickEventHandler.handleEventHandlerRegistration(client);
- if (uidl.hasAttribute("style")) {
- componentStyleNames = uidl.getStringAttribute("style").split(" ");
- } else {
- componentStyleNames = new String[0];
- }
-
- setLocked(uidl.getBooleanAttribute("locked"));
-
- setPositionReversed(uidl.getBooleanAttribute("reversed"));
-
- setStylenames();
-
- position = uidl.getStringAttribute("position");
- setSplitPosition(position);
-
- final Paintable newFirstChild = client.getPaintable(uidl
- .getChildUIDL(0));
- final Paintable newSecondChild = client.getPaintable(uidl
- .getChildUIDL(1));
- if (firstChild != newFirstChild) {
- if (firstChild != null) {
- client.unregisterPaintable((Paintable) firstChild);
- }
- setFirstWidget((Widget) newFirstChild);
- }
- if (secondChild != newSecondChild) {
- if (secondChild != null) {
- client.unregisterPaintable((Paintable) secondChild);
- }
- setSecondWidget((Widget) newSecondChild);
- }
- newFirstChild.updateFromUIDL(uidl.getChildUIDL(0), client);
- newSecondChild.updateFromUIDL(uidl.getChildUIDL(1), client);
-
- renderInformation.updateSize(getElement());
-
- if (BrowserInfo.get().isIE7()) {
- // Part III of IE7 hack
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- iLayout();
- }
- });
- }
-
- // This is needed at least for cases like #3458 to take
- // appearing/disappearing scrollbars into account.
- client.runDescendentsLayout(this);
-
- rendering = false;
-
- }
-
@Override
public boolean remove(Widget w) {
boolean removed = super.remove(w);
@@ -336,7 +210,7 @@ public class VSplitPanel extends ComplexPanel implements Container,
return removed;
}
- private void setLocked(boolean newValue) {
+ void setLocked(boolean newValue) {
if (locked != newValue) {
locked = newValue;
splitterSize = -1;
@@ -344,7 +218,7 @@ public class VSplitPanel extends ComplexPanel implements Container,
}
}
- private void setPositionReversed(boolean reversed) {
+ void setPositionReversed(boolean reversed) {
if (positionReversed != reversed) {
if (orientation == ORIENTATION_HORIZONTAL) {
DOM.setStyleAttribute(splitter, "right", "");
@@ -358,49 +232,47 @@ public class VSplitPanel extends ComplexPanel implements Container,
}
}
- private void setSplitPosition(String pos) {
+ void setSplitPosition(String pos) {
if (pos == null) {
return;
}
// Convert percentage values to pixels
if (pos.indexOf("%") > 0) {
- pos = Float.parseFloat(pos.substring(0, pos.length() - 1))
- / 100
- * (orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth()
- : getOffsetHeight()) + "px";
+ int size = orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth()
+ : getOffsetHeight();
+ float percentage = Float.parseFloat(pos.substring(0,
+ pos.length() - 1));
+ pos = percentage / 100 * size + "px";
}
+ String attributeName;
if (orientation == ORIENTATION_HORIZONTAL) {
if (positionReversed) {
- DOM.setStyleAttribute(splitter, "right", pos);
+ attributeName = "right";
} else {
- DOM.setStyleAttribute(splitter, "left", pos);
+ attributeName = "left";
}
} else {
if (positionReversed) {
- DOM.setStyleAttribute(splitter, "bottom", pos);
+ attributeName = "bottom";
} else {
- DOM.setStyleAttribute(splitter, "top", pos);
+ attributeName = "top";
}
}
- iLayout();
- client.runDescendentsLayout(this);
+ Style style = splitter.getStyle();
+ if (!pos.equals(style.getProperty(attributeName))) {
+ style.setProperty(attributeName, pos);
+ updateSizes();
+ }
}
- /*
- * Calculates absolutely positioned container places/sizes (non-Javadoc)
- *
- * @see com.vaadin.terminal.gwt.client.NeedsLayout#layout()
- */
- public void iLayout() {
+ void updateSizes() {
if (!isAttached()) {
return;
}
- renderInformation.updateSize(getElement());
-
int wholeSize;
int pixelPosition;
@@ -430,12 +302,28 @@ public class VSplitPanel extends ComplexPanel implements Container,
DOM.setStyleAttribute(secondContainer, "left",
(pixelPosition + getSplitterSize()) + "px");
- int contentHeight = renderInformation.getRenderedSize().getHeight();
- firstRenderSpace.setHeight(contentHeight);
- firstRenderSpace.setWidth(pixelPosition);
- secondRenderSpace.setHeight(contentHeight);
- secondRenderSpace.setWidth(secondContainerWidth);
-
+ LayoutManager layoutManager = LayoutManager.get(client);
+ ConnectorMap connectorMap = ConnectorMap.get(client);
+ if (firstChild != null) {
+ ComponentConnector connector = connectorMap
+ .getConnector(firstChild);
+ if (connector.isRelativeWidth()) {
+ layoutManager.reportWidthAssignedToRelative(connector,
+ pixelPosition);
+ } else {
+ layoutManager.setNeedsMeasure(connector);
+ }
+ }
+ if (secondChild != null) {
+ ComponentConnector connector = connectorMap
+ .getConnector(secondChild);
+ if (connector.isRelativeWidth()) {
+ layoutManager.reportWidthAssignedToRelative(connector,
+ secondContainerWidth);
+ } else {
+ layoutManager.setNeedsMeasure(connector);
+ }
+ }
break;
case ORIENTATION_VERTICAL:
wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight");
@@ -463,34 +351,50 @@ public class VSplitPanel extends ComplexPanel implements Container,
DOM.setStyleAttribute(secondContainer, "top",
(pixelPosition + getSplitterSize()) + "px");
- int contentWidth = renderInformation.getRenderedSize().getWidth();
- firstRenderSpace.setHeight(pixelPosition);
- firstRenderSpace.setWidth(contentWidth);
- secondRenderSpace.setHeight(secondContainerHeight);
- secondRenderSpace.setWidth(contentWidth);
-
+ layoutManager = LayoutManager.get(client);
+ connectorMap = ConnectorMap.get(client);
+ if (firstChild != null) {
+ ComponentConnector connector = connectorMap
+ .getConnector(firstChild);
+ if (connector.isRelativeHeight()) {
+ layoutManager.reportHeightAssignedToRelative(connector,
+ pixelPosition);
+ } else {
+ layoutManager.setNeedsMeasure(connector);
+ }
+ }
+ if (secondChild != null) {
+ ComponentConnector connector = connectorMap
+ .getConnector(secondChild);
+ if (connector.isRelativeHeight()) {
+ layoutManager.reportHeightAssignedToRelative(connector,
+ secondContainerHeight);
+ } else {
+ layoutManager.setNeedsMeasure(connector);
+ }
+ }
break;
}
- // fixes scrollbars issues on webkit based browsers
- Util.runWebkitOverflowAutoFix(secondContainer);
- Util.runWebkitOverflowAutoFix(firstContainer);
-
}
- private void setFirstWidget(Widget w) {
+ void setFirstWidget(Widget w) {
if (firstChild != null) {
firstChild.removeFromParent();
}
- super.add(w, firstContainer);
+ if (w != null) {
+ super.add(w, firstContainer);
+ }
firstChild = w;
}
- private void setSecondWidget(Widget w) {
+ void setSecondWidget(Widget w) {
if (secondChild != null) {
secondChild.removeFromParent();
}
- super.add(w, secondContainer);
+ if (w != null) {
+ super.add(w, secondContainer);
+ }
secondChild = w;
}
@@ -658,7 +562,38 @@ public class VSplitPanel extends ComplexPanel implements Container,
if (!Util.isTouchEvent(event)) {
onMouseMove(event);
}
- updateSplitPositionToServer();
+ fireEvent(new SplitterMoveEvent(this));
+ }
+
+ public interface SplitterMoveHandler extends EventHandler {
+ public void splitterMoved(SplitterMoveEvent event);
+
+ public static class SplitterMoveEvent extends
+ GwtEvent<SplitterMoveHandler> {
+
+ public static final Type<SplitterMoveHandler> TYPE = new Type<SplitterMoveHandler>();
+
+ private Widget splitPanel;
+
+ public SplitterMoveEvent(Widget splitPanel) {
+ this.splitPanel = splitPanel;
+ }
+
+ @Override
+ public com.google.gwt.event.shared.GwtEvent.Type<SplitterMoveHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(SplitterMoveHandler handler) {
+ handler.splitterMoved(this);
+ }
+
+ }
+ }
+
+ String getSplitterPosition() {
+ return position;
}
/**
@@ -723,98 +658,7 @@ public class VSplitPanel extends ComplexPanel implements Container,
return splitterSize;
}
- @Override
- public void setHeight(String height) {
- if (this.height.equals(height)) {
- return;
- }
-
- this.height = height;
- super.setHeight(height);
-
- if (!rendering && client != null) {
- setSplitPosition(position);
- }
- }
-
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
-
- this.width = width;
- super.setWidth(width);
-
- if (!rendering && client != null) {
- setSplitPosition(position);
- }
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- if (child == firstChild) {
- return firstRenderSpace;
- } else if (child == secondChild) {
- return secondRenderSpace;
- }
-
- return null;
- }
-
- public boolean hasChildComponent(Widget component) {
- return (component != null && (component == firstChild || component == secondChild));
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- if (oldComponent == firstChild) {
- setFirstWidget(newComponent);
- } else if (oldComponent == secondChild) {
- setSecondWidget(newComponent);
- }
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- // content size change might cause change to its available space
- // (scrollbars)
- for (Paintable paintable : child) {
- client.handleComponentRelativeSize((Widget) paintable);
- }
- if (height != null && width != null) {
- /*
- * If the height and width has been specified the child components
- * cannot make the size of the layout change
- */
-
- return true;
- }
-
- if (renderInformation.updateSize(getElement())) {
- return false;
- } else {
- return true;
- }
-
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // TODO Implement caption handling
- }
-
- /**
- * Updates the new split position back to server.
- */
- private void updateSplitPositionToServer() {
- float pos = 0;
- if (position.indexOf("%") > 0) {
- pos = Float.valueOf(position.substring(0, position.length() - 1));
- } else {
- pos = Integer
- .parseInt(position.substring(0, position.length() - 2));
- }
- client.updateVariable(id, "position", pos, immediate);
- }
-
- private void setStylenames() {
+ void setStylenames() {
final String splitterSuffix = (orientation == ORIENTATION_HORIZONTAL ? "-hsplitter"
: "-vsplitter");
final String firstContainerSuffix = "-first-container";
@@ -829,13 +673,12 @@ public class VSplitPanel extends ComplexPanel implements Container,
splitterStyle = CLASSNAME + splitterSuffix + "-locked";
lockedSuffix = "-locked";
}
- for (int i = 0; i < componentStyleNames.length; i++) {
- splitterStyle += " " + CLASSNAME + splitterSuffix + "-"
- + componentStyleNames[i] + lockedSuffix;
- firstStyle += " " + CLASSNAME + firstContainerSuffix + "-"
- + componentStyleNames[i];
+ for (String style : componentStyleNames) {
+ splitterStyle += " " + CLASSNAME + splitterSuffix + "-" + style
+ + lockedSuffix;
+ firstStyle += " " + CLASSNAME + firstContainerSuffix + "-" + style;
secondStyle += " " + CLASSNAME + secondContainerSuffix + "-"
- + componentStyleNames[i];
+ + style;
}
DOM.setElementProperty(splitter, "className", splitterStyle);
DOM.setElementProperty(firstContainer, "className", firstStyle);
@@ -849,4 +692,5 @@ public class VSplitPanel extends ComplexPanel implements Container,
public boolean isEnabled() {
return enabled;
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelHorizontal.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelHorizontal.java
new file mode 100644
index 0000000000..9048a59d7d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelHorizontal.java
@@ -0,0 +1,12 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+public class VSplitPanelHorizontal extends VAbstractSplitPanel {
+
+ public VSplitPanelHorizontal() {
+ super(VAbstractSplitPanel.ORIENTATION_HORIZONTAL);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelVertical.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelVertical.java
new file mode 100644
index 0000000000..d22ebed5d9
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VSplitPanelVertical.java
@@ -0,0 +1,12 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+public class VSplitPanelVertical extends VAbstractSplitPanel {
+
+ public VSplitPanelVertical() {
+ super(VAbstractSplitPanel.ORIENTATION_VERTICAL);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VerticalSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VerticalSplitPanelConnector.java
new file mode 100644
index 0000000000..83404177c0
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VerticalSplitPanelConnector.java
@@ -0,0 +1,19 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.splitpanel;
+
+import com.google.gwt.core.client.GWT;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.VerticalSplitPanel;
+
+@Connect(value = VerticalSplitPanel.class, loadStyle = LoadStyle.EAGER)
+public class VerticalSplitPanelConnector extends AbstractSplitPanelConnector {
+
+ @Override
+ protected VAbstractSplitPanel createWidget() {
+ return GWT.create(VSplitPanelVertical.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java
new file mode 100644
index 0000000000..f16ee3463f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java
@@ -0,0 +1,333 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.table;
+
+import java.util.Iterator;
+
+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.Style.Position;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable.ContextMenuDetails;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable.VScrollTableBody.VScrollTableRow;
+
+@Connect(com.vaadin.ui.Table.class)
+public class TableConnector extends AbstractComponentContainerConnector
+ implements Paintable, DirectionalManagedLayout, PostLayoutListener {
+
+ @Override
+ protected void init() {
+ super.init();
+ getWidget().init(getConnection());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal
+ * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection)
+ */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().rendering = true;
+
+ // If a row has an open context menu, it will be closed as the row is
+ // detached. Retain a reference here so we can restore the menu if
+ // required.
+ ContextMenuDetails contextMenuBeforeUpdate = getWidget().contextMenu;
+
+ if (uidl.hasAttribute(VScrollTable.ATTRIBUTE_PAGEBUFFER_FIRST)) {
+ getWidget().serverCacheFirst = uidl
+ .getIntAttribute(VScrollTable.ATTRIBUTE_PAGEBUFFER_FIRST);
+ getWidget().serverCacheLast = uidl
+ .getIntAttribute(VScrollTable.ATTRIBUTE_PAGEBUFFER_LAST);
+ } else {
+ getWidget().serverCacheFirst = -1;
+ getWidget().serverCacheLast = -1;
+ }
+ /*
+ * We need to do this before updateComponent since updateComponent calls
+ * this.setHeight() which will calculate a new body height depending on
+ * the space available.
+ */
+ if (uidl.hasAttribute("colfooters")) {
+ getWidget().showColFooters = uidl.getBooleanAttribute("colfooters");
+ }
+
+ getWidget().tFoot.setVisible(getWidget().showColFooters);
+
+ if (!isRealUpdate(uidl)) {
+ getWidget().rendering = false;
+ return;
+ }
+
+ getWidget().enabled = isEnabled();
+
+ if (BrowserInfo.get().isIE8() && !getWidget().enabled) {
+ /*
+ * The disabled shim will not cover the table body if it is relative
+ * in IE8. See #7324
+ */
+ getWidget().scrollBodyPanel.getElement().getStyle()
+ .setPosition(Position.STATIC);
+ } else if (BrowserInfo.get().isIE8()) {
+ getWidget().scrollBodyPanel.getElement().getStyle()
+ .setPosition(Position.RELATIVE);
+ }
+
+ getWidget().paintableId = uidl.getStringAttribute("id");
+ getWidget().immediate = getState().isImmediate();
+
+ int previousTotalRows = getWidget().totalRows;
+ getWidget().updateTotalRows(uidl);
+ boolean totalRowsChanged = (getWidget().totalRows != previousTotalRows);
+
+ getWidget().updateDragMode(uidl);
+
+ getWidget().updateSelectionProperties(uidl, getState(), isReadOnly());
+
+ if (uidl.hasAttribute("alb")) {
+ getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
+ } else {
+ // Need to clear the actions if the action handlers have been
+ // removed
+ getWidget().bodyActionKeys = null;
+ }
+
+ getWidget().setCacheRateFromUIDL(uidl);
+
+ getWidget().recalcWidths = uidl.hasAttribute("recalcWidths");
+ if (getWidget().recalcWidths) {
+ getWidget().tHead.clear();
+ getWidget().tFoot.clear();
+ }
+
+ getWidget().updatePageLength(uidl);
+
+ getWidget().updateFirstVisibleAndScrollIfNeeded(uidl);
+
+ getWidget().showRowHeaders = uidl.getBooleanAttribute("rowheaders");
+ getWidget().showColHeaders = uidl.getBooleanAttribute("colheaders");
+
+ getWidget().updateSortingProperties(uidl);
+
+ boolean keyboardSelectionOverRowFetchInProgress = getWidget()
+ .selectSelectedRows(uidl);
+
+ getWidget().updateActionMap(uidl);
+
+ getWidget().updateColumnProperties(uidl);
+
+ UIDL ac = uidl.getChildByTagName("-ac");
+ if (ac == null) {
+ if (getWidget().dropHandler != null) {
+ // remove dropHandler if not present anymore
+ getWidget().dropHandler = null;
+ }
+ } else {
+ if (getWidget().dropHandler == null) {
+ getWidget().dropHandler = getWidget().new VScrollTableDropHandler();
+ }
+ getWidget().dropHandler.updateAcceptRules(ac);
+ }
+
+ UIDL partialRowAdditions = uidl.getChildByTagName("prows");
+ UIDL partialRowUpdates = uidl.getChildByTagName("urows");
+ if (partialRowUpdates != null || partialRowAdditions != null) {
+ // we may have pending cache row fetch, cancel it. See #2136
+ getWidget().rowRequestHandler.cancel();
+
+ getWidget().updateRowsInBody(partialRowUpdates);
+ getWidget().addAndRemoveRows(partialRowAdditions);
+ } else {
+ UIDL rowData = uidl.getChildByTagName("rows");
+ if (rowData != null) {
+ // we may have pending cache row fetch, cancel it. See #2136
+ getWidget().rowRequestHandler.cancel();
+
+ if (!getWidget().recalcWidths
+ && getWidget().initializedAndAttached) {
+ getWidget().updateBody(rowData,
+ uidl.getIntAttribute("firstrow"),
+ uidl.getIntAttribute("rows"));
+ if (getWidget().headerChangedDuringUpdate) {
+ getWidget().triggerLazyColumnAdjustment(true);
+ } else if (!getWidget().isScrollPositionVisible()
+ || totalRowsChanged
+ || getWidget().lastRenderedHeight != getWidget().scrollBody
+ .getOffsetHeight()) {
+ // webkits may still bug with their disturbing scrollbar
+ // bug, see #3457
+ // Run overflow fix for the scrollable area
+ // #6698 - If there's a scroll going on, don't abort it
+ // by changing overflows as the length of the contents
+ // *shouldn't* have changed (unless the number of rows
+ // or the height of the widget has also changed)
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ Util.runWebkitOverflowAutoFix(getWidget().scrollBodyPanel
+ .getElement());
+ }
+ });
+ }
+ } else {
+ getWidget().initializeRows(uidl, rowData);
+ }
+ }
+ }
+
+ // If a row had an open context menu before the update, and after the
+ // update there's a row with the same key as that row, restore the
+ // context menu. See #8526.
+ showSavedContextMenu(contextMenuBeforeUpdate);
+
+ if (!getWidget().isSelectable()) {
+ getWidget().scrollBody.addStyleName(VScrollTable.CLASSNAME
+ + "-body-noselection");
+ } else {
+ getWidget().scrollBody.removeStyleName(VScrollTable.CLASSNAME
+ + "-body-noselection");
+ }
+
+ getWidget().hideScrollPositionAnnotation();
+
+ // selection is no in sync with server, avoid excessive server visits by
+ // clearing to flag used during the normal operation
+ if (!keyboardSelectionOverRowFetchInProgress) {
+ getWidget().selectionChanged = false;
+ }
+
+ /*
+ * This is called when the Home or page up button has been pressed in
+ * selectable mode and the next selected row was not yet rendered in the
+ * client
+ */
+ if (getWidget().selectFirstItemInNextRender
+ || getWidget().focusFirstItemInNextRender) {
+ getWidget().selectFirstRenderedRowInViewPort(
+ getWidget().focusFirstItemInNextRender);
+ getWidget().selectFirstItemInNextRender = getWidget().focusFirstItemInNextRender = false;
+ }
+
+ /*
+ * This is called when the page down or end button has been pressed in
+ * selectable mode and the next selected row was not yet rendered in the
+ * client
+ */
+ if (getWidget().selectLastItemInNextRender
+ || getWidget().focusLastItemInNextRender) {
+ getWidget().selectLastRenderedRowInViewPort(
+ getWidget().focusLastItemInNextRender);
+ getWidget().selectLastItemInNextRender = getWidget().focusLastItemInNextRender = false;
+ }
+ getWidget().multiselectPending = false;
+
+ if (getWidget().focusedRow != null) {
+ if (!getWidget().focusedRow.isAttached()
+ && !getWidget().rowRequestHandler.isRunning()) {
+ // focused row has been orphaned, can't focus
+ getWidget().focusRowFromBody();
+ }
+ }
+
+ getWidget().tabIndex = uidl.hasAttribute("tabindex") ? uidl
+ .getIntAttribute("tabindex") : 0;
+ getWidget().setProperTabIndex();
+
+ getWidget().resizeSortedColumnForSortIndicator();
+
+ // Remember this to detect situations where overflow hack might be
+ // needed during scrolling
+ getWidget().lastRenderedHeight = getWidget().scrollBody
+ .getOffsetHeight();
+
+ getWidget().rendering = false;
+ getWidget().headerChangedDuringUpdate = false;
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VScrollTable.class);
+ }
+
+ @Override
+ public VScrollTable getWidget() {
+ return (VScrollTable) super.getWidget();
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP, not rendered
+ }
+
+ public void layoutVertically() {
+ getWidget().updateHeight();
+ }
+
+ public void layoutHorizontally() {
+ getWidget().updateWidth();
+ }
+
+ public void postLayout() {
+ VScrollTable table = getWidget();
+ if (table.sizeNeedsInit) {
+ table.sizeInit();
+ Scheduler.get().scheduleFinally(new ScheduledCommand() {
+ public void execute() {
+ getLayoutManager().setNeedsMeasure(TableConnector.this);
+ getLayoutManager().setNeedsMeasure(
+ TableConnector.this.getParent());
+ getLayoutManager().setNeedsVerticalLayout(
+ TableConnector.this);
+ getLayoutManager().layoutNow();
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly() || getState().isPropertyReadOnly();
+ }
+
+ @Override
+ public AbstractFieldState getState() {
+ return (AbstractFieldState) super.getState();
+ }
+
+ /**
+ * Shows a saved row context menu if the row for the context menu is still
+ * visible. Does nothing if a context menu has not been saved.
+ *
+ * @param savedContextMenu
+ */
+ public void showSavedContextMenu(ContextMenuDetails savedContextMenu) {
+ if (isEnabled() && savedContextMenu != null) {
+ Iterator<Widget> iterator = getWidget().scrollBody.iterator();
+ while (iterator.hasNext()) {
+ Widget w = iterator.next();
+ VScrollTableRow row = (VScrollTableRow) w;
+ if (row.getKey().equals(savedContextMenu.rowKey)) {
+ getWidget().contextMenu = savedContextMenu;
+ getConnection().getContextMenu().showAt(row,
+ savedContextMenu.left, savedContextMenu.top);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
index a022a2bd83..c45c26c4ac 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.table;
import java.util.ArrayList;
import java.util.Collection;
@@ -55,6 +55,7 @@ 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.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;
@@ -62,17 +63,22 @@ import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Container;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.Focusable;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
import com.vaadin.terminal.gwt.client.TooltipInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
-import com.vaadin.terminal.gwt.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow;
+import com.vaadin.terminal.gwt.client.ui.Action;
+import com.vaadin.terminal.gwt.client.ui.ActionOwner;
+import com.vaadin.terminal.gwt.client.ui.FocusableScrollPanel;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
+import com.vaadin.terminal.gwt.client.ui.TreeAction;
import com.vaadin.terminal.gwt.client.ui.dd.DDUtil;
import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler;
import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCallback;
@@ -81,6 +87,10 @@ import com.vaadin.terminal.gwt.client.ui.dd.VDragEvent;
import com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler;
import com.vaadin.terminal.gwt.client.ui.dd.VTransferable;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.terminal.gwt.client.ui.embedded.VEmbedded;
+import com.vaadin.terminal.gwt.client.ui.label.VLabel;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable.VScrollTableBody.VScrollTableRow;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
/**
* VScrollTable
@@ -105,17 +115,31 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
*
* TODO implement unregistering for child components in Cells
*/
-public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
- VHasDropHandler, FocusHandler, BlurHandler, Focusable, ActionOwner {
+public class VScrollTable extends FlowPanel implements HasWidgets,
+ ScrollHandler, VHasDropHandler, FocusHandler, BlurHandler, Focusable,
+ ActionOwner {
- public static final String ATTRIBUTE_PAGEBUFFER_FIRST = "pb-ft";
- public static final String ATTRIBUTE_PAGEBUFFER_LAST = "pb-l";
+ public enum SelectMode {
+ NONE(0), SINGLE(1), MULTI(2);
+ private int id;
+
+ private SelectMode(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+ }
private static final String ROW_HEADER_COLUMN_KEY = "0";
public static final String CLASSNAME = "v-table";
public static final String CLASSNAME_SELECTION_FOCUS = CLASSNAME + "-focus";
+ public static final String ATTRIBUTE_PAGEBUFFER_FIRST = "pb-ft";
+ public static final String ATTRIBUTE_PAGEBUFFER_LAST = "pb-l";
+
public static final String ITEM_CLICK_EVENT_ID = "itemClick";
public static final String HEADER_CLICK_EVENT_ID = "handleHeaderClick";
public static final String FOOTER_CLICK_EVENT_ID = "handleFooterClick";
@@ -164,10 +188,10 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
protected ApplicationConnection client;
protected String paintableId;
- private boolean immediate;
+ boolean immediate;
private boolean nullSelectionAllowed = true;
- private int selectMode = Table.SELECT_MODE_NONE;
+ private SelectMode selectMode = SelectMode.NONE;
private final HashSet<String> selectedRowKeys = new HashSet<String>();
@@ -180,15 +204,15 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
/*
* These are used when jumping between pages when pressing Home and End
*/
- private boolean selectLastItemInNextRender = false;
- private boolean selectFirstItemInNextRender = false;
- private boolean focusFirstItemInNextRender = false;
- private boolean focusLastItemInNextRender = false;
+ boolean selectLastItemInNextRender = false;
+ boolean selectFirstItemInNextRender = false;
+ boolean focusFirstItemInNextRender = false;
+ boolean focusLastItemInNextRender = false;
/*
* The currently focused row
*/
- private VScrollTableRow focusedRow;
+ VScrollTableRow focusedRow;
/*
* Helper to store selection range start in when using the keyboard
@@ -199,7 +223,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* Flag for notifying when the selection has changed and should be sent to
* the server
*/
- private boolean selectionChanged = false;
+ boolean selectionChanged = false;
/*
* The speed (in pixels) which the scrolling scrolls vertically/horizontally
@@ -208,7 +232,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private Timer scrollingVelocityTimer = null;
- private String[] bodyActionKeys;
+ String[] bodyActionKeys;
private boolean enableDebug = false;
@@ -283,19 +307,18 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private final HashSet<SelectionRange> selectedRowRanges = new HashSet<SelectionRange>();
- private boolean initializedAndAttached = false;
+ boolean initializedAndAttached = false;
/**
* Flag to indicate if a column width recalculation is needed due update.
*/
- private boolean headerChangedDuringUpdate = false;
+ boolean headerChangedDuringUpdate = false;
protected final TableHead tHead = new TableHead();
- private final TableFooter tFoot = new TableFooter();
+ final TableFooter tFoot = new TableFooter();
- private final FocusableScrollPanel scrollBodyPanel = new FocusableScrollPanel(
- true);
+ final FocusableScrollPanel scrollBodyPanel = new FocusableScrollPanel(true);
private KeyPressHandler navKeyPressHandler = new KeyPressHandler() {
public void onKeyPress(KeyPressEvent keyPressEvent) {
@@ -384,12 +407,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
};
- private int totalRows;
+ int totalRows;
private Set<String> collapsedColumns;
- private final RowRequestHandler rowRequestHandler;
- private VScrollTableBody scrollBody;
+ final RowRequestHandler rowRequestHandler;
+ VScrollTableBody scrollBody;
private int firstvisible = 0;
private boolean sortAscending;
private String sortColumn;
@@ -404,9 +427,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private String[] visibleColOrder;
private boolean initialContentReceived = false;
private Element scrollPositionElement;
- private boolean enabled;
- private boolean showColHeaders;
- private boolean showColFooters;
+ boolean enabled;
+ boolean showColHeaders;
+ boolean showColFooters;
/** flag to indicate that table body has changed */
private boolean isNewBody = true;
@@ -418,18 +441,15 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
*/
boolean recalcWidths = false;
- private final ArrayList<Panel> lazyUnregistryBag = new ArrayList<Panel>();
- private String height;
- private String width = "";
- private boolean rendering = false;
+ boolean rendering = false;
private boolean hasFocus = false;
private int dragmode;
private int multiselectmode;
- private int tabIndex;
+ int tabIndex;
private TouchScrollDelegate touchScrollDelegate;
- private int lastRenderedHeight;
+ int lastRenderedHeight;
/**
* Values (serverCacheFirst+serverCacheLast) sent by server that tells which
@@ -443,14 +463,16 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* scrolling in the client will cause empty buttons to be rendered
* (cached=true request for non-existing components)
*/
- private int serverCacheFirst = -1;
- private int serverCacheLast = -1;
+ int serverCacheFirst = -1;
+ int serverCacheLast = -1;
+
+ boolean sizeNeedsInit = true;
/**
* Used to recall the position of an open context menu if we need to close
* and reopen it during a row update.
*/
- private class ContextMenuDetails {
+ class ContextMenuDetails {
String rowKey;
int left;
int top;
@@ -462,7 +484,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- ContextMenuDetails contextMenu;
+ protected ContextMenuDetails contextMenu = null;
public VScrollTable() {
setMultiSelectMode(MULTISELECT_MODE_DEFAULT);
@@ -509,6 +531,17 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
rowRequestHandler = new RowRequestHandler();
}
+ public void init(ApplicationConnection client) {
+ this.client = client;
+ // Add a handler to clear saved context menu details when the menu
+ // closes. See #8526.
+ client.getContextMenu().addCloseHandler(new CloseHandler<PopupPanel>() {
+ public void onClose(CloseEvent<PopupPanel> event) {
+ contextMenu = null;
+ }
+ });
+ }
+
protected TouchScrollDelegate getTouchScrollDelegate() {
if (touchScrollDelegate == null) {
touchScrollDelegate = new TouchScrollDelegate(
@@ -830,243 +863,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return KeyCodes.KEY_END;
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal
- * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection)
- */
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
-
- // On the first rendering, add a handler to clear saved context menu
- // details when the menu closes. See #8526.
- if (this.client == null) {
- client.getContextMenu().addCloseHandler(
- new CloseHandler<PopupPanel>() {
- public void onClose(CloseEvent<PopupPanel> event) {
- contextMenu = null;
- }
- });
- }
- // If a row has an open context menu, it will be closed as the row is
- // detached. Retain a reference here so we can restore the menu if
- // required.
- ContextMenuDetails savedContextMenu = contextMenu;
-
- if (uidl.hasAttribute(ATTRIBUTE_PAGEBUFFER_FIRST)) {
- serverCacheFirst = uidl.getIntAttribute(ATTRIBUTE_PAGEBUFFER_FIRST);
- serverCacheLast = uidl.getIntAttribute(ATTRIBUTE_PAGEBUFFER_LAST);
- } else {
- serverCacheFirst = -1;
- serverCacheLast = -1;
- }
- /*
- * We need to do this before updateComponent since updateComponent calls
- * this.setHeight() which will calculate a new body height depending on
- * the space available.
- */
- if (uidl.hasAttribute("colfooters")) {
- showColFooters = uidl.getBooleanAttribute("colfooters");
- }
-
- tFoot.setVisible(showColFooters);
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
-
- enabled = !uidl.hasAttribute("disabled");
-
- if (BrowserInfo.get().isIE8() && !enabled) {
- /*
- * The disabled shim will not cover the table body if it is relative
- * in IE8. See #7324
- */
- scrollBodyPanel.getElement().getStyle()
- .setPosition(Position.STATIC);
- } else if (BrowserInfo.get().isIE8()) {
- scrollBodyPanel.getElement().getStyle()
- .setPosition(Position.RELATIVE);
- }
-
- this.client = client;
- paintableId = uidl.getStringAttribute("id");
- immediate = uidl.getBooleanAttribute("immediate");
-
- int previousTotalRows = totalRows;
- updateTotalRows(uidl);
- boolean totalRowsChanged = (totalRows != previousTotalRows);
-
- updateDragMode(uidl);
-
- updateSelectionProperties(uidl);
-
- if (uidl.hasAttribute("alb")) {
- bodyActionKeys = uidl.getStringArrayAttribute("alb");
- } else {
- // Need to clear the actions if the action handlers have been
- // removed
- bodyActionKeys = null;
- }
-
- setCacheRateFromUIDL(uidl);
-
- recalcWidths = uidl.hasAttribute("recalcWidths");
- if (recalcWidths) {
- tHead.clear();
- tFoot.clear();
- }
-
- updatePageLength(uidl);
-
- updateFirstVisibleAndScrollIfNeeded(uidl);
-
- showRowHeaders = uidl.getBooleanAttribute("rowheaders");
- showColHeaders = uidl.getBooleanAttribute("colheaders");
-
- updateSortingProperties(uidl);
-
- boolean keyboardSelectionOverRowFetchInProgress = selectSelectedRows(uidl);
-
- updateActionMap(uidl);
-
- updateColumnProperties(uidl);
-
- UIDL ac = uidl.getChildByTagName("-ac");
- if (ac == null) {
- if (dropHandler != null) {
- // remove dropHandler if not present anymore
- dropHandler = null;
- }
- } else {
- if (dropHandler == null) {
- dropHandler = new VScrollTableDropHandler();
- }
- dropHandler.updateAcceptRules(ac);
- }
-
- UIDL partialRowAdditions = uidl.getChildByTagName("prows");
- UIDL partialRowUpdates = uidl.getChildByTagName("urows");
- if (partialRowUpdates != null || partialRowAdditions != null) {
- // we may have pending cache row fetch, cancel it. See #2136
- rowRequestHandler.cancel();
-
- updateRowsInBody(partialRowUpdates);
- addAndRemoveRows(partialRowAdditions);
- } else {
- UIDL rowData = uidl.getChildByTagName("rows");
- if (rowData != null) {
- // we may have pending cache row fetch, cancel it. See #2136
- rowRequestHandler.cancel();
-
- if (!recalcWidths && initializedAndAttached) {
- updateBody(rowData, uidl.getIntAttribute("firstrow"),
- uidl.getIntAttribute("rows"));
- if (headerChangedDuringUpdate) {
- triggerLazyColumnAdjustment(true);
- } else if (!isScrollPositionVisible()
- || totalRowsChanged
- || lastRenderedHeight != scrollBody
- .getOffsetHeight()) {
- // webkits may still bug with their disturbing scrollbar
- // bug, see #3457
- // Run overflow fix for the scrollable area
- // #6698 - If there's a scroll going on, don't abort it
- // by changing overflows as the length of the contents
- // *shouldn't* have changed (unless the number of rows
- // or the height of the widget has also changed)
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- Util.runWebkitOverflowAutoFix(scrollBodyPanel
- .getElement());
- }
- });
- }
- } else {
- initializeRows(uidl, rowData);
- }
- }
- }
-
- // If a row had an open context menu before the update, and after the
- // update there's a row with the same key as that row, restore the
- // context menu. See #8526.
- if (enabled && savedContextMenu != null) {
- for (Widget w : scrollBody.renderedRows) {
- VScrollTableRow row = (VScrollTableRow) w;
- if (row.isVisible()
- && row.getKey().equals(savedContextMenu.rowKey)) {
- contextMenu = savedContextMenu;
- client.getContextMenu().showAt(row, savedContextMenu.left,
- savedContextMenu.top);
- }
- }
- }
-
- if (!isSelectable()) {
- scrollBody.addStyleName(CLASSNAME + "-body-noselection");
- } else {
- scrollBody.removeStyleName(CLASSNAME + "-body-noselection");
- }
-
- hideScrollPositionAnnotation();
- purgeUnregistryBag();
-
- // selection is no in sync with server, avoid excessive server visits by
- // clearing to flag used during the normal operation
- if (!keyboardSelectionOverRowFetchInProgress) {
- selectionChanged = false;
- }
-
- /*
- * This is called when the Home or page up button has been pressed in
- * selectable mode and the next selected row was not yet rendered in the
- * client
- */
- if (selectFirstItemInNextRender || focusFirstItemInNextRender) {
- selectFirstRenderedRowInViewPort(focusFirstItemInNextRender);
- selectFirstItemInNextRender = focusFirstItemInNextRender = false;
- }
-
- /*
- * This is called when the page down or end button has been pressed in
- * selectable mode and the next selected row was not yet rendered in the
- * client
- */
- if (selectLastItemInNextRender || focusLastItemInNextRender) {
- selectLastRenderedRowInViewPort(focusLastItemInNextRender);
- selectLastItemInNextRender = focusLastItemInNextRender = false;
- }
- multiselectPending = false;
-
- if (focusedRow != null) {
- if (!focusedRow.isAttached() && !rowRequestHandler.isRunning()) {
- // focused row has been orphaned, can't focus
- focusRowFromBody();
- }
- }
-
- tabIndex = uidl.hasAttribute("tabindex") ? uidl
- .getIntAttribute("tabindex") : 0;
- setProperTabIndex();
-
- resizeSortedColumnForSortIndicator();
-
- // Remember this to detect situations where overflow hack might be
- // needed during scrolling
- lastRenderedHeight = scrollBody.getOffsetHeight();
-
- rendering = false;
- headerChangedDuringUpdate = false;
- }
-
- private void initializeRows(UIDL uidl, UIDL rowData) {
+ void initializeRows(UIDL uidl, UIDL rowData) {
if (scrollBody != null) {
scrollBody.removeFromParent();
- lazyUnregistryBag.add(scrollBody);
}
scrollBody = createScrollBody();
@@ -1080,13 +879,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
tFoot.setHorizontalScrollPosition(0);
initialContentReceived = true;
- if (isAttached()) {
- sizeInit();
- }
+ sizeNeedsInit = true;
scrollBody.restoreRowVisibility();
}
- private void updateColumnProperties(UIDL uidl) {
+ void updateColumnProperties(UIDL uidl) {
updateColumnOrder(uidl);
updateCollapsedColumns(uidl);
@@ -1121,7 +918,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- private boolean selectSelectedRows(UIDL uidl) {
+ boolean selectSelectedRows(UIDL uidl) {
boolean keyboardSelectionOverRowFetchInProgress = false;
if (uidl.hasVariable("selected")) {
@@ -1158,7 +955,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return keyboardSelectionOverRowFetchInProgress;
}
- private void updateSortingProperties(UIDL uidl) {
+ void updateSortingProperties(UIDL uidl) {
oldSortColumn = sortColumn;
if (uidl.hasVariable("sortascending")) {
sortAscending = uidl.getBooleanVariable("sortascending");
@@ -1166,7 +963,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- private void resizeSortedColumnForSortIndicator() {
+ void resizeSortedColumnForSortIndicator() {
// Force recalculation of the captionContainer element inside the header
// cell to accomodate for the size of the sort arrow.
HeaderCell sortedHeader = tHead.getHeaderCell(sortColumn);
@@ -1181,7 +978,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- private void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) {
+ void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) {
firstvisible = uidl.hasVariable("firstvisible") ? uidl
.getIntVariable("firstvisible") : 0;
if (firstvisible != lastRequestedFirstvisible && scrollBody != null) {
@@ -1196,7 +993,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return (int) (rowIx * scrollBody.getRowHeight());
}
- private void updatePageLength(UIDL uidl) {
+ void updatePageLength(UIDL uidl) {
int oldPageLength = pageLength;
if (uidl.hasAttribute("pagelength")) {
pageLength = uidl.getIntAttribute("pagelength");
@@ -1207,11 +1004,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (oldPageLength != pageLength && initializedAndAttached) {
// page length changed, need to update size
- sizeInit();
+ sizeNeedsInit = true;
}
}
- private void updateSelectionProperties(UIDL uidl) {
+ void updateSelectionProperties(UIDL uidl, ComponentState state,
+ boolean readOnly) {
setMultiSelectMode(uidl.hasAttribute("multiselectmode") ? uidl
.getIntAttribute("multiselectmode") : MULTISELECT_MODE_DEFAULT);
@@ -1219,19 +1017,19 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
.getBooleanAttribute("nsa") : true;
if (uidl.hasAttribute("selectmode")) {
- if (uidl.getBooleanAttribute("readonly")) {
- selectMode = Table.SELECT_MODE_NONE;
+ if (readOnly) {
+ selectMode = SelectMode.NONE;
} else if (uidl.getStringAttribute("selectmode").equals("multi")) {
- selectMode = Table.SELECT_MODE_MULTI;
+ selectMode = SelectMode.MULTI;
} else if (uidl.getStringAttribute("selectmode").equals("single")) {
- selectMode = Table.SELECT_MODE_SINGLE;
+ selectMode = SelectMode.SINGLE;
} else {
- selectMode = Table.SELECT_MODE_NONE;
+ selectMode = SelectMode.NONE;
}
}
}
- private void updateDragMode(UIDL uidl) {
+ void updateDragMode(UIDL uidl) {
dragmode = uidl.hasAttribute("dragmode") ? uidl
.getIntAttribute("dragmode") : 0;
if (BrowserInfo.get().isIE()) {
@@ -1264,11 +1062,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
totalRows = newTotalRows;
}
- protected int getTotalRows() {
+ public int getTotalRows() {
return totalRows;
}
- private void focusRowFromBody() {
+ void focusRowFromBody() {
if (selectedRowKeys.size() == 1) {
// try to focus a row currently selected and in viewport
String selectedRowKey = selectedRowKeys.iterator().next();
@@ -1296,7 +1094,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* @param focusOnly
* Should the focus only be moved to the last row
*/
- private void selectLastRenderedRowInViewPort(boolean focusOnly) {
+ void selectLastRenderedRowInViewPort(boolean focusOnly) {
int index = firstRowInViewPort + getFullyVisibleRowCount();
VScrollTableRow lastRowInViewport = scrollBody.getRowByRowIndex(index);
if (lastRowInViewport == null) {
@@ -1321,7 +1119,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* @param focusOnly
* Should the focus only be moved to the first row
*/
- private void selectFirstRenderedRowInViewPort(boolean focusOnly) {
+ void selectFirstRenderedRowInViewPort(boolean focusOnly) {
int index = firstRowInViewPort;
VScrollTableRow firstInViewport = scrollBody.getRowByRowIndex(index);
if (firstInViewport == null) {
@@ -1335,7 +1133,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- private void setCacheRateFromUIDL(UIDL uidl) {
+ void setCacheRateFromUIDL(UIDL uidl) {
setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr")
: CACHE_RATE_DEFAULT);
}
@@ -1347,20 +1145,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- /**
- * Unregisters Paintables in "trashed" HasWidgets (IScrollTableBodys or
- * IScrollTableRows). This is done lazily as Table must survive from
- * "subtreecaching" logic.
- */
- private void purgeUnregistryBag() {
- for (Iterator<Panel> iterator = lazyUnregistryBag.iterator(); iterator
- .hasNext();) {
- client.unregisterChildPaintables(iterator.next());
- }
- lazyUnregistryBag.clear();
- }
-
- private void updateActionMap(UIDL mainUidl) {
+ void updateActionMap(UIDL mainUidl) {
UIDL actionsUidl = mainUidl.getChildByTagName("actions");
if (actionsUidl == null) {
return;
@@ -1462,7 +1247,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* @param reqRows
* amount of rows in data set
*/
- private void updateBody(UIDL uidl, int firstRow, int reqRows) {
+ void updateBody(UIDL uidl, int firstRow, int reqRows) {
if (uidl == null || reqRows < 1) {
// container is empty, remove possibly existing rows
if (firstRow <= 0) {
@@ -1479,7 +1264,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
discardRowsOutsideCacheWindow();
}
- private void updateRowsInBody(UIDL partialRowUpdates) {
+ void updateRowsInBody(UIDL partialRowUpdates) {
if (partialRowUpdates == null) {
return;
}
@@ -1612,20 +1397,20 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
private boolean isMultiSelectModeSimple() {
- return selectMode == Table.SELECT_MODE_MULTI
+ return selectMode == SelectMode.MULTI
&& multiselectmode == MULTISELECT_MODE_SIMPLE;
}
private boolean isSingleSelectMode() {
- return selectMode == Table.SELECT_MODE_SINGLE;
+ return selectMode == SelectMode.SINGLE;
}
private boolean isMultiSelectModeAny() {
- return selectMode == Table.SELECT_MODE_MULTI;
+ return selectMode == SelectMode.MULTI;
}
private boolean isMultiSelectModeDefault() {
- return selectMode == Table.SELECT_MODE_MULTI
+ return selectMode == SelectMode.MULTI
&& multiselectmode == MULTISELECT_MODE_DEFAULT;
}
@@ -1641,7 +1426,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
protected boolean isSelectable() {
- return selectMode > Table.SELECT_MODE_NONE;
+ return selectMode.getId() > SelectMode.NONE.getId();
}
private boolean isCollapsedColumn(String colKey) {
@@ -1692,7 +1477,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* The key to search with
* @return
*/
- protected VScrollTableRow getRenderedRowByKey(String key) {
+ public VScrollTableRow getRenderedRowByKey(String key) {
if (scrollBody != null) {
final Iterator<Widget> it = scrollBody.iterator();
VScrollTableRow r = null;
@@ -1823,14 +1608,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
@Override
- protected void onAttach() {
- super.onAttach();
- if (initialContentReceived) {
- sizeInit();
- }
- }
-
- @Override
protected void onDetach() {
rowRequestHandler.cancel();
super.onDetach();
@@ -1853,7 +1630,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
*
* * Makes deferred request to get some cache rows
*/
- private void sizeInit() {
+ void sizeInit() {
+ sizeNeedsInit = false;
+
+ scrollBody.setContainerHeight();
+
/*
* We will use browsers table rendering algorithm to find proper column
* widths. If content and header take less space than available, we will
@@ -1908,7 +1689,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
boolean willHaveScrollbarz = willHaveScrollbars();
// fix "natural" width if width not set
- if (width == null || "".equals(width)) {
+ if (isDynamicWidth()) {
int w = total;
w += scrollBody.getCellExtraWidth() * visibleColOrder.length;
if (willHaveScrollbarz) {
@@ -2036,7 +1817,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* Fix "natural" height if height is not set. This must be after width
* fixing so the components' widths have been adjusted.
*/
- if (height == null || "".equals(height)) {
+ if (isDynamicHeight()) {
/*
* We must force an update of the row height as this point as it
* might have been (incorrectly) calculated earlier
@@ -2120,7 +1901,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* @return true if content area will have scrollbars visible.
*/
protected boolean willHaveScrollbars() {
- if (!(height != null && !height.equals(""))) {
+ if (isDynamicHeight()) {
if (pageLength < totalRows) {
return true;
}
@@ -2159,19 +1940,19 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
style.setDisplay(Display.BLOCK);
}
- private void hideScrollPositionAnnotation() {
+ void hideScrollPositionAnnotation() {
if (scrollPositionElement != null) {
DOM.setStyleAttribute(scrollPositionElement, "display", "none");
}
}
- private boolean isScrollPositionVisible() {
+ boolean isScrollPositionVisible() {
return scrollPositionElement != null
&& !scrollPositionElement.getStyle().getDisplay()
.equals(Display.NONE.toString());
}
- private class RowRequestHandler extends Timer {
+ class RowRequestHandler extends Timer {
private int reqFirstRow = 0;
private int reqRows = 0;
@@ -2346,11 +2127,10 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* of the caption container element by the correct amount
*/
public void resizeCaptionContainer(int rightSpacing) {
-
int captionContainerWidth = width
- colResizeWidget.getOffsetWidth() - rightSpacing;
- if (BrowserInfo.get().isIE6() || td.getClassName().contains("-asc")
+ if (td.getClassName().contains("-asc")
|| td.getClassName().contains("-desc")) {
// Leave room for the sort indicator
captionContainerWidth -= sortIndicator.getOffsetWidth();
@@ -2573,7 +2353,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private void fireHeaderClickedEvent(Event event) {
if (client.hasEventListeners(VScrollTable.this,
HEADER_CLICK_EVENT_ID)) {
- MouseEventDetails details = new MouseEventDetails(event);
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event);
client.updateVariable(paintableId, "headerClickEvent",
details.toString(), false);
client.updateVariable(paintableId, "headerClickCID", cid, true);
@@ -2830,8 +2611,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
int hw = captionContainer.getOffsetWidth()
+ scrollBody.getCellExtraWidth();
- if (BrowserInfo.get().isGecko()
- || BrowserInfo.get().isIE7()) {
+ if (BrowserInfo.get().isGecko()) {
hw += sortIndicator.getOffsetWidth();
}
if (columnIndex < 0) {
@@ -3098,12 +2878,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
public void setHorizontalScrollPosition(int scrollLeft) {
- if (BrowserInfo.get().isIE6()) {
- hTableWrapper.getStyle().setPosition(Position.RELATIVE);
- hTableWrapper.getStyle().setLeft(-scrollLeft, Unit.PX);
- } else {
- hTableWrapper.setScrollLeft(scrollLeft);
- }
+ hTableWrapper.setScrollLeft(scrollLeft);
}
public void setColumnCollapsingAllowed(boolean cc) {
@@ -3636,7 +3411,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private void fireFooterClickedEvent(Event event) {
if (client.hasEventListeners(VScrollTable.this,
FOOTER_CLICK_EVENT_ID)) {
- MouseEventDetails details = new MouseEventDetails(event);
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event);
client.updateVariable(paintableId, "footerClickEvent",
details.toString(), false);
client.updateVariable(paintableId, "footerClickCID", cid, true);
@@ -3966,12 +3742,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* The value of the leftScroll
*/
public void setHorizontalScrollPosition(int scrollLeft) {
- if (BrowserInfo.get().isIE6()) {
- hTableWrapper.getStyle().setProperty("position", "relative");
- hTableWrapper.getStyle().setPropertyPx("left", -scrollLeft);
- } else {
- hTableWrapper.setScrollLeft(scrollLeft);
- }
+ hTableWrapper.setScrollLeft(scrollLeft);
}
/**
@@ -4406,7 +4177,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
Element td = toBeRemoved.getElement().getChild(i).cast();
client.registerTooltip(VScrollTable.this, td, null);
}
- lazyUnregistryBag.add(toBeRemoved);
tBodyElement.removeChild(toBeRemoved.getElement());
orphan(toBeRemoved);
renderedRows.remove(index);
@@ -4417,12 +4187,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
throw new UnsupportedOperationException();
}
- @Override
- protected void onAttach() {
- super.onAttach();
- setContainerHeight();
- }
-
/**
* Fix container blocks height according to totalRows to avoid
* "bouncing" when scrolling
@@ -4614,15 +4378,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- public class VScrollTableRow extends Panel implements ActionOwner,
- Container {
+ public class VScrollTableRow extends Panel implements ActionOwner {
private static final int TOUCHSCROLL_TIMEOUT = 100;
private static final int DRAGMODE_MULTIROW = 2;
protected ArrayList<Widget> childWidgets = new ArrayList<Widget>();
private boolean selected = false;
protected final int rowKey;
- private List<UIDL> pendingComponentPaints;
private String[] actionKeys = null;
private final TableRowElement rowElement;
@@ -4744,12 +4506,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
addCell(uidl, cell.toString(), aligns[col++], style,
isRenderHtmlInCells(), sorted, description);
} else {
- final Paintable cellContent = client
+ final ComponentConnector cellContent = client
.getPaintable((UIDL) cell);
- addCell(uidl, (Widget) cellContent, aligns[col++],
+ addCell(uidl, cellContent.getWidget(), aligns[col++],
style, sorted);
- paintComponent(cellContent, (UIDL) cell);
}
}
}
@@ -4822,29 +4583,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return index;
}
- protected void paintComponent(Paintable p, UIDL uidl) {
- if (isAttached()) {
- p.updateFromUIDL(uidl, client);
- } else {
- if (pendingComponentPaints == null) {
- pendingComponentPaints = new LinkedList<UIDL>();
- }
- pendingComponentPaints.add(uidl);
- }
- }
-
- @Override
- protected void onAttach() {
- super.onAttach();
- if (pendingComponentPaints != null) {
- for (UIDL uidl : pendingComponentPaints) {
- Paintable paintable = client.getPaintable(uidl);
- paintable.updateFromUIDL(uidl, client);
- }
- pendingComponentPaints.clear();
- }
- }
-
@Override
protected void onDetach() {
super.onDetach();
@@ -5003,7 +4741,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
false);
}
- MouseEventDetails details = new MouseEventDetails(event);
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event);
client.updateVariable(paintableId, "clickEvent",
details.toString(), immediate);
@@ -5027,8 +4766,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (!containsWidget) {
// Only text nodes has tooltips
- if (client.getTooltipTitleInfo(VScrollTable.this,
- target) != null) {
+ if (ConnectorMap.get(client).getWidgetTooltipInfo(
+ VScrollTable.this, target) != null) {
// Cell has description, use it
client.handleTooltipEvent(event, VScrollTable.this,
target);
@@ -5361,7 +5100,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
Element targetTdOrTr) {
mDown = true;
VTransferable transferable = new VTransferable();
- transferable.setDragSource(VScrollTable.this);
+ transferable.setDragSource(ConnectorMap.get(client)
+ .getConnector(VScrollTable.this));
transferable.setData("itemId", "" + rowKey);
NodeList<TableCellElement> cells = rowElement.getCells();
for (int i = 0; i < cells.getLength(); i++) {
@@ -5580,29 +5320,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return paintableId;
}
- public RenderSpace getAllocatedSpace(Widget child) {
- int w = 0;
- int i = getColIndexOf(child);
- HeaderCell headerCell = tHead.getHeaderCell(i);
- if (headerCell != null) {
- if (initializedAndAttached) {
- w = headerCell.getWidth();
- } else {
- // header offset width is not absolutely correct value,
- // but a best guess (expecting similar content in all
- // columns ->
- // if one component is relative width so are others)
- w = headerCell.getOffsetWidth() - getCellExtraWidth();
- }
- }
- return new RenderSpace(w, 0) {
- @Override
- public int getHeight() {
- return (int) getRowHeight();
- }
- };
- }
-
private int getColIndexOf(Widget child) {
com.google.gwt.dom.client.Element widgetCell = child
.getElement().getParentElement().getParentElement();
@@ -5615,37 +5332,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return -1;
}
- public boolean hasChildComponent(Widget component) {
- return childWidgets.contains(component);
- }
-
- public void replaceChildComponent(Widget oldComponent,
- Widget newComponent) {
- com.google.gwt.dom.client.Element parentElement = oldComponent
- .getElement().getParentElement();
- int index = childWidgets.indexOf(oldComponent);
- oldComponent.removeFromParent();
-
- parentElement.appendChild(newComponent.getElement());
- childWidgets.add(index, newComponent);
- adopt(newComponent);
-
- }
-
- public boolean requestLayout(Set<Paintable> children) {
- // row size should never change and system wouldn't event
- // survive as this is a kind of fake paitable
- return true;
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP, not rendered
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Should never be called,
- // Component container interface faked here to get layouts
- // render properly
+ public Widget getWidgetForPaintable() {
+ return this;
}
}
@@ -5780,7 +5468,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (!hasFocus) {
scrollBodyPanel.setFocus(true);
}
+
}
+
}
/**
@@ -5822,7 +5512,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return;
}
- if (height == null || height.equals("")) {
+ if (isDynamicHeight()) {
return;
}
@@ -5846,16 +5536,14 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
scrollBodyPanel.setScrollPosition(scrollTop + 1);
scrollBodyPanel.setScrollPosition(scrollTop - 1);
}
+
+ sizeNeedsInit = true;
}
}
}
- @Override
- public void setWidth(String width) {
- if (this.width.equals(width)) {
- return;
- }
+ void updateWidth() {
if (!isVisible()) {
/*
* Do not update size when the table is hidden as all column widths
@@ -5865,9 +5553,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return;
}
- this.width = width;
- if (width != null && !"".equals(width)) {
- super.setWidth(width);
+ if (!isDynamicWidth()) {
int innerPixels = getOffsetWidth() - getBorderWidth();
if (innerPixels < 0) {
innerPixels = 0;
@@ -5879,11 +5565,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
} else {
- // Undefined width
- super.setWidth("");
-
- // Readjust size of table
- sizeInit();
+ sizeNeedsInit = true;
// readjust undefined width columns
triggerLazyColumnAdjustment(false);
@@ -5997,8 +5679,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- if ((height == null || "".equals(height))
- && totalRows == pageLength) {
+ if (isDynamicHeight() && totalRows == pageLength) {
// fix body height (may vary if lazy loading is offhorizontal
// scrollbar appears/disappears)
int bodyHeight = scrollBody.getRequiredHeight();
@@ -6070,7 +5751,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private int containerHeight;
private void setContainerHeight() {
- if (height != null && !"".equals(height)) {
+ if (!isDynamicHeight()) {
containerHeight = getOffsetHeight();
containerHeight -= showColHeaders ? tHead.getOffsetHeight() : 0;
containerHeight -= tFoot.getOffsetHeight();
@@ -6085,41 +5766,46 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
private int contentAreaBorderHeight = -1;
private int scrollLeft;
private int scrollTop;
- private VScrollTableDropHandler dropHandler;
+ VScrollTableDropHandler dropHandler;
private boolean navKeyDown;
- private boolean multiselectPending;
+ boolean multiselectPending;
/**
* @return border top + border bottom of the scrollable area of table
*/
private int getContentAreaBorderHeight() {
if (contentAreaBorderHeight < 0) {
- if (BrowserInfo.get().isIE7() || BrowserInfo.get().isIE6()) {
- contentAreaBorderHeight = Util
- .measureVerticalBorder(scrollBodyPanel.getElement());
- } else {
- DOM.setStyleAttribute(scrollBodyPanel.getElement(), "overflow",
- "hidden");
- int oh = scrollBodyPanel.getOffsetHeight();
- int ch = scrollBodyPanel.getElement().getPropertyInt(
- "clientHeight");
- contentAreaBorderHeight = oh - ch;
- DOM.setStyleAttribute(scrollBodyPanel.getElement(), "overflow",
- "auto");
- }
+
+ DOM.setStyleAttribute(scrollBodyPanel.getElement(), "overflow",
+ "hidden");
+ int oh = scrollBodyPanel.getOffsetHeight();
+ int ch = scrollBodyPanel.getElement()
+ .getPropertyInt("clientHeight");
+ contentAreaBorderHeight = oh - ch;
+ DOM.setStyleAttribute(scrollBodyPanel.getElement(), "overflow",
+ "auto");
}
return contentAreaBorderHeight;
}
@Override
public void setHeight(String height) {
- this.height = height;
+ if (height.length() == 0
+ && getElement().getStyle().getHeight().length() != 0) {
+ /*
+ * Changing from defined to undefined size -> should do a size init
+ * to take page length into account again
+ */
+ sizeNeedsInit = true;
+ }
super.setHeight(height);
+ }
+
+ void updateHeight() {
setContainerHeight();
- if (initializedAndAttached) {
- updatePageLength();
- }
+ updatePageLength();
+
if (!rendering) {
// Webkit may sometimes get an odd rendering bug (white space
// between header and body), see bug #3875. Running
@@ -6439,8 +6125,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
@Override
- public Paintable getPaintable() {
- return VScrollTable.this;
+ public ComponentConnector getConnector() {
+ return ConnectorMap.get(client).getConnector(VScrollTable.this);
}
public ApplicationConnection getApplicationConnection() {
@@ -6460,7 +6146,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* The row to where the selection head should move
* @return Returns true if focus was moved successfully, else false
*/
- protected boolean setRowFocus(VScrollTableRow row) {
+ public boolean setRowFocus(VScrollTableRow row) {
if (!isSelectable()) {
return false;
@@ -6800,7 +6486,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (BrowserInfo.get().isIE()) {
// IE sometimes moves focus to a clicked table cell...
Element focusedElement = Util.getIEFocusedElement();
- if (Util.getPaintableForElement(client, getParent(), focusedElement) == this) {
+ if (Util.getConnectorForElement(client, getParent(), focusedElement) == this) {
// ..in that case, steal the focus back to the focus handler
// but not if focus is in a child component instead (#7965)
focus();
@@ -6882,7 +6568,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* actions may need focus.
*
*/
- private void setProperTabIndex() {
+ void setProperTabIndex() {
int storedScrollTop = 0;
int storedScrollLeft = 0;
@@ -6991,7 +6677,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
return function(){ return false; };
}-*/;
- protected void triggerLazyColumnAdjustment(boolean now) {
+ public void triggerLazyColumnAdjustment(boolean now) {
lazyAdjustColumnWidths.cancel();
if (now) {
lazyAdjustColumnWidths.run();
@@ -7000,9 +6686,31 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
+ private boolean isDynamicWidth() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ return paintable.isUndefinedWidth();
+ }
+
+ private boolean isDynamicHeight() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ if (paintable == null) {
+ // This should be refactored. As isDynamicHeight can be called from
+ // a timer it is possible that the connector has been unregistered
+ // when this method is called, causing getConnector to return null.
+ return false;
+ }
+ return paintable.isUndefinedHeight();
+ }
+
private void debug(String msg) {
if (enableDebug) {
VConsole.error(msg);
}
}
+
+ public Widget getWidgetForPaintable() {
+ return this;
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetBaseConnector.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetBaseConnector.java
new file mode 100644
index 0000000000..e16e84d112
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetBaseConnector.java
@@ -0,0 +1,99 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.tabsheet;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+
+public abstract class TabsheetBaseConnector extends
+ AbstractComponentContainerConnector implements Paintable {
+
+ public static final String ATTRIBUTE_TAB_DISABLED = "disabled";
+ public static final String ATTRIBUTE_TAB_DESCRIPTION = "description";
+ public static final String ATTRIBUTE_TAB_ERROR_MESSAGE = "error";
+ public static final String ATTRIBUTE_TAB_CAPTION = "caption";
+ public static final String ATTRIBUTE_TAB_ICON = "icon";
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().client = client;
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // Update member references
+ getWidget().id = uidl.getId();
+ getWidget().disabled = !isEnabled();
+
+ // Render content
+ final UIDL tabs = uidl.getChildUIDL(0);
+
+ // Widgets in the TabSheet before update
+ ArrayList<Widget> oldWidgets = new ArrayList<Widget>();
+ for (Iterator<Widget> iterator = getWidget().getWidgetIterator(); iterator
+ .hasNext();) {
+ oldWidgets.add(iterator.next());
+ }
+
+ // Clear previous values
+ getWidget().tabKeys.clear();
+ getWidget().disabledTabKeys.clear();
+
+ 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(ATTRIBUTE_TAB_DISABLED)) {
+ getWidget().disabledTabKeys.add(key);
+ }
+
+ getWidget().tabKeys.add(key);
+
+ if (selected) {
+ getWidget().activeTabIndex = index;
+ }
+ getWidget().renderTab(tab, index, selected, hidden);
+ index++;
+ }
+
+ int tabCount = getWidget().getTabCount();
+ while (tabCount-- > index) {
+ getWidget().removeTab(index);
+ }
+
+ for (int i = 0; i < getWidget().getTabCount(); i++) {
+ ComponentConnector p = getWidget().getTab(i);
+ // null for PlaceHolder widgets
+ if (p != null) {
+ oldWidgets.remove(p.getWidget());
+ }
+ }
+
+ // Detach any old tab widget, should be max 1
+ for (Iterator<Widget> iterator = oldWidgets.iterator(); iterator
+ .hasNext();) {
+ Widget oldWidget = iterator.next();
+ if (oldWidget.isAttached()) {
+ oldWidget.removeFromParent();
+ }
+ }
+
+ }
+
+ @Override
+ public VTabsheetBase getWidget() {
+ return (VTabsheetBase) super.getWidget();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java
new file mode 100644
index 0000000000..7829934f70
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java
@@ -0,0 +1,105 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.tabsheet;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+import com.vaadin.ui.TabSheet;
+
+@Connect(TabSheet.class)
+public class TabsheetConnector extends TabsheetBaseConnector implements
+ SimpleManagedLayout, MayScrollChildren {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+ if (isRealUpdate(uidl)) {
+ // Handle stylename changes before generics (might affect size
+ // calculations)
+ getWidget().handleStyleNames(uidl, getState());
+ }
+
+ super.updateFromUIDL(uidl, client);
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ // tabs; push or not
+ if (!isUndefinedWidth()) {
+ DOM.setStyleAttribute(getWidget().tabs, "overflow", "hidden");
+ } else {
+ getWidget().showAllTabs();
+ DOM.setStyleAttribute(getWidget().tabs, "width", "");
+ DOM.setStyleAttribute(getWidget().tabs, "overflow", "visible");
+ getWidget().updateDynamicWidth();
+ }
+
+ if (!isUndefinedHeight()) {
+ // Must update height after the styles have been set
+ getWidget().updateContentNodeHeight();
+ getWidget().updateOpenTabSize();
+ }
+
+ getWidget().iLayout();
+
+ // Re run relative size update to ensure optimal scrollbars
+ // TODO isolate to situation that visible tab has undefined height
+ try {
+ client.handleComponentRelativeSize(getWidget().tp
+ .getWidget(getWidget().tp.getVisibleWidget()));
+ } catch (Exception e) {
+ // Ignore, most likely empty tabsheet
+ }
+
+ getWidget().waitingForResponse = false;
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTabsheet.class);
+ }
+
+ @Override
+ public VTabsheet getWidget() {
+ return (VTabsheet) super.getWidget();
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ /* Tabsheet does not render its children's captions */
+ }
+
+ public void layout() {
+ VTabsheet tabsheet = getWidget();
+
+ tabsheet.updateContentNodeHeight();
+
+ if (isUndefinedWidth()) {
+ tabsheet.contentNode.getStyle().setProperty("width", "");
+ } else {
+ int contentWidth = tabsheet.getOffsetWidth()
+ - tabsheet.getContentAreaBorderWidth();
+ if (contentWidth < 0) {
+ contentWidth = 0;
+ }
+ tabsheet.contentNode.getStyle().setProperty("width",
+ contentWidth + "px");
+ }
+
+ tabsheet.updateOpenTabSize();
+ if (isUndefinedWidth()) {
+ tabsheet.updateDynamicWidth();
+ }
+
+ tabsheet.iLayout();
+
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java
index f1a5b31379..c97ede1252 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java
@@ -2,14 +2,15 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.tabsheet;
import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.event.dom.client.BlurEvent;
@@ -35,15 +36,16 @@ import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderInformation;
-import com.vaadin.terminal.gwt.client.RenderSpace;
import com.vaadin.terminal.gwt.client.TooltipInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ui.label.VLabel;
public class VTabsheet extends VTabsheetBase implements Focusable,
FocusHandler, BlurHandler, KeyDownHandler {
@@ -239,27 +241,33 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private ApplicationConnection client;
TabCaption(Tab tab, ApplicationConnection client) {
- super(null, client);
+ super(client);
this.client = client;
this.tab = tab;
}
- @Override
public boolean updateCaption(UIDL uidl) {
- if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)
- || uidl.hasAttribute(ATTRIBUTE_ERROR)) {
+ if (uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION)) {
TooltipInfo tooltipInfo = new TooltipInfo();
- tooltipInfo.setTitle(uidl
- .getStringAttribute(ATTRIBUTE_DESCRIPTION));
- if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
- tooltipInfo.setErrorUidl(uidl.getErrors());
- }
+ tooltipInfo
+ .setTitle(uidl
+ .getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION));
+ tooltipInfo
+ .setErrorMessage(uidl
+ .getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE));
client.registerTooltip(getTabsheet(), getElement(), tooltipInfo);
} else {
client.registerTooltip(getTabsheet(), getElement(), null);
}
- boolean ret = super.updateCaption(uidl);
+ // TODO need to call this instead of super because the caption does
+ // not have an owner
+ boolean ret = updateCaptionWithoutOwner(
+ uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_CAPTION),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DISABLED),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION),
+ uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE),
+ uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ICON));
setClosable(uidl.hasAttribute("closable"));
@@ -291,33 +299,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return tab;
}
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- if (BrowserInfo.get().isIE7()) {
- /*
- * IE7 apparently has problems with calculating width for
- * floated elements inside a DIV with padding. Set the width
- * explicitly for the caption.
- */
- fixTextWidth();
- }
- }
-
- private void fixTextWidth() {
- Element captionText = getTextElement();
- if (captionText == null) {
- return;
- }
-
- int captionWidth = Util.getRequiredWidth(captionText);
- int scrollWidth = captionText.getScrollWidth();
- if (scrollWidth > captionWidth) {
- captionWidth = scrollWidth;
- }
- captionText.getStyle().setPropertyPx("width", captionWidth);
- }
-
public void setClosable(boolean closable) {
this.closable = closable;
if (closable && closeButton == null) {
@@ -552,45 +533,32 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
// Can't use "style" as it's already in use
public static final String TAB_STYLE_NAME = "tabstyle";
+ final Element tabs; // tabbar and 'scroller' container
+ Tab focusedTab;
+ /**
+ * The tabindex property (position in the browser's focus cycle.) Named like
+ * this to avoid confusion with activeTabIndex.
+ */
+ int tabulatorIndex = 0;
+
private static final FocusImpl focusImpl = FocusImpl.getFocusImplForPanel();
- private final Element tabs; // tabbar and 'scroller' container
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
- private Tab focusedTab;
-
- /**
- * The tabindex property (position in the browser's focus cycle.) Named like
- * this to avoid confusion with activeTabIndex.
- */
- private int tabulatorIndex = 0;
-
/**
* The index of the first visible tab (when scrolled)
*/
private int scrollerIndex = 0;
- private final TabBar tb = new TabBar(this);
- private final VTabsheetPanel tp = new VTabsheetPanel();
- private final Element contentNode, deco;
-
- private String height;
- private String width;
+ final TabBar tb = new TabBar(this);
+ final VTabsheetPanel tp = new VTabsheetPanel();
+ final Element contentNode;
- private boolean waitingForResponse;
+ private final Element deco;
- private final RenderInformation renderInformation = new RenderInformation();
-
- /**
- * Previous visible widget is set invisible with CSS (not display: none, but
- * visibility: hidden), to avoid flickering during render process. Normal
- * visibility must be returned later when new widget is rendered.
- */
- private Widget previousVisibleWidget;
-
- private boolean rendering = false;
+ boolean waitingForResponse;
private String currentStyle;
@@ -615,18 +583,14 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
addStyleDependentName("loading");
- // run updating variables in deferred command to bypass some FF
- // optimization issues
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- previousVisibleWidget = tp.getWidget(tp.getVisibleWidget());
- DOM.setStyleAttribute(
- DOM.getParent(previousVisibleWidget.getElement()),
- "visibility", "hidden");
- client.updateVariable(id, "selected", tabKeys.get(tabIndex)
- .toString(), true);
- }
- });
+ // 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);
waitingForResponse = true;
}
// Note that we return true when tabIndex == activeTabIndex; the active
@@ -651,12 +615,16 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
client.updateVariable(id, "close", tabKeys.get(tabIndex), true);
}
- private boolean isDynamicWidth() {
- return width == null || width.equals("");
+ boolean isDynamicWidth() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ return paintable.isUndefinedWidth();
}
- private boolean isDynamicHeight() {
- return height == null || height.equals("");
+ boolean isDynamicHeight() {
+ ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+ this);
+ return paintable.isUndefinedHeight();
}
public VTabsheet() {
@@ -742,85 +710,24 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return scrollerIndex > index;
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
-
- if (!uidl.getBooleanAttribute("cached")) {
- // Handle stylename changes before generics (might affect size
- // calculations)
- handleStyleNames(uidl);
- }
-
- super.updateFromUIDL(uidl, client);
- if (cachedUpdate) {
- rendering = false;
- return;
- }
-
- // tabs; push or not
- if (!isDynamicWidth()) {
- DOM.setStyleAttribute(tabs, "overflow", "hidden");
- } else {
- showAllTabs();
- DOM.setStyleAttribute(tabs, "width", "");
- DOM.setStyleAttribute(tabs, "overflow", "visible");
- updateDynamicWidth();
- }
-
- if (!isDynamicHeight()) {
- // Must update height after the styles have been set
- updateContentNodeHeight();
- updateOpenTabSize();
- }
-
- if (uidl.hasAttribute("tabindex")) {
- tabulatorIndex = uidl.getIntAttribute("tabindex");
- if (tabulatorIndex == -1) {
- blur();
- }
- }
-
- // If a tab was focused before, focus the new active tab
- if (focusedTab != null && tb.getTabCount() > 0 && tabulatorIndex != -1) {
- focus();
- }
-
- iLayout();
- // Re run relative size update to ensure optimal scrollbars
- // TODO isolate to situation that visible tab has undefined height
- try {
- client.handleComponentRelativeSize(tp.getWidget(tp
- .getVisibleWidget()));
- } catch (Exception e) {
- // Ignore, most likely empty tabsheet
- }
-
- renderInformation.updateSize(getElement());
-
- waitingForResponse = false;
- rendering = false;
- }
-
- private void handleStyleNames(UIDL uidl) {
+ void handleStyleNames(UIDL uidl, ComponentState state) {
// Add proper stylenames for all elements (easier to prevent unwanted
// style inheritance)
- if (uidl.hasAttribute("style")) {
- final String style = uidl.getStringAttribute("style");
- if (currentStyle != style) {
- currentStyle = style;
- final String[] styles = style.split(" ");
+ if (state.hasStyles()) {
+ final List<String> styles = state.getStyles();
+ if (!currentStyle.equals(styles.toString())) {
+ currentStyle = styles.toString();
final String tabsBaseClass = TABS_CLASSNAME;
String tabsClass = tabsBaseClass;
final String contentBaseClass = CLASSNAME + "-content";
String contentClass = contentBaseClass;
final String decoBaseClass = CLASSNAME + "-deco";
String decoClass = decoBaseClass;
- for (int i = 0; i < styles.length; i++) {
- tb.addStyleDependentName(styles[i]);
- tabsClass += " " + tabsBaseClass + "-" + styles[i];
- contentClass += " " + contentBaseClass + "-" + styles[i];
- decoClass += " " + decoBaseClass + "-" + styles[i];
+ for (String style : styles) {
+ tb.addStyleDependentName(style);
+ tabsClass += " " + tabsBaseClass + "-" + style;
+ contentClass += " " + contentBaseClass + "-" + style;
+ decoClass += " " + decoBaseClass + "-" + style;
}
DOM.setElementProperty(tabs, "className", tabsClass);
DOM.setElementProperty(contentNode, "className", contentClass);
@@ -844,7 +751,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
}
- private void updateDynamicWidth() {
+ void updateDynamicWidth() {
// Find width consumed by tabs
TableCellElement spacerCell = ((TableElement) tb.getElement().cast())
.getRows().getItem(0).getCells().getItem(tb.getTabCount());
@@ -921,22 +828,24 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
tab.recalculateCaptionWidth();
UIDL tabContentUIDL = null;
- Paintable tabContent = null;
+ ComponentConnector tabContentPaintable = null;
+ Widget tabContentWidget = null;
if (tabUidl.getChildCount() > 0) {
tabContentUIDL = tabUidl.getChildUIDL(0);
- tabContent = client.getPaintable(tabContentUIDL);
+ tabContentPaintable = client.getPaintable(tabContentUIDL);
+ tabContentWidget = tabContentPaintable.getWidget();
}
- if (tabContent != null) {
+ if (tabContentPaintable != null) {
/* This is a tab with content information */
- int oldIndex = tp.getWidgetIndex((Widget) tabContent);
+ int oldIndex = tp.getWidgetIndex(tabContentWidget);
if (oldIndex != -1 && oldIndex != index) {
/*
* The tab has previously been rendered in another position so
* we must move the cached content to correct position
*/
- tp.insert((Widget) tabContent, index);
+ tp.insert(tabContentWidget, index);
}
} else {
/* A tab whose content has not yet been loaded */
@@ -960,10 +869,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
} else {
if (tabContentUIDL != null) {
// updating a drawn child on hidden tab
- if (tp.getWidgetIndex((Widget) tabContent) < 0) {
- tp.insert((Widget) tabContent, index);
+ if (tp.getWidgetIndex(tabContentWidget) < 0) {
+ tp.insert(tabContentWidget, index);
}
- tabContent.updateFromUIDL(tabContentUIDL, client);
} else if (tp.getWidgetCount() <= index) {
tp.add(new PlaceHolder());
}
@@ -986,57 +894,39 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void renderContent(final UIDL contentUIDL) {
- final Paintable content = client.getPaintable(contentUIDL);
+ final ComponentConnector content = client.getPaintable(contentUIDL);
+ Widget newWidget = content.getWidget();
if (tp.getWidgetCount() > activeTabIndex) {
Widget old = tp.getWidget(activeTabIndex);
- if (old != content) {
+ if (old != newWidget) {
tp.remove(activeTabIndex);
- if (old instanceof Paintable) {
- client.unregisterPaintable((Paintable) old);
+ ConnectorMap paintableMap = ConnectorMap.get(client);
+ if (paintableMap.isConnector(old)) {
+ paintableMap.unregisterConnector(paintableMap
+ .getConnector(old));
}
- tp.insert((Widget) content, activeTabIndex);
+ tp.insert(content.getWidget(), activeTabIndex);
}
} else {
- tp.add((Widget) content);
+ tp.add(content.getWidget());
}
tp.showWidget(activeTabIndex);
VTabsheet.this.iLayout();
- (content).updateFromUIDL(contentUIDL, client);
/*
* The size of a cached, relative sized component must be updated to
* report correct size to updateOpenTabSize().
*/
if (contentUIDL.getBooleanAttribute("cached")) {
- client.handleComponentRelativeSize((Widget) content);
+ client.handleComponentRelativeSize(content.getWidget());
}
updateOpenTabSize();
VTabsheet.this.removeStyleDependentName("loading");
- if (previousVisibleWidget != null) {
- DOM.setStyleAttribute(
- DOM.getParent(previousVisibleWidget.getElement()),
- "visibility", "");
- previousVisibleWidget = null;
- }
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- this.height = height;
- updateContentNodeHeight();
-
- if (!rendering) {
- updateOpenTabSize();
- iLayout();
- // TODO Check if this is needed
- client.runDescendentsLayout(this);
- }
}
- private void updateContentNodeHeight() {
- if (height != null && !"".equals(height)) {
+ void updateContentNodeHeight() {
+ if (!isDynamicHeight()) {
int contentHeight = getOffsetHeight();
contentHeight -= DOM.getElementPropertyInt(deco, "offsetHeight");
contentHeight -= tb.getOffsetHeight();
@@ -1046,55 +936,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
// Set proper values for content element
DOM.setStyleAttribute(contentNode, "height", contentHeight + "px");
- renderSpace.setHeight(contentHeight);
} else {
DOM.setStyleAttribute(contentNode, "height", "");
- renderSpace.setHeight(0);
- }
- }
-
- @Override
- public void setWidth(String width) {
- if ((this.width == null && width.equals(""))
- || (this.width != null && this.width.equals(width))) {
- return;
- }
-
- super.setWidth(width);
- if (width.equals("")) {
- width = null;
}
- this.width = width;
- if (width == null) {
- renderSpace.setWidth(0);
- contentNode.getStyle().setProperty("width", "");
- } else {
- int contentWidth = getOffsetWidth() - getContentAreaBorderWidth();
- if (contentWidth < 0) {
- contentWidth = 0;
- }
- contentNode.getStyle().setProperty("width", contentWidth + "px");
- renderSpace.setWidth(contentWidth);
- }
-
- if (!rendering) {
- if (isDynamicHeight()) {
- Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, tp,
- this);
- }
-
- updateOpenTabSize();
- iLayout();
- // TODO Check if this is needed
- client.runDescendentsLayout(this);
-
- }
-
}
public void iLayout() {
updateTabScroller();
- tp.runWebkitOverflowAutoFix();
}
/**
@@ -1102,7 +950,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
* position: absolute (to work around a firefox flickering bug) we must keep
* this up-to-date by hand.
*/
- private void updateOpenTabSize() {
+ void updateOpenTabSize() {
/*
* The overflow=auto element must have a height specified, otherwise it
* will be just as high as the contents and no scrollbars will appear
@@ -1112,10 +960,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
int minWidth = 0;
if (!isDynamicHeight()) {
- height = renderSpace.getHeight();
+ height = contentNode.getOffsetHeight();
}
if (!isDynamicWidth()) {
- width = renderSpace.getWidth();
+ width = contentNode.getOffsetWidth() - getContentAreaBorderWidth();
} else {
/*
* If the tabbar is wider than the content we need to use the tabbar
@@ -1132,8 +980,11 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
* Layouts the tab-scroller elements, and applies styles.
*/
private void updateTabScroller() {
- if (width != null) {
- DOM.setStyleAttribute(tabs, "width", width);
+ if (!isDynamicWidth()) {
+ ComponentConnector paintable = ConnectorMap.get(client)
+ .getConnector(this);
+ DOM.setStyleAttribute(tabs, "width", paintable.getState()
+ .getWidth());
}
// Make sure scrollerIndex is valid
@@ -1178,7 +1029,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
- private void showAllTabs() {
+ void showAllTabs() {
scrollerIndex = tb.getFirstVisibleTab();
for (int i = 0; i < tb.getTabCount(); i++) {
Tab t = tb.getTab(i);
@@ -1215,92 +1066,29 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
@Override
- protected Iterator getPaintableIterator() {
+ protected Iterator<Widget> getWidgetIterator() {
return tp.iterator();
}
- public boolean hasChildComponent(Widget component) {
- if (tp.getWidgetIndex(component) < 0) {
- return false;
- } else {
- return true;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- tp.replaceComponent(oldComponent, newComponent);
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- /* Tabsheet does not render its children's captions */
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- if (!isDynamicHeight() && !isDynamicWidth()) {
- /*
- * If the height and width has been specified for this container the
- * child components cannot make the size of the layout change
- */
- // layout size change may affect its available space (scrollbars)
- for (Paintable paintable : child) {
- client.handleComponentRelativeSize((Widget) paintable);
- }
- return true;
- }
-
- updateOpenTabSize();
-
- if (renderInformation.updateSize(getElement())) {
- /*
- * Size has changed so we let the child components know about the
- * new size.
- */
- iLayout();
- client.runDescendentsLayout(this);
- /*
- * Firefox and IE9 need a nudge to prevent unwanted scrollbars with
- * Chameleon theme (#8625)
- */
- if (BrowserInfo.get().isFirefox() || BrowserInfo.get().isIE9()) {
- Util.setStyleTemporarily((Element) tp.getElement()
- .getFirstChildElement(), "overflow", "");
- }
- return false;
- } else {
- /*
- * Size has not changed so we do not need to propagate the event
- * further
- */
- return true;
- }
-
- }
-
private int borderW = -1;
- private int getContentAreaBorderWidth() {
+ int getContentAreaBorderWidth() {
if (borderW < 0) {
borderW = Util.measureHorizontalBorder(contentNode);
}
return borderW;
}
- private final RenderSpace renderSpace = new RenderSpace(0, 0, true);
-
- public RenderSpace getAllocatedSpace(Widget child) {
- // All tabs have equal amount of space allocated
- return renderSpace;
- }
-
@Override
protected int getTabCount() {
return tb.getTabCount();
}
@Override
- protected Paintable getTab(int index) {
+ protected ComponentConnector getTab(int index) {
if (tp.getWidgetCount() > index) {
- return (Paintable) tp.getWidget(index);
+ Widget widget = tp.getWidget(index);
+ return ConnectorMap.get(client).getConnector(widget);
}
return null;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetBase.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetBase.java
new file mode 100644
index 0000000000..ed9883dd35
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetBase.java
@@ -0,0 +1,75 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.tabsheet;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+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.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.UIDL;
+
+public abstract class VTabsheetBase extends ComplexPanel {
+
+ protected String id;
+ protected ApplicationConnection client;
+
+ protected final ArrayList<String> tabKeys = new ArrayList<String>();
+ protected int activeTabIndex = 0;
+ protected boolean disabled;
+ protected boolean readonly;
+ protected Set<String> disabledTabKeys = new HashSet<String>();
+
+ public VTabsheetBase(String classname) {
+ setElement(DOM.createDiv());
+ setStyleName(classname);
+ }
+
+ /**
+ * @return a list of currently shown Widgets
+ */
+ abstract protected Iterator<Widget> getWidgetIterator();
+
+ /**
+ * Clears current tabs and contents
+ */
+ abstract protected void clearPaintables();
+
+ /**
+ * Implement in extending classes. This method should render needed elements
+ * and set the visibility of the tab according to the 'selected' parameter.
+ */
+ protected abstract void renderTab(final UIDL tabUidl, int index,
+ boolean selected, boolean hidden);
+
+ /**
+ * Implement in extending classes. This method should render any previously
+ * non-cached content and set the activeTabIndex property to the specified
+ * index.
+ */
+ protected abstract void selectTab(int index, final UIDL contentUidl);
+
+ /**
+ * Implement in extending classes. This method should return the number of
+ * tabs currently rendered.
+ */
+ protected abstract int getTabCount();
+
+ /**
+ * Implement in extending classes. This method should return the Paintable
+ * corresponding to the given index.
+ */
+ protected abstract ComponentConnector getTab(int index);
+
+ /**
+ * Implement in extending classes. This method should remove the rendered
+ * tab with the specified index.
+ */
+ protected abstract void removeTab(int index);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java
index 126b0ebea1..f2b37c3a1c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.tabsheet;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
@@ -13,12 +13,12 @@ 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.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
/**
* A panel that displays all of its child widgets in a 'deck', where only one
* can be visible at a time. It is used by
- * {@link com.vaadin.terminal.gwt.client.ui.VTabsheet}.
+ * {@link com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheet}.
*
* This class has the same basic functionality as the GWT DeckPanel
* {@link com.google.gwt.user.client.ui.DeckPanel}, with the exception that it
@@ -141,8 +141,11 @@ public class VTabsheetPanel extends ComplexPanel {
hide(DOM.getParent(visibleWidget.getElement()));
}
visibleWidget = newVisible;
- unHide(DOM.getParent(visibleWidget.getElement()));
}
+ // Always ensure the selected tab is visible. If server prevents a tab
+ // change we might end up here with visibleWidget == newVisible but its
+ // parent is still hidden.
+ unHide(DOM.getParent(visibleWidget.getElement()));
}
private void hide(Element e) {
@@ -190,16 +193,12 @@ public class VTabsheetPanel extends ComplexPanel {
getElement().getStyle().setPropertyPx("height", height);
// widget wrapper height
- wrapperDiv.getStyle().setPropertyPx("height", height);
- runWebkitOverflowAutoFix();
- }
-
- public void runWebkitOverflowAutoFix() {
- if (visibleWidget != null) {
- Util.runWebkitOverflowAutoFix(DOM.getParent(visibleWidget
- .getElement()));
+ if (dynamicHeight) {
+ wrapperDiv.getStyle().clearHeight();
+ } else {
+ // widget wrapper height
+ wrapperDiv.getStyle().setPropertyPx("height", height);
}
-
}
public void replaceComponent(Widget oldComponent, Widget newComponent) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/textarea/TextAreaConnector.java b/src/com/vaadin/terminal/gwt/client/ui/textarea/TextAreaConnector.java
new file mode 100644
index 0000000000..0f3ae0ad4f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/textarea/TextAreaConnector.java
@@ -0,0 +1,42 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.textarea;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.textfield.TextFieldConnector;
+import com.vaadin.ui.TextArea;
+
+@Connect(TextArea.class)
+public class TextAreaConnector extends TextFieldConnector {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // Call parent renderer explicitly
+ super.updateFromUIDL(uidl, client);
+
+ if (uidl.hasAttribute("rows")) {
+ getWidget().setRows(uidl.getIntAttribute("rows"));
+ }
+
+ if (getWidget().getMaxLength() >= 0) {
+ getWidget().sinkEvents(Event.ONKEYUP);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTextArea.class);
+ }
+
+ @Override
+ public VTextArea getWidget() {
+ return (VTextArea) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/textarea/VTextArea.java
index c6107e3b0e..c600b2fd1e 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/textarea/VTextArea.java
@@ -2,15 +2,14 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.textarea;
import com.google.gwt.core.client.Scheduler;
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.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
/**
* This class represents a multiline textfield (textarea).
@@ -29,20 +28,6 @@ public class VTextArea extends VTextField {
setStyleName(CLASSNAME);
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Call parent renderer explicitly
- super.updateFromUIDL(uidl, client);
-
- if (uidl.hasAttribute("rows")) {
- setRows(uidl.getIntAttribute("rows"));
- }
-
- if (getMaxLength() >= 0) {
- sinkEvents(Event.ONKEYUP);
- }
- }
-
public void setRows(int rows) {
setRows(getElement(), rows);
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/textfield/TextFieldConnector.java b/src/com/vaadin/terminal/gwt/client/ui/textfield/TextFieldConnector.java
new file mode 100644
index 0000000000..7e9e786676
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/textfield/TextFieldConnector.java
@@ -0,0 +1,123 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.textfield;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractFieldConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
+import com.vaadin.ui.TextField;
+
+@Connect(value = TextField.class, loadStyle = LoadStyle.EAGER)
+public class TextFieldConnector extends AbstractFieldConnector implements
+ Paintable, BeforeShortcutActionListener {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // Save details
+ getWidget().client = client;
+ getWidget().paintableId = uidl.getId();
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().setReadOnly(isReadOnly());
+
+ getWidget().inputPrompt = uidl
+ .getStringAttribute(VTextField.ATTR_INPUTPROMPT);
+
+ getWidget().setMaxLength(
+ uidl.hasAttribute("maxLength") ? uidl
+ .getIntAttribute("maxLength") : -1);
+
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().listenTextChangeEvents = hasEventListener("ie");
+ if (getWidget().listenTextChangeEvents) {
+ getWidget().textChangeEventMode = uidl
+ .getStringAttribute(VTextField.ATTR_TEXTCHANGE_EVENTMODE);
+ if (getWidget().textChangeEventMode
+ .equals(VTextField.TEXTCHANGE_MODE_EAGER)) {
+ getWidget().textChangeEventTimeout = 1;
+ } else {
+ getWidget().textChangeEventTimeout = uidl
+ .getIntAttribute(VTextField.ATTR_TEXTCHANGE_TIMEOUT);
+ if (getWidget().textChangeEventTimeout < 1) {
+ // Sanitize and allow lazy/timeout with timeout set to 0 to
+ // work as eager
+ getWidget().textChangeEventTimeout = 1;
+ }
+ }
+ getWidget().sinkEvents(VTextField.TEXTCHANGE_EVENTS);
+ getWidget().attachCutEventListener(getWidget().getElement());
+ }
+
+ if (uidl.hasAttribute("cols")) {
+ getWidget().setColumns(
+ new Integer(uidl.getStringAttribute("cols")).intValue());
+ }
+
+ final String text = uidl.getStringVariable("text");
+
+ /*
+ * We skip the text content update if field has been repainted, but text
+ * has not been changed. Additional sanity check verifies there is no
+ * change in the que (in which case we count more on the server side
+ * value).
+ */
+ if (!(uidl
+ .getBooleanAttribute(VTextField.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS)
+ && getWidget().valueBeforeEdit != null && text
+ .equals(getWidget().valueBeforeEdit))) {
+ getWidget().updateFieldContent(text);
+ }
+
+ if (uidl.hasAttribute("selpos")) {
+ final int pos = uidl.getIntAttribute("selpos");
+ final int length = uidl.getIntAttribute("sellen");
+ /*
+ * Gecko defers setting the text so we need to defer the selection.
+ */
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ getWidget().setSelectionRange(pos, length);
+ }
+ });
+ }
+
+ // Here for backward compatibility; to be moved to TextArea.
+ // Optimization: server does not send attribute for the default 'true'
+ // state.
+ if (uidl.hasAttribute("wordwrap")
+ && uidl.getBooleanAttribute("wordwrap") == false) {
+ getWidget().setWordwrap(false);
+ } else {
+ getWidget().setWordwrap(true);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTextField.class);
+ }
+
+ @Override
+ public VTextField getWidget() {
+ return (VTextField) super.getWidget();
+ }
+
+ public void onBeforeShortcutAction(Event e) {
+ getWidget().valueChange(false);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java
index d1e4f7ca5b..7bd392b503 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java
@@ -2,9 +2,8 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.textfield;
-import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
@@ -15,7 +14,6 @@ import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
@@ -24,11 +22,9 @@ import com.google.gwt.user.client.ui.TextBoxBase;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VTooltip;
-import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
+import com.vaadin.terminal.gwt.client.ui.Field;
/**
* This class represents a basic text input field with one row.
@@ -36,9 +32,8 @@ import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutAct
* @author Vaadin Ltd.
*
*/
-public class VTextField extends TextBoxBase implements Paintable, Field,
- ChangeHandler, FocusHandler, BlurHandler, BeforeShortcutActionListener,
- KeyDownHandler {
+public class VTextField extends TextBoxBase implements Field, ChangeHandler,
+ FocusHandler, BlurHandler, KeyDownHandler {
public static final String VAR_CUR_TEXT = "curText";
@@ -52,11 +47,11 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
*/
public static final String CLASSNAME_FOCUS = "focus";
- protected String id;
+ protected String paintableId;
protected ApplicationConnection client;
- private String valueBeforeEdit = null;
+ protected String valueBeforeEdit = null;
/**
* Set to false if a text change event has been sent since the last value
@@ -65,20 +60,18 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
*/
private boolean valueBeforeEditIsSynced = true;
- private boolean immediate = false;
- private int extraHorizontalPixels = -1;
- private int extraVerticalPixels = -1;
+ protected boolean immediate = false;
private int maxLength = -1;
private static final String CLASSNAME_PROMPT = "prompt";
- private static final String ATTR_INPUTPROMPT = "prompt";
+ protected static final String ATTR_INPUTPROMPT = "prompt";
public static final String ATTR_TEXTCHANGE_TIMEOUT = "iet";
public static final String VAR_CURSOR = "c";
public static final String ATTR_TEXTCHANGE_EVENTMODE = "iem";
- private static final String TEXTCHANGE_MODE_EAGER = "EAGER";
+ protected static final String TEXTCHANGE_MODE_EAGER = "EAGER";
private static final String TEXTCHANGE_MODE_TIMEOUT = "TIMEOUT";
- private String inputPrompt = null;
+ protected String inputPrompt = null;
private boolean prompting = false;
private int lastCursorPos = -1;
private boolean wordwrap = true;
@@ -89,12 +82,6 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
protected VTextField(Element node) {
super(node);
- if (BrowserInfo.get().getIEVersion() > 0
- && BrowserInfo.get().getIEVersion() < 8) {
- // Fixes IE margin problem (#2058)
- DOM.setStyleAttribute(node, "marginTop", "-1px");
- DOM.setStyleAttribute(node, "marginBottom", "-1px");
- }
setStyleName(CLASSNAME);
addChangeHandler(this);
if (BrowserInfo.get().isIE()) {
@@ -117,7 +104,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
* Eager polling for a change is bit dum and heavy operation, so I guess we
* should first try to survive without.
*/
- private static final int TEXTCHANGE_EVENTS = Event.ONPASTE
+ protected static final int TEXTCHANGE_EVENTS = Event.ONPASTE
| Event.KEYEVENTS | Event.ONMOUSEUP;
@Override
@@ -163,7 +150,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
client.sendPendingVariableChanges();
} else {
// Default case - just send an immediate text change message
- client.updateVariable(id, VAR_CUR_TEXT, text, true);
+ client.updateVariable(paintableId, VAR_CUR_TEXT, text, true);
// Shouldn't investigate valueBeforeEdit to avoid duplicate text
// change events as the states are not in sync any more
@@ -185,9 +172,9 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
}
};
private boolean scheduled = false;
- private boolean listenTextChangeEvents;
- private String textChangeEventMode;
- private int textChangeEventTimeout;
+ protected boolean listenTextChangeEvents;
+ protected String textChangeEventMode;
+ protected int textChangeEventTimeout;
private void deferTextChangeEvent() {
if (textChangeEventMode.equals(TEXTCHANGE_MODE_TIMEOUT) && scheduled) {
@@ -220,129 +207,19 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
super.setReadOnly(readOnly);
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- this.client = client;
- id = uidl.getId();
-
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- if (uidl.getBooleanAttribute("readonly")) {
- setReadOnly(true);
- } else {
- setReadOnly(false);
- }
-
- inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT);
-
- setMaxLength(uidl.hasAttribute("maxLength") ? uidl
- .getIntAttribute("maxLength") : -1);
-
- immediate = uidl.getBooleanAttribute("immediate");
-
- listenTextChangeEvents = client.hasEventListeners(this, "ie");
- if (listenTextChangeEvents) {
- textChangeEventMode = uidl
- .getStringAttribute(ATTR_TEXTCHANGE_EVENTMODE);
- if (textChangeEventMode.equals(TEXTCHANGE_MODE_EAGER)) {
- textChangeEventTimeout = 1;
- } else {
- textChangeEventTimeout = uidl
- .getIntAttribute(ATTR_TEXTCHANGE_TIMEOUT);
- if (textChangeEventTimeout < 1) {
- // Sanitize and allow lazy/timeout with timeout set to 0 to
- // work as eager
- textChangeEventTimeout = 1;
- }
- }
- sinkEvents(TEXTCHANGE_EVENTS);
- attachCutEventListener(getElement());
- }
-
- if (uidl.hasAttribute("cols")) {
- setColumns(new Integer(uidl.getStringAttribute("cols")).intValue());
- }
-
- final String text = uidl.getStringVariable("text");
-
- /*
- * We skip the text content update if field has been repainted, but text
- * has not been changed. Additional sanity check verifies there is no
- * change in the que (in which case we count more on the server side
- * value).
- */
- if (!(uidl.getBooleanAttribute(ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS)
- && valueBeforeEdit != null && text.equals(valueBeforeEdit))) {
- updateFieldContent(text);
- }
-
- if (uidl.hasAttribute("selpos")) {
- final int pos = uidl.getIntAttribute("selpos");
- final int length = uidl.getIntAttribute("sellen");
- /*
- * Gecko defers setting the text so we need to defer the selection.
- */
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- setSelectionRange(pos, length);
- }
- });
- }
-
- // Here for backward compatibility; to be moved to TextArea.
- // Optimization: server does not send attribute for the default 'true'
- // state.
- if (uidl.hasAttribute("wordwrap")
- && uidl.getBooleanAttribute("wordwrap") == false) {
- setWordwrap(false);
- } else {
- setWordwrap(true);
- }
- }
-
- private void updateFieldContent(final String text) {
+ protected void updateFieldContent(final String text) {
setPrompting(inputPrompt != null && focusedTextField != this
&& (text.equals("")));
- if (BrowserInfo.get().isFF3()) {
- /*
- * Firefox 3 is really sluggish when updating input attached to dom.
- * Some optimizations seems to work much better in Firefox3 if we
- * update the actual content lazily when the rest of the DOM has
- * stabilized. In tests, about ten times better performance is
- * achieved with this optimization. See for eg. #2898
- */
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- String fieldValue;
- if (prompting) {
- fieldValue = isReadOnly() ? "" : inputPrompt;
- addStyleDependentName(CLASSNAME_PROMPT);
- } else {
- fieldValue = text;
- removeStyleDependentName(CLASSNAME_PROMPT);
- }
- /*
- * Avoid resetting the old value. Prevents cursor flickering
- * which then again happens due to this Gecko hack.
- */
- if (!getText().equals(fieldValue)) {
- setText(fieldValue);
- }
- }
- });
+ String fieldValue;
+ if (prompting) {
+ fieldValue = isReadOnly() ? "" : inputPrompt;
+ addStyleDependentName(CLASSNAME_PROMPT);
} else {
- String fieldValue;
- if (prompting) {
- fieldValue = isReadOnly() ? "" : inputPrompt;
- addStyleDependentName(CLASSNAME_PROMPT);
- } else {
- fieldValue = text;
- removeStyleDependentName(CLASSNAME_PROMPT);
- }
- setText(fieldValue);
+ fieldValue = text;
+ removeStyleDependentName(CLASSNAME_PROMPT);
}
+ setText(fieldValue);
lastTextChangeString = valueBeforeEdit = text;
valueBeforeEditIsSynced = true;
@@ -358,7 +235,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
/*-{
var me = this;
el.oncut = $entry(function() {
- me.@com.vaadin.terminal.gwt.client.ui.VTextField::onCut()();
+ me.@com.vaadin.terminal.gwt.client.ui.textfield.VTextField::onCut()();
});
}-*/;
@@ -384,7 +261,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
}
}
- private void setMaxLength(int newMaxLength) {
+ protected void setMaxLength(int newMaxLength) {
if (newMaxLength >= 0) {
maxLength = newMaxLength;
if (getElement().getTagName().toLowerCase().equals("textarea")) {
@@ -403,7 +280,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
}
- protected int getMaxLength() {
+ public int getMaxLength() {
return maxLength;
}
@@ -420,20 +297,20 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
* true if the field was blurred
*/
public void valueChange(boolean blurred) {
- if (client != null && id != null) {
+ if (client != null && paintableId != null) {
boolean sendBlurEvent = false;
boolean sendValueChange = false;
if (blurred && client.hasEventListeners(this, EventId.BLUR)) {
sendBlurEvent = true;
- client.updateVariable(id, EventId.BLUR, "", false);
+ client.updateVariable(paintableId, EventId.BLUR, "", false);
}
String newText = getText();
if (!prompting && newText != null
&& !newText.equals(valueBeforeEdit)) {
sendValueChange = immediate;
- client.updateVariable(id, "text", getText(), false);
+ client.updateVariable(paintableId, "text", getText(), false);
valueBeforeEdit = newText;
valueBeforeEditIsSynced = true;
}
@@ -466,7 +343,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
if (Util.isAttachedAndDisplayed(this)) {
int cursorPos = getCursorPos();
if (lastCursorPos != cursorPos) {
- client.updateVariable(id, VAR_CURSOR, cursorPos, false);
+ client.updateVariable(paintableId, VAR_CURSOR, cursorPos, false);
lastCursorPos = cursorPos;
return true;
}
@@ -488,14 +365,10 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
setText("");
removeStyleDependentName(CLASSNAME_PROMPT);
setPrompting(false);
- if (BrowserInfo.get().isIE6()) {
- // IE6 does not show the cursor when tabbing into the field
- setCursorPos(0);
- }
}
focusedTextField = this;
if (client.hasEventListeners(this, EventId.FOCUS)) {
- client.updateVariable(client.getPid(this), EventId.FOCUS, "", true);
+ client.updateVariable(paintableId, EventId.FOCUS, "", true);
}
}
@@ -537,79 +410,6 @@ public class VTextField extends TextBoxBase implements Paintable, Field,
} catch (e) {}
}-*/;
- /**
- * @return space used by components paddings and borders
- */
- private int getExtraHorizontalPixels() {
- if (extraHorizontalPixels < 0) {
- detectExtraSizes();
- }
- return extraHorizontalPixels;
- }
-
- /**
- * @return space used by components paddings and borders
- */
- private int getExtraVerticalPixels() {
- if (extraVerticalPixels < 0) {
- detectExtraSizes();
- }
- return extraVerticalPixels;
- }
-
- /**
- * Detects space used by components paddings and borders. Used when
- * relational size are used.
- */
- private void detectExtraSizes() {
- Element clone = Util.cloneNode(getElement(), false);
- DOM.setElementAttribute(clone, "id", "");
- DOM.setStyleAttribute(clone, "visibility", "hidden");
- DOM.setStyleAttribute(clone, "position", "absolute");
- // due FF3 bug set size to 10px and later subtract it from extra pixels
- DOM.setStyleAttribute(clone, "width", "10px");
- DOM.setStyleAttribute(clone, "height", "10px");
- DOM.appendChild(DOM.getParent(getElement()), clone);
- extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10;
- extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10;
-
- DOM.removeChild(DOM.getParent(getElement()), clone);
- }
-
- @Override
- public void setHeight(String height) {
- if (height.endsWith("px")) {
- int h = Integer.parseInt(height.substring(0, height.length() - 2));
- h -= getExtraVerticalPixels();
- if (h < 0) {
- h = 0;
- }
-
- super.setHeight(h + "px");
- } else {
- super.setHeight(height);
- }
- }
-
- @Override
- public void setWidth(String width) {
- if (width.endsWith("px")) {
- int w = Integer.parseInt(width.substring(0, width.length() - 2));
- w -= getExtraHorizontalPixels();
- if (w < 0) {
- w = 0;
- }
-
- super.setWidth(w + "px");
- } else {
- super.setWidth(width);
- }
- }
-
- public void onBeforeShortcutAction(Event e) {
- valueChange(false);
- }
-
// Here for backward compatibility; to be moved to TextArea
public void setWordwrap(boolean enabled) {
if (enabled == wordwrap) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/tree/TreeConnector.java b/src/com/vaadin/terminal/gwt/client/ui/tree/TreeConnector.java
new file mode 100644
index 0000000000..ebe5d22e99
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/tree/TreeConnector.java
@@ -0,0 +1,260 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.tree;
+
+import java.util.Iterator;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.TooltipInfo;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.tree.VTree.TreeNode;
+import com.vaadin.ui.Tree;
+
+@Connect(Tree.class)
+public class TreeConnector extends AbstractComponentConnector implements
+ Paintable {
+
+ public static final String ATTRIBUTE_NODE_STYLE = "style";
+ public static final String ATTRIBUTE_NODE_CAPTION = "caption";
+ public static final String ATTRIBUTE_NODE_ICON = "icon";
+
+ public static final String ATTRIBUTE_ACTION_CAPTION = "caption";
+ public static final String ATTRIBUTE_ACTION_ICON = ATTRIBUTE_NODE_ICON;
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().rendering = true;
+
+ getWidget().client = client;
+
+ if (uidl.hasAttribute("partialUpdate")) {
+ handleUpdate(uidl);
+ getWidget().rendering = false;
+ return;
+ }
+
+ getWidget().paintableId = uidl.getId();
+
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().disabled = !isEnabled();
+ getWidget().readonly = isReadOnly();
+
+ getWidget().dragMode = uidl.hasAttribute("dragMode") ? uidl
+ .getIntAttribute("dragMode") : 0;
+
+ getWidget().isNullSelectionAllowed = uidl
+ .getBooleanAttribute("nullselect");
+
+ if (uidl.hasAttribute("alb")) {
+ getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
+ }
+
+ getWidget().body.clear();
+ // clear out any references to nodes that no longer are attached
+ getWidget().clearNodeToKeyMap();
+ TreeNode childTree = null;
+ UIDL childUidl = null;
+ for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
+ childUidl = (UIDL) i.next();
+ if ("actions".equals(childUidl.getTag())) {
+ updateActionMap(childUidl);
+ continue;
+ } else if ("-ac".equals(childUidl.getTag())) {
+ getWidget().updateDropHandler(childUidl);
+ continue;
+ }
+ childTree = getWidget().new TreeNode();
+ updateNodeFromUIDL(childTree, childUidl);
+ getWidget().body.add(childTree);
+ childTree.addStyleDependentName("root");
+ childTree.childNodeContainer.addStyleDependentName("root");
+ }
+ if (childTree != null && childUidl != null) {
+ boolean leaf = !childUidl.getTag().equals("node");
+ childTree.addStyleDependentName(leaf ? "leaf-last" : "last");
+ childTree.childNodeContainer.addStyleDependentName("last");
+ }
+ final String selectMode = uidl.getStringAttribute("selectmode");
+ getWidget().selectable = !"none".equals(selectMode);
+ getWidget().isMultiselect = "multi".equals(selectMode);
+
+ if (getWidget().isMultiselect) {
+ if (BrowserInfo.get().isTouchDevice()) {
+ // Always use the simple mode for touch devices that do not have
+ // shift/ctrl keys (#8595)
+ getWidget().multiSelectMode = VTree.MULTISELECT_MODE_SIMPLE;
+ } else {
+ getWidget().multiSelectMode = uidl
+ .getIntAttribute("multiselectmode");
+ }
+ }
+
+ getWidget().selectedIds = uidl.getStringArrayVariableAsSet("selected");
+
+ // Update lastSelection and focusedNode to point to *actual* nodes again
+ // after the old ones have been cleared from the body. This fixes focus
+ // and keyboard navigation issues as described in #7057 and other
+ // tickets.
+ if (getWidget().lastSelection != null) {
+ getWidget().lastSelection = getWidget().getNodeByKey(
+ getWidget().lastSelection.key);
+ }
+ if (getWidget().focusedNode != null) {
+ getWidget().setFocusedNode(
+ getWidget().getNodeByKey(getWidget().focusedNode.key));
+ }
+
+ if (getWidget().lastSelection == null
+ && getWidget().focusedNode == null
+ && !getWidget().selectedIds.isEmpty()) {
+ getWidget().setFocusedNode(
+ getWidget().getNodeByKey(
+ getWidget().selectedIds.iterator().next()));
+ getWidget().focusedNode.setFocused(false);
+ }
+
+ getWidget().rendering = false;
+
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTree.class);
+ }
+
+ @Override
+ public VTree getWidget() {
+ return (VTree) super.getWidget();
+ }
+
+ private void handleUpdate(UIDL uidl) {
+ final TreeNode rootNode = getWidget().getNodeByKey(
+ uidl.getStringAttribute("rootKey"));
+ if (rootNode != null) {
+ if (!rootNode.getState()) {
+ // expanding node happened server side
+ rootNode.setState(true, false);
+ }
+ renderChildNodes(rootNode, (Iterator) uidl.getChildIterator());
+ }
+ }
+
+ /**
+ * Registers action for the root and also for individual nodes
+ *
+ * @param uidl
+ */
+ private void updateActionMap(UIDL uidl) {
+ final Iterator<?> it = uidl.getChildIterator();
+ while (it.hasNext()) {
+ final UIDL action = (UIDL) it.next();
+ final String key = action.getStringAttribute("key");
+ final String caption = action
+ .getStringAttribute(ATTRIBUTE_ACTION_CAPTION);
+ String iconUrl = null;
+ if (action.hasAttribute(ATTRIBUTE_ACTION_ICON)) {
+ iconUrl = getConnection().translateVaadinUri(
+ action.getStringAttribute(ATTRIBUTE_ACTION_ICON));
+ }
+ getWidget().registerAction(key, caption, iconUrl);
+ }
+
+ }
+
+ public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl) {
+ String nodeKey = uidl.getStringAttribute("key");
+ treeNode.setText(uidl.getStringAttribute(ATTRIBUTE_NODE_CAPTION));
+ treeNode.key = nodeKey;
+
+ getWidget().registerNode(treeNode);
+
+ if (uidl.hasAttribute("al")) {
+ treeNode.actionKeys = uidl.getStringArrayAttribute("al");
+ }
+
+ if (uidl.getTag().equals("node")) {
+ if (uidl.getChildCount() == 0) {
+ treeNode.childNodeContainer.setVisible(false);
+ } else {
+ renderChildNodes(treeNode, (Iterator) uidl.getChildIterator());
+ treeNode.childrenLoaded = true;
+ }
+ } else {
+ treeNode.addStyleName(TreeNode.CLASSNAME + "-leaf");
+ }
+ if (uidl.hasAttribute(ATTRIBUTE_NODE_STYLE)) {
+ treeNode.setNodeStyleName(uidl
+ .getStringAttribute(ATTRIBUTE_NODE_STYLE));
+ }
+
+ String description = uidl.getStringAttribute("descr");
+ if (description != null && getConnection() != null) {
+ // Set tooltip
+ TooltipInfo info = new TooltipInfo(description);
+ getConnection().registerTooltip(this, nodeKey, info);
+ } else {
+ // Remove possible previous tooltip
+ getConnection().registerTooltip(this, nodeKey, null);
+ }
+
+ if (uidl.getBooleanAttribute("expanded") && !treeNode.getState()) {
+ treeNode.setState(true, false);
+ }
+
+ if (uidl.getBooleanAttribute("selected")) {
+ treeNode.setSelected(true);
+ // ensure that identifier is in selectedIds array (this may be a
+ // partial update)
+ getWidget().selectedIds.add(nodeKey);
+ }
+
+ treeNode.setIcon(uidl.getStringAttribute(ATTRIBUTE_NODE_ICON));
+ }
+
+ void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i) {
+ containerNode.childNodeContainer.clear();
+ containerNode.childNodeContainer.setVisible(true);
+ while (i.hasNext()) {
+ final UIDL childUidl = i.next();
+ // actions are in bit weird place, don't mix them with children,
+ // but current node's actions
+ if ("actions".equals(childUidl.getTag())) {
+ updateActionMap(childUidl);
+ continue;
+ }
+ final TreeNode childTree = getWidget().new TreeNode();
+ updateNodeFromUIDL(childTree, childUidl);
+ containerNode.childNodeContainer.add(childTree);
+ if (!i.hasNext()) {
+ childTree
+ .addStyleDependentName(childTree.isLeaf() ? "leaf-last"
+ : "last");
+ childTree.childNodeContainer.addStyleDependentName("last");
+ }
+ }
+ containerNode.childrenLoaded = true;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly() || getState().isPropertyReadOnly();
+ }
+
+ @Override
+ public AbstractFieldState getState() {
+ return (AbstractFieldState) super.getState();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTree.java b/src/com/vaadin/terminal/gwt/client/ui/tree/VTree.java
index 1d5ec206aa..6f19cba957 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTree.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/tree/VTree.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.tree;
import java.util.ArrayList;
import java.util.HashMap;
@@ -39,12 +39,20 @@ import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.TooltipInfo;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Action;
+import com.vaadin.terminal.gwt.client.ui.ActionOwner;
+import com.vaadin.terminal.gwt.client.ui.FocusElementPanel;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.TreeAction;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
import com.vaadin.terminal.gwt.client.ui.dd.DDUtil;
import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler;
import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCallback;
@@ -58,9 +66,9 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
/**
*
*/
-public class VTree extends FocusElementPanel implements Paintable,
- VHasDropHandler, FocusHandler, BlurHandler, KeyPressHandler,
- KeyDownHandler, SubPartAware, ActionOwner {
+public class VTree extends FocusElementPanel implements VHasDropHandler,
+ FocusHandler, BlurHandler, KeyPressHandler, KeyDownHandler,
+ SubPartAware, ActionOwner {
public static final String CLASSNAME = "v-tree";
@@ -78,17 +86,17 @@ public class VTree extends FocusElementPanel implements Paintable,
private static final int CHARCODE_SPACE = 32;
- private final FlowPanel body = new FlowPanel();
+ final FlowPanel body = new FlowPanel();
- private Set<String> selectedIds = new HashSet<String>();
- private ApplicationConnection client;
- private String paintableId;
- private boolean selectable;
- private boolean isMultiselect;
+ Set<String> selectedIds = new HashSet<String>();
+ ApplicationConnection client;
+ String paintableId;
+ boolean selectable;
+ boolean isMultiselect;
private String currentMouseOverKey;
- private TreeNode lastSelection;
- private TreeNode focusedNode;
- private int multiSelectMode = MULTISELECT_MODE_DEFAULT;
+ TreeNode lastSelection;
+ TreeNode focusedNode;
+ int multiSelectMode = MULTISELECT_MODE_DEFAULT;
private final HashMap<String, TreeNode> keyToNode = new HashMap<String, TreeNode>();
@@ -98,23 +106,23 @@ public class VTree extends FocusElementPanel implements Paintable,
*/
private final HashMap<String, String> actionMap = new HashMap<String, String>();
- private boolean immediate;
+ boolean immediate;
- private boolean isNullSelectionAllowed = true;
+ boolean isNullSelectionAllowed = true;
- private boolean disabled = false;
+ boolean disabled = false;
- private boolean readonly;
+ boolean readonly;
- private boolean rendering;
+ boolean rendering;
private VAbstractDropHandler dropHandler;
- private int dragMode;
+ int dragMode;
private boolean selectionHasChanged = false;
- private String[] bodyActionKeys;
+ String[] bodyActionKeys;
public VLazyExecutor iconLoaded = new VLazyExecutor(50,
new ScheduledCommand() {
@@ -213,24 +221,6 @@ public class VTree extends FocusElementPanel implements Paintable,
}
}
- private void updateActionMap(UIDL c) {
- final Iterator<?> it = c.getChildIterator();
- while (it.hasNext()) {
- final UIDL action = (UIDL) it.next();
- final String key = action.getStringAttribute("key");
- final String caption = action.getStringAttribute("caption");
- actionMap.put(key + "_c", caption);
- if (action.hasAttribute("icon")) {
- // TODO need some uri handling ??
- actionMap.put(key + "_i", client.translateVaadinUri(action
- .getStringAttribute("icon")));
- } else {
- actionMap.remove(key + "_i");
- }
- }
-
- }
-
public String getActionCaption(String actionKey) {
return actionMap.get(actionKey + "_c");
}
@@ -239,105 +229,6 @@ public class VTree extends FocusElementPanel implements Paintable,
return actionMap.get(actionKey + "_i");
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Ensure correct implementation and let container manage caption
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- rendering = true;
-
- this.client = client;
-
- if (uidl.hasAttribute("partialUpdate")) {
- handleUpdate(uidl);
- rendering = false;
- return;
- }
-
- paintableId = uidl.getId();
-
- immediate = uidl.hasAttribute("immediate");
-
- disabled = uidl.getBooleanAttribute("disabled");
- readonly = uidl.getBooleanAttribute("readonly");
-
- dragMode = uidl.hasAttribute("dragMode") ? uidl
- .getIntAttribute("dragMode") : 0;
-
- isNullSelectionAllowed = uidl.getBooleanAttribute("nullselect");
-
- if (uidl.hasAttribute("alb")) {
- bodyActionKeys = uidl.getStringArrayAttribute("alb");
- }
-
- body.clear();
- // clear out any references to nodes that no longer are attached
- keyToNode.clear();
- TreeNode childTree = null;
- UIDL childUidl = null;
- for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
- childUidl = (UIDL) i.next();
- if ("actions".equals(childUidl.getTag())) {
- updateActionMap(childUidl);
- continue;
- } else if ("-ac".equals(childUidl.getTag())) {
- updateDropHandler(childUidl);
- continue;
- }
- childTree = new TreeNode();
- if (childTree.ie6compatnode != null) {
- body.add(childTree);
- }
- childTree.updateFromUIDL(childUidl, client);
- if (childTree.ie6compatnode == null) {
- body.add(childTree);
- }
- childTree.addStyleDependentName("root");
- childTree.childNodeContainer.addStyleDependentName("root");
- }
- if (childTree != null && childUidl != null) {
- boolean leaf = !childUidl.getTag().equals("node");
- childTree.addStyleDependentName(leaf ? "leaf-last" : "last");
- childTree.childNodeContainer.addStyleDependentName("last");
- }
- final String selectMode = uidl.getStringAttribute("selectmode");
- selectable = !"none".equals(selectMode);
- isMultiselect = "multi".equals(selectMode);
-
- if (isMultiselect) {
- if (BrowserInfo.get().isTouchDevice()) {
- // Always use the simple mode for touch devices that do not have
- // shift/ctrl keys (#8595)
- multiSelectMode = MULTISELECT_MODE_SIMPLE;
- } else {
- multiSelectMode = uidl.getIntAttribute("multiselectmode");
- }
- }
-
- selectedIds = uidl.getStringArrayVariableAsSet("selected");
-
- // Update lastSelection and focusedNode to point to *actual* nodes again
- // after the old ones have been cleared from the body. This fixes focus
- // and keyboard navigation issues as described in #7057 and other
- // tickets.
- if (lastSelection != null) {
- lastSelection = keyToNode.get(lastSelection.key);
- }
- if (focusedNode != null) {
- setFocusedNode(keyToNode.get(focusedNode.key));
- }
-
- if (lastSelection == null && focusedNode == null
- && !selectedIds.isEmpty()) {
- setFocusedNode(keyToNode.get(selectedIds.iterator().next()));
- focusedNode.setFocused(false);
- }
-
- rendering = false;
-
- }
-
/**
* Returns the first root node of the tree or null if there are no root
* nodes.
@@ -384,7 +275,7 @@ public class VTree extends FocusElementPanel implements Paintable,
drag.getDropDetails().put("itemIdOver", currentMouseOverKey);
if (currentMouseOverKey != null) {
- TreeNode treeNode = keyToNode.get(currentMouseOverKey);
+ TreeNode treeNode = getNodeByKey(currentMouseOverKey);
VerticalDropLocation detail = treeNode.getDropDetail(drag
.getCurrentGwtEvent());
Boolean overTreeNode = null;
@@ -406,7 +297,7 @@ public class VTree extends FocusElementPanel implements Paintable,
return treeNode == null ? null : treeNode.key;
}
- private void updateDropHandler(UIDL childUidl) {
+ void updateDropHandler(UIDL childUidl) {
if (dropHandler == null) {
dropHandler = new VAbstractDropHandler() {
@@ -448,7 +339,7 @@ public class VTree extends FocusElementPanel implements Paintable,
.getDropDetails().get("detail");
if (curDetail == detail
&& newKey.equals(currentMouseOverKey)) {
- keyToNode.get(newKey).emphasis(detail);
+ getNodeByKey(newKey).emphasis(detail);
}
/*
* Else drag is already on a different
@@ -470,7 +361,7 @@ public class VTree extends FocusElementPanel implements Paintable,
private void cleanUp() {
if (currentMouseOverKey != null) {
- keyToNode.get(currentMouseOverKey).emphasis(null);
+ getNodeByKey(currentMouseOverKey).emphasis(null);
currentMouseOverKey = null;
}
}
@@ -482,8 +373,8 @@ public class VTree extends FocusElementPanel implements Paintable,
}
@Override
- public Paintable getPaintable() {
- return VTree.this;
+ public ComponentConnector getConnector() {
+ return ConnectorMap.get(client).getConnector(VTree.this);
}
public ApplicationConnection getApplicationConnection() {
@@ -495,24 +386,12 @@ public class VTree extends FocusElementPanel implements Paintable,
dropHandler.updateAcceptRules(childUidl);
}
- private void handleUpdate(UIDL uidl) {
- final TreeNode rootNode = keyToNode.get(uidl
- .getStringAttribute("rootKey"));
- if (rootNode != null) {
- if (!rootNode.getState()) {
- // expanding node happened server side
- rootNode.setState(true, false);
- }
- rootNode.renderChildNodes(uidl.getChildIterator());
- }
- }
-
public void setSelected(TreeNode treeNode, boolean selected) {
if (selected) {
if (!isMultiselect) {
while (selectedIds.size() > 0) {
final String id = selectedIds.iterator().next();
- final TreeNode oldSelection = keyToNode.get(id);
+ final TreeNode oldSelection = getNodeByKey(id);
if (oldSelection != null) {
// can be null if the node is not visible (parent
// collapsed)
@@ -581,33 +460,26 @@ public class VTree extends FocusElementPanel implements Paintable,
public String key;
- private String[] actionKeys = null;
+ String[] actionKeys = null;
- private boolean childrenLoaded;
+ boolean childrenLoaded;
- private Element nodeCaptionDiv;
+ Element nodeCaptionDiv;
protected Element nodeCaptionSpan;
- private FlowPanel childNodeContainer;
+ FlowPanel childNodeContainer;
private boolean open;
private Icon icon;
- private Element ie6compatnode;
-
private Event mouseDownEvent;
private int cachedHeight = -1;
private boolean focused = false;
- /**
- * Track onload events as IE6 sends two
- */
- private boolean onloadHandled = false;
-
public TreeNode() {
constructDom();
sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS
@@ -705,11 +577,11 @@ public class VTree extends FocusElementPanel implements Paintable,
// always when clicking an item, focus it
setFocusedNode(this, false);
- if (!isIE6OrOpera()) {
+ if (!BrowserInfo.get().isOpera()) {
/*
* Ensure that the tree's focus element also gains focus
* (TreeNodes focus is faked using FocusElementPanel in browsers
- * other than IE6 and Opera).
+ * other than Opera).
*/
focus();
}
@@ -777,14 +649,7 @@ public class VTree extends FocusElementPanel implements Paintable,
final Element target = DOM.eventGetTarget(event);
if (type == Event.ONLOAD && target == icon.getElement()) {
- if (onloadHandled) {
- return;
- }
- if (BrowserInfo.get().isIE6()) {
- fixWidth();
- }
iconLoaded.trigger();
- onloadHandled = true;
}
if (disabled) {
@@ -805,7 +670,7 @@ public class VTree extends FocusElementPanel implements Paintable,
fireClick(event);
}
if (type == Event.ONCLICK) {
- if (getElement() == target || ie6compatnode == target) {
+ if (getElement() == target) {
// state change
toggleState();
} else if (!readonly && inCaption) {
@@ -860,7 +725,8 @@ public class VTree extends FocusElementPanel implements Paintable,
if (mouseDownEvent != null) {
// start actual drag on slight move when mouse is down
VTransferable t = new VTransferable();
- t.setDragSource(VTree.this);
+ t.setDragSource(ConnectorMap.get(client).getConnector(
+ VTree.this));
t.setData("itemId", key);
VDragEvent drag = VDragAndDropManager.get().startDrag(
t, mouseDownEvent, true);
@@ -891,7 +757,7 @@ public class VTree extends FocusElementPanel implements Paintable,
* previously modified field may contain dirty variables.
*/
if (!treeHasFocus) {
- if (isIE6OrOpera()) {
+ if (BrowserInfo.get().isOpera()) {
if (focusedNode == null) {
getNodeByKey(key).setFocused(true);
} else {
@@ -901,7 +767,8 @@ public class VTree extends FocusElementPanel implements Paintable,
focus();
}
}
- final MouseEventDetails details = new MouseEventDetails(evt);
+ final MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(evt);
ScheduledCommand command = new ScheduledCommand() {
public void execute() {
// Determine if we should send the event immediately to the
@@ -952,15 +819,6 @@ public class VTree extends FocusElementPanel implements Paintable,
protected void constructDom() {
addStyleName(CLASSNAME);
- // workaround for a very weird IE6 issue #1245
- if (BrowserInfo.get().isIE6()) {
- ie6compatnode = DOM.createDiv();
- setStyleName(ie6compatnode, CLASSNAME + "-ie6compatnode");
- DOM.setInnerText(ie6compatnode, " ");
- DOM.appendChild(getElement(), ie6compatnode);
-
- DOM.sinkEvents(ie6compatnode, Event.ONCLICK);
- }
nodeCaptionDiv = DOM.createDiv();
DOM.setElementProperty(nodeCaptionDiv, "className", CLASSNAME
@@ -972,7 +830,7 @@ public class VTree extends FocusElementPanel implements Paintable,
DOM.appendChild(nodeCaptionDiv, wrapper);
DOM.appendChild(wrapper, nodeCaptionSpan);
- if (isIE6OrOpera()) {
+ if (BrowserInfo.get().isOpera()) {
/*
* Focus the caption div of the node to get keyboard navigation
* to work without scrolling up or down when focusing a node.
@@ -985,76 +843,6 @@ public class VTree extends FocusElementPanel implements Paintable,
setWidget(childNodeContainer);
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- setText(uidl.getStringAttribute("caption"));
- key = uidl.getStringAttribute("key");
-
- keyToNode.put(key, this);
-
- if (uidl.hasAttribute("al")) {
- actionKeys = uidl.getStringArrayAttribute("al");
- }
-
- if (uidl.getTag().equals("node")) {
- if (uidl.getChildCount() == 0) {
- childNodeContainer.setVisible(false);
- } else {
- renderChildNodes(uidl.getChildIterator());
- childrenLoaded = true;
- }
- } else {
- addStyleName(CLASSNAME + "-leaf");
- }
- if (uidl.hasAttribute("style")) {
- addStyleName(CLASSNAME + "-" + uidl.getStringAttribute("style"));
- Widget.setStyleName(nodeCaptionDiv, CLASSNAME + "-caption-"
- + uidl.getStringAttribute("style"), true);
- childNodeContainer.addStyleName(CLASSNAME + "-children-"
- + uidl.getStringAttribute("style"));
- }
-
- String description = uidl.getStringAttribute("descr");
- if (description != null && client != null) {
- // Set tooltip
- TooltipInfo info = new TooltipInfo(description);
- client.registerTooltip(VTree.this, key, info);
- } else {
- // Remove possible previous tooltip
- client.registerTooltip(VTree.this, key, null);
- }
-
- if (uidl.getBooleanAttribute("expanded") && !getState()) {
- setState(true, false);
- }
-
- if (uidl.getBooleanAttribute("selected")) {
- setSelected(true);
- // ensure that identifier is in selectedIds array (this may be a
- // partial update)
- selectedIds.add(key);
- }
-
- if (uidl.hasAttribute("icon")) {
- if (icon == null) {
- onloadHandled = false;
- icon = new Icon(client);
- DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv),
- icon.getElement(), nodeCaptionSpan);
- }
- icon.setUri(uidl.getStringAttribute("icon"));
- } else {
- if (icon != null) {
- DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv),
- icon.getElement());
- icon = null;
- }
- }
-
- if (BrowserInfo.get().isIE6() && isAttached()) {
- fixWidth();
- }
- }
-
public boolean isLeaf() {
String[] styleNames = getStyleName().split(" ");
for (String styleName : styleNames) {
@@ -1065,7 +853,7 @@ public class VTree extends FocusElementPanel implements Paintable,
return false;
}
- private void setState(boolean state, boolean notifyServer) {
+ void setState(boolean state, boolean notifyServer) {
if (open == state) {
return;
}
@@ -1096,43 +884,14 @@ public class VTree extends FocusElementPanel implements Paintable,
}
}
- private boolean getState() {
+ boolean getState() {
return open;
}
- private void setText(String text) {
+ void setText(String text) {
DOM.setInnerText(nodeCaptionSpan, text);
}
- private void renderChildNodes(Iterator<?> i) {
- childNodeContainer.clear();
- childNodeContainer.setVisible(true);
- while (i.hasNext()) {
- final UIDL childUidl = (UIDL) i.next();
- // actions are in bit weird place, don't mix them with children,
- // but current node's actions
- if ("actions".equals(childUidl.getTag())) {
- updateActionMap(childUidl);
- continue;
- }
- final TreeNode childTree = new TreeNode();
- if (ie6compatnode != null) {
- childNodeContainer.add(childTree);
- }
- childTree.updateFromUIDL(childUidl, client);
- if (ie6compatnode == null) {
- childNodeContainer.add(childTree);
- }
- if (!i.hasNext()) {
- childTree
- .addStyleDependentName(childTree.isLeaf() ? "leaf-last"
- : "last");
- childTree.childNodeContainer.addStyleDependentName("last");
- }
- }
- childrenLoaded = true;
- }
-
public boolean isChildrenLoaded() {
return childrenLoaded;
}
@@ -1233,32 +992,6 @@ public class VTree extends FocusElementPanel implements Paintable,
}
/*
- * We need to fix the width of TreeNodes so that the float in
- * ie6compatNode does not wrap (see ticket #1245)
- */
- private void fixWidth() {
- nodeCaptionDiv.getStyle().setProperty("styleFloat", "left");
- nodeCaptionDiv.getStyle().setProperty("display", "inline");
- nodeCaptionDiv.getStyle().setProperty("marginLeft", "0");
- final int captionWidth = ie6compatnode.getOffsetWidth()
- + nodeCaptionDiv.getOffsetWidth();
- setWidth(captionWidth + "px");
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.google.gwt.user.client.ui.Widget#onAttach()
- */
- @Override
- public void onAttach() {
- super.onAttach();
- if (ie6compatnode != null) {
- fixWidth();
- }
- }
-
- /*
* (non-Javadoc)
*
* @see com.google.gwt.user.client.ui.Widget#onDetach()
@@ -1288,19 +1021,14 @@ public class VTree extends FocusElementPanel implements Paintable,
public void setFocused(boolean focused) {
if (!this.focused && focused) {
nodeCaptionDiv.addClassName(CLASSNAME_FOCUSED);
- if (BrowserInfo.get().isIE6()) {
- ie6compatnode.addClassName(CLASSNAME_FOCUSED);
- }
+
this.focused = focused;
- if (isIE6OrOpera()) {
+ if (BrowserInfo.get().isOpera()) {
nodeCaptionDiv.focus();
}
treeHasFocus = true;
} else if (this.focused && !focused) {
nodeCaptionDiv.removeClassName(CLASSNAME_FOCUSED);
- if (BrowserInfo.get().isIE6()) {
- ie6compatnode.removeClassName(CLASSNAME_FOCUSED);
- }
this.focused = focused;
treeHasFocus = false;
}
@@ -1313,6 +1041,34 @@ public class VTree extends FocusElementPanel implements Paintable,
Util.scrollIntoViewVertically(nodeCaptionDiv);
}
+ public void setIcon(String iconUrl) {
+ if (iconUrl != null) {
+ // Add icon if not present
+ if (icon == null) {
+ icon = new Icon(client);
+ DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv),
+ icon.getElement(), nodeCaptionSpan);
+ }
+ icon.setUri(iconUrl);
+ } else {
+ // Remove icon if present
+ if (icon != null) {
+ DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv),
+ icon.getElement());
+ icon = null;
+ }
+ }
+ }
+
+ public void setNodeStyleName(String styleName) {
+ addStyleName(TreeNode.CLASSNAME + "-" + styleName);
+ setStyleName(nodeCaptionDiv, TreeNode.CLASSNAME + "-caption-"
+ + styleName, true);
+ childNodeContainer.addStyleName(TreeNode.CLASSNAME + "-children-"
+ + styleName);
+
+ }
+
}
public VDropHandler getDropHandler() {
@@ -2187,7 +1943,7 @@ public class VTree extends FocusElementPanel implements Paintable,
*/
public Element getSubPartElement(String subPart) {
if ("fe".equals(subPart)) {
- if (isIE6OrOpera() && focusedNode != null) {
+ if (BrowserInfo.get().isOpera() && focusedNode != null) {
return focusedNode.getElement();
}
return getFocusElement();
@@ -2219,11 +1975,7 @@ public class VTree extends FocusElementPanel implements Paintable,
}
if (expandCollapse) {
- if (treeNode.ie6compatnode != null) {
- return treeNode.ie6compatnode;
- } else {
- return treeNode.getElement();
- }
+ return treeNode.getElement();
} else {
return treeNode.nodeCaptionSpan;
}
@@ -2267,8 +2019,7 @@ public class VTree extends FocusElementPanel implements Paintable,
return null;
}
- if (subElement == treeNode.getElement()
- || subElement == treeNode.ie6compatnode) {
+ if (subElement == treeNode.getElement()) {
// Targets expand/collapse arrow
isExpandCollapse = true;
}
@@ -2330,7 +2081,22 @@ public class VTree extends FocusElementPanel implements Paintable,
}
}
- private boolean isIE6OrOpera() {
- return BrowserInfo.get().isIE6() || BrowserInfo.get().isOpera();
+ public void registerAction(String key, String caption, String iconUrl) {
+ actionMap.put(key + "_c", caption);
+ if (iconUrl != null) {
+ actionMap.put(key + "_i", iconUrl);
+ } else {
+ actionMap.remove(key + "_i");
+ }
+
+ }
+
+ public void registerNode(TreeNode treeNode) {
+ keyToNode.put(treeNode.key, treeNode);
}
+
+ public void clearNodeToKeyMap() {
+ keyToNode.clear();
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/treetable/TreeTableConnector.java b/src/com/vaadin/terminal/gwt/client/ui/treetable/TreeTableConnector.java
new file mode 100644
index 0000000000..f81771781b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/treetable/TreeTableConnector.java
@@ -0,0 +1,102 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.treetable;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.FocusableScrollPanel;
+import com.vaadin.terminal.gwt.client.ui.table.TableConnector;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable.VScrollTableBody.VScrollTableRow;
+import com.vaadin.terminal.gwt.client.ui.treetable.VTreeTable.PendingNavigationEvent;
+import com.vaadin.ui.TreeTable;
+
+@Connect(TreeTable.class)
+public class TreeTableConnector extends TableConnector {
+ public static final String ATTRIBUTE_HIERARCHY_COLUMN_INDEX = "hci";
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ FocusableScrollPanel widget = null;
+ int scrollPosition = 0;
+ if (getWidget().collapseRequest) {
+ widget = (FocusableScrollPanel) getWidget().getWidget(1);
+ scrollPosition = widget.getScrollPosition();
+ }
+ getWidget().animationsEnabled = uidl.getBooleanAttribute("animate");
+ getWidget().colIndexOfHierarchy = uidl
+ .hasAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) ? uidl
+ .getIntAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) : 0;
+ int oldTotalRows = getWidget().getTotalRows();
+ super.updateFromUIDL(uidl, client);
+ if (getWidget().collapseRequest) {
+ if (getWidget().collapsedRowKey != null
+ && getWidget().scrollBody != null) {
+ VScrollTableRow row = getWidget().getRenderedRowByKey(
+ getWidget().collapsedRowKey);
+ if (row != null) {
+ getWidget().setRowFocus(row);
+ getWidget().focus();
+ }
+ }
+
+ int scrollPosition2 = widget.getScrollPosition();
+ if (scrollPosition != scrollPosition2) {
+ widget.setScrollPosition(scrollPosition);
+ }
+
+ // check which rows are needed from the server and initiate a
+ // deferred fetch
+ getWidget().onScroll(null);
+ }
+ // Recalculate table size if collapse request, or if page length is zero
+ // (not sent by server) and row count changes (#7908).
+ if (getWidget().collapseRequest
+ || (!uidl.hasAttribute("pagelength") && getWidget()
+ .getTotalRows() != oldTotalRows)) {
+ /*
+ * Ensure that possibly removed/added scrollbars are considered.
+ * Triggers row calculations, removes cached rows etc. Basically
+ * cleans up state. Be careful if touching this, you will break
+ * pageLength=0 if you remove this.
+ */
+ getWidget().triggerLazyColumnAdjustment(true);
+
+ getWidget().collapseRequest = false;
+ }
+ if (uidl.hasAttribute("focusedRow")) {
+ String key = uidl.getStringAttribute("focusedRow");
+ getWidget().setRowFocus(getWidget().getRenderedRowByKey(key));
+ getWidget().focusParentResponsePending = false;
+ } else if (uidl.hasAttribute("clearFocusPending")) {
+ // Special case to detect a response to a focusParent request that
+ // does not return any focusedRow because the selected node has no
+ // parent
+ getWidget().focusParentResponsePending = false;
+ }
+
+ while (!getWidget().collapseRequest
+ && !getWidget().focusParentResponsePending
+ && !getWidget().pendingNavigationEvents.isEmpty()) {
+ // Keep replaying any queued events as long as we don't have any
+ // potential content changes pending
+ PendingNavigationEvent event = getWidget().pendingNavigationEvents
+ .removeFirst();
+ getWidget()
+ .handleNavigation(event.keycode, event.ctrl, event.shift);
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTreeTable.class);
+ }
+
+ @Override
+ public VTreeTable getWidget() {
+ return (VTreeTable) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java b/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java
index 8e55400f31..2e15e7c445 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/treetable/VTreeTable.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.treetable;
import java.util.ArrayList;
import java.util.Iterator;
@@ -24,21 +24,19 @@ 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.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.ComputedStyle;
-import com.vaadin.terminal.gwt.client.RenderSpace;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow;
-import com.vaadin.terminal.gwt.client.ui.VTreeTable.VTreeTableScrollBody.VTreeTableRow;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable;
+import com.vaadin.terminal.gwt.client.ui.treetable.VTreeTable.VTreeTableScrollBody.VTreeTableRow;
public class VTreeTable extends VScrollTable {
- private static class PendingNavigationEvent {
- private final int keycode;
- private final boolean ctrl;
- private final boolean shift;
+ static class PendingNavigationEvent {
+ final int keycode;
+ final boolean ctrl;
+ final boolean shift;
public PendingNavigationEvent(int keycode, boolean ctrl, boolean shift) {
this.keycode = keycode;
@@ -59,82 +57,14 @@ public class VTreeTable extends VScrollTable {
}
}
- public static final String ATTRIBUTE_HIERARCHY_COLUMN_INDEX = "hci";
- private boolean collapseRequest;
+ boolean collapseRequest;
private boolean selectionPending;
- private int colIndexOfHierarchy;
- private String collapsedRowKey;
- private VTreeTableScrollBody scrollBody;
- private boolean animationsEnabled;
- private LinkedList<PendingNavigationEvent> pendingNavigationEvents = new LinkedList<VTreeTable.PendingNavigationEvent>();
- private boolean focusParentResponsePending;
-
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- FocusableScrollPanel widget = null;
- int scrollPosition = 0;
- if (collapseRequest) {
- widget = (FocusableScrollPanel) getWidget(1);
- scrollPosition = widget.getScrollPosition();
- }
- animationsEnabled = uidl.getBooleanAttribute("animate");
- colIndexOfHierarchy = uidl
- .hasAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) ? uidl
- .getIntAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) : 0;
- int oldTotalRows = getTotalRows();
- super.updateFromUIDL(uidl, client);
- if (collapseRequest) {
- if (collapsedRowKey != null && scrollBody != null) {
- VScrollTableRow row = getRenderedRowByKey(collapsedRowKey);
- if (row != null) {
- setRowFocus(row);
- focus();
- }
- }
-
- int scrollPosition2 = widget.getScrollPosition();
- if (scrollPosition != scrollPosition2) {
- widget.setScrollPosition(scrollPosition);
- }
-
- // check which rows are needed from the server and initiate a
- // deferred fetch
- onScroll(null);
- }
- // Recalculate table size if collapse request, or if page length is zero
- // (not sent by server) and row count changes (#7908).
- if (collapseRequest
- || (!uidl.hasAttribute("pagelength") && getTotalRows() != oldTotalRows)) {
- /*
- * Ensure that possibly removed/added scrollbars are considered.
- * Triggers row calculations, removes cached rows etc. Basically
- * cleans up state. Be careful if touching this, you will break
- * pageLength=0 if you remove this.
- */
- triggerLazyColumnAdjustment(true);
-
- collapseRequest = false;
- }
- if (uidl.hasAttribute("focusedRow")) {
- String key = uidl.getStringAttribute("focusedRow");
- setRowFocus(getRenderedRowByKey(key));
- focusParentResponsePending = false;
- } else if (uidl.hasAttribute("clearFocusPending")) {
- // Special case to detect a response to a focusParent request that
- // does not return any focusedRow because the selected node has no
- // parent
- focusParentResponsePending = false;
- }
-
- while (!collapseRequest && !focusParentResponsePending
- && !pendingNavigationEvents.isEmpty()) {
- // Keep replaying any queued events as long as we don't have any
- // potential content changes pending
- PendingNavigationEvent event = pendingNavigationEvents
- .removeFirst();
- handleNavigation(event.keycode, event.ctrl, event.shift);
- }
- }
+ int colIndexOfHierarchy;
+ String collapsedRowKey;
+ VTreeTableScrollBody scrollBody;
+ boolean animationsEnabled;
+ LinkedList<PendingNavigationEvent> pendingNavigationEvents = new LinkedList<VTreeTable.PendingNavigationEvent>();
+ boolean focusParentResponsePending;
@Override
protected VScrollTableBody createScrollBody() {
@@ -169,7 +99,7 @@ public class VTreeTable extends VScrollTable {
private boolean browserSupportsAnimation() {
BrowserInfo bi = BrowserInfo.get();
- return !(bi.isIE6() || bi.isIE7() || bi.isSafari4());
+ return !(bi.isSafari4());
}
class VTreeTableScrollBody extends VScrollTable.VScrollTableBody {
@@ -293,29 +223,6 @@ public class VTreeTable extends VScrollTable {
}
}
- @Override
- public RenderSpace getAllocatedSpace(Widget child) {
- if (widgetInHierarchyColumn == child) {
- final int hierarchyAndIconWidth = getHierarchyAndIconWidth();
- final RenderSpace allocatedSpace = super
- .getAllocatedSpace(child);
- return new RenderSpace() {
- @Override
- public int getWidth() {
- return allocatedSpace.getWidth()
- - hierarchyAndIconWidth;
- }
-
- @Override
- public int getHeight() {
- return allocatedSpace.getHeight();
- }
-
- };
- }
- return super.getAllocatedSpace(child);
- }
-
private int getHierarchyAndIconWidth() {
int consumedSpace = treeSpacer.getOffsetWidth();
if (treeSpacer.getParentElement().getChildCount() > 2) {
@@ -894,4 +801,5 @@ public class VTreeTable extends VScrollTable {
int newTotalRows = uidl.getIntAttribute("totalrows");
setTotalRows(newTotalRows);
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java
new file mode 100644
index 0000000000..328d0fc18b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java
@@ -0,0 +1,69 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.twincolselect;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.OptionGroupBaseConnector;
+import com.vaadin.ui.TwinColSelect;
+
+@Connect(TwinColSelect.class)
+public class TwinColSelectConnector extends OptionGroupBaseConnector implements
+ DirectionalManagedLayout {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // Captions are updated before super call to ensure the widths are set
+ // correctly
+ if (isRealUpdate(uidl)) {
+ getWidget().updateCaptions(uidl);
+ getLayoutManager().setNeedsHorizontalLayout(this);
+ }
+
+ super.updateFromUIDL(uidl, client);
+ }
+
+ @Override
+ protected void init() {
+ getLayoutManager().registerDependency(this,
+ getWidget().captionWrapper.getElement());
+ }
+
+ @Override
+ public void onUnregister() {
+ getLayoutManager().unregisterDependency(this,
+ getWidget().captionWrapper.getElement());
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VTwinColSelect.class);
+ }
+
+ @Override
+ public VTwinColSelect getWidget() {
+ return (VTwinColSelect) super.getWidget();
+ }
+
+ public void layoutVertically() {
+ if (isUndefinedHeight()) {
+ getWidget().clearInternalHeights();
+ } else {
+ getWidget().setInternalHeights();
+ }
+ }
+
+ public void layoutHorizontally() {
+ if (isUndefinedWidth()) {
+ getWidget().clearInternalWidths();
+ } else {
+ getWidget().setInternalWidths();
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/VTwinColSelect.java
index b569dbc94e..8f1ea09b8f 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/VTwinColSelect.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.twincolselect;
import java.util.ArrayList;
import java.util.HashSet;
@@ -26,10 +26,11 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.button.VButton;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.VOptionGroupBase;
public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
MouseDownHandler, DoubleClickHandler, SubPartAware {
@@ -46,7 +47,7 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
private final DoubleClickListBox selections;
- private FlowPanel captionWrapper;
+ FlowPanel captionWrapper;
private HTML optionsCaption = null;
@@ -60,8 +61,6 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
private final Panel panel;
- private boolean widthSet = false;
-
/**
* A ListBox which catches double clicks
*
@@ -157,18 +156,7 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
return selectionsCaption;
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- // Captions are updated before super call to ensure the widths are set
- // correctly
- if (!uidl.getBooleanAttribute("cached")) {
- updateCaptions(uidl);
- }
-
- super.updateFromUIDL(uidl, client);
- }
-
- private void updateCaptions(UIDL uidl) {
+ protected void updateCaptions(UIDL uidl) {
String leftCaption = (uidl.hasAttribute(ATTRIBUTE_LEFT_CAPTION) ? uidl
.getStringAttribute(ATTRIBUTE_LEFT_CAPTION) : null);
String rightCaption = (uidl.hasAttribute(ATTRIBUTE_RIGHT_CAPTION) ? uidl
@@ -238,32 +226,6 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
}
}
- int cols = -1;
- if (getColumns() > 0) {
- cols = getColumns();
- } else if (!widthSet) {
- cols = DEFAULT_COLUMN_COUNT;
- }
-
- if (cols >= 0) {
- String colWidth = cols + "em";
- String containerWidth = (2 * cols + 4) + "em";
- // Caption wrapper width == optionsSelect + buttons +
- // selectionsSelect
- String captionWrapperWidth = (2 * cols + 4 - 0.5) + "em";
-
- options.setWidth(colWidth);
- if (optionsCaption != null) {
- optionsCaption.setWidth(colWidth);
- }
- selections.setWidth(colWidth);
- if (selectionsCaption != null) {
- selectionsCaption.setWidth(colWidth);
- }
- buttons.setWidth("3.5em");
- optionsContainer.setWidth(containerWidth);
- captionWrapper.setWidth(captionWrapperWidth);
- }
if (getRows() > 0) {
options.setVisibleItemCount(getRows());
selections.setVisibleItemCount(getRows());
@@ -297,7 +259,7 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
Set<String> movedItems = moveSelectedItems(options, selections);
selectedKeys.addAll(movedItems);
- client.updateVariable(id, "selected",
+ client.updateVariable(paintableId, "selected",
selectedKeys.toArray(new String[selectedKeys.size()]),
isImmediate());
}
@@ -306,7 +268,7 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
Set<String> movedItems = moveSelectedItems(selections, options);
selectedKeys.removeAll(movedItems);
- client.updateVariable(id, "selected",
+ client.updateVariable(paintableId, "selected",
selectedKeys.toArray(new String[selectedKeys.size()]),
isImmediate());
}
@@ -372,35 +334,15 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
}
}
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- if ("".equals(height)) {
- options.setHeight("");
- selections.setHeight("");
- } else {
- setInternalHeights();
- }
+ void clearInternalHeights() {
+ selections.setHeight("");
+ options.setHeight("");
}
- private void setInternalHeights() {
- int captionHeight = 0;
- int totalHeight;
- if (BrowserInfo.get().isIE6()) {
- String o = getElement().getStyle().getOverflow();
+ void setInternalHeights() {
+ int captionHeight = Util.getRequiredHeight(captionWrapper);
+ int totalHeight = getOffsetHeight();
- getElement().getStyle().setOverflow(Overflow.HIDDEN);
- totalHeight = getOffsetHeight();
- getElement().getStyle().setProperty("overflow", o);
- } else {
- totalHeight = getOffsetHeight();
- }
-
- if (optionsCaption != null) {
- captionHeight = Util.getRequiredHeight(optionsCaption);
- } else if (selectionsCaption != null) {
- captionHeight = Util.getRequiredHeight(selectionsCaption);
- }
String selectHeight = (totalHeight - captionHeight) + "px";
selections.setHeight(selectHeight);
@@ -408,27 +350,40 @@ public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
}
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- if (!"".equals(width) && width != null) {
- setInternalWidths();
- widthSet = true;
+ void clearInternalWidths() {
+ int cols = -1;
+ if (getColumns() > 0) {
+ cols = getColumns();
} else {
- widthSet = false;
+ cols = DEFAULT_COLUMN_COUNT;
+ }
+
+ if (cols >= 0) {
+ String colWidth = cols + "em";
+ String containerWidth = (2 * cols + 4) + "em";
+ // Caption wrapper width == optionsSelect + buttons +
+ // selectionsSelect
+ String captionWrapperWidth = (2 * cols + 4 - 0.5) + "em";
+
+ options.setWidth(colWidth);
+ if (optionsCaption != null) {
+ optionsCaption.setWidth(colWidth);
+ }
+ selections.setWidth(colWidth);
+ if (selectionsCaption != null) {
+ selectionsCaption.setWidth(colWidth);
+ }
+ buttons.setWidth("3.5em");
+ optionsContainer.setWidth(containerWidth);
+ captionWrapper.setWidth(captionWrapperWidth);
}
}
- private void setInternalWidths() {
+ void setInternalWidths() {
DOM.setStyleAttribute(getElement(), "position", "relative");
int bordersAndPaddings = Util.measureHorizontalPaddingAndBorder(
buttons.getElement(), 0);
- if (BrowserInfo.get().isIE6()) {
- // IE6 sets a border on selects by default..
- bordersAndPaddings += 4;
- }
-
int buttonWidth = Util.getRequiredWidth(buttons);
int totalWidth = getOffsetWidth();
diff --git a/src/com/vaadin/terminal/gwt/client/ui/upload/UploadConnector.java b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadConnector.java
new file mode 100644
index 0000000000..0a2c0b241e
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadConnector.java
@@ -0,0 +1,67 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.upload;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.Upload;
+
+@Connect(value = Upload.class, loadStyle = LoadStyle.LAZY)
+public class UploadConnector extends AbstractComponentConnector implements
+ Paintable {
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+ if (uidl.hasAttribute("notStarted")) {
+ getWidget().t.schedule(400);
+ return;
+ }
+ if (uidl.hasAttribute("forceSubmit")) {
+ getWidget().submit();
+ return;
+ }
+ getWidget().setImmediate(getState().isImmediate());
+ getWidget().client = client;
+ getWidget().paintableId = uidl.getId();
+ getWidget().nextUploadId = uidl.getIntAttribute("nextid");
+ final String action = client.translateVaadinUri(uidl
+ .getStringVariable("action"));
+ getWidget().element.setAction(action);
+ if (uidl.hasAttribute("buttoncaption")) {
+ getWidget().submitButton.setText(uidl
+ .getStringAttribute("buttoncaption"));
+ getWidget().submitButton.setVisible(true);
+ } else {
+ getWidget().submitButton.setVisible(false);
+ }
+ getWidget().fu.setName(getWidget().paintableId + "_file");
+
+ if (!isEnabled() || isReadOnly()) {
+ getWidget().disableUpload();
+ } else if (!uidl.getBooleanAttribute("state")) {
+ // Enable the button only if an upload is not in progress
+ getWidget().enableUpload();
+ getWidget().ensureTargetFrame();
+ }
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VUpload.class);
+ }
+
+ @Override
+ public VUpload getWidget() {
+ return (VUpload) super.getWidget();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategy.java b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java
index 455a9bf601..18cfc643d3 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategy.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java
@@ -1,16 +1,16 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.upload;
public class UploadIFrameOnloadStrategy {
native void hookEvents(com.google.gwt.dom.client.Element iframe,
VUpload upload)
/*-{
- iframe.onload = $entry(function() {
- upload.@com.vaadin.terminal.gwt.client.ui.VUpload::onSubmitComplete()();
- });
+ iframe.onload = function() {
+ upload.@com.vaadin.terminal.gwt.client.ui.upload.VUpload::onSubmitComplete()();
+ };
}-*/;
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategyIE.java b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java
index b23d82fa22..19d38a8a95 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/UploadIFrameOnloadStrategyIE.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java
@@ -1,7 +1,7 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.upload;
import com.google.gwt.dom.client.Element;
@@ -13,11 +13,11 @@ public class UploadIFrameOnloadStrategyIE extends UploadIFrameOnloadStrategy {
@Override
native void hookEvents(Element iframe, VUpload upload)
/*-{
- iframe.onreadystatechange = $entry(function() {
+ iframe.onreadystatechange = function() {
if (iframe.readyState == 'complete') {
- upload.@com.vaadin.terminal.gwt.client.ui.VUpload::onSubmitComplete()();
+ upload.@com.vaadin.terminal.gwt.client.ui.upload.VUpload::onSubmitComplete()();
}
- });
+ };
}-*/;
@Override
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUpload.java b/src/com/vaadin/terminal/gwt/client/ui/upload/VUpload.java
index f3105b70a1..4fe53fb89c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VUpload.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/upload/VUpload.java
@@ -2,7 +2,7 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.upload;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
@@ -23,10 +23,9 @@ import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.button.VButton;
/**
*
@@ -34,7 +33,7 @@ import com.vaadin.terminal.gwt.client.VTooltip;
* events even though the upload component is already detached.
*
*/
-public class VUpload extends SimplePanel implements Paintable {
+public class VUpload extends SimplePanel {
private final class MyFileUpload extends FileUpload {
@Override
@@ -72,19 +71,19 @@ public class VUpload extends SimplePanel implements Paintable {
ApplicationConnection client;
- private String paintableId;
+ protected String paintableId;
/**
* Button that initiates uploading
*/
- private final VButton submitButton;
+ protected final VButton submitButton;
/**
* When expecting big files, programmer may initiate some UI changes when
* uploading the file starts. Bit after submitting file we'll visit the
* server to check possible changes.
*/
- private Timer t;
+ protected Timer t;
/**
* some browsers tries to send form twice if submit is called in button
@@ -99,11 +98,11 @@ public class VUpload extends SimplePanel implements Paintable {
private Hidden maxfilesize = new Hidden();
- private FormElement element;
+ protected FormElement element;
private com.google.gwt.dom.client.Element synthesizedFrame;
- private int nextUploadId;
+ protected int nextUploadId;
public VUpload() {
super(com.google.gwt.dom.client.Document.get().createFormElement());
@@ -144,47 +143,9 @@ public class VUpload extends SimplePanel implements Paintable {
private static native void setEncoding(Element form, String encoding)
/*-{
form.enctype = encoding;
- // For IE6
- form.encoding = encoding;
}-*/;
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
- if (uidl.hasAttribute("notStarted")) {
- t.schedule(400);
- return;
- }
- if (uidl.hasAttribute("forceSubmit")) {
- submit();
- return;
- }
- setImmediate(uidl.getBooleanAttribute("immediate"));
- this.client = client;
- paintableId = uidl.getId();
- nextUploadId = uidl.getIntAttribute("nextid");
- final String action = client.translateVaadinUri(uidl
- .getStringVariable("action"));
- element.setAction(action);
- if (uidl.hasAttribute("buttoncaption")) {
- submitButton.setText(uidl.getStringAttribute("buttoncaption"));
- submitButton.setVisible(true);
- } else {
- submitButton.setVisible(false);
- }
- fu.setName(paintableId + "_file");
-
- if (uidl.hasAttribute("disabled") || uidl.hasAttribute("readonly")) {
- disableUpload();
- } else if (!uidl.getBooleanAttribute("state")) {
- // Enable the button only if an upload is not in progress
- enableUpload();
- ensureTargetFrame();
- }
- }
-
- private void setImmediate(boolean booleanAttribute) {
+ protected void setImmediate(boolean booleanAttribute) {
if (immediate != booleanAttribute) {
immediate = booleanAttribute;
if (immediate) {
@@ -278,7 +239,7 @@ public class VUpload extends SimplePanel implements Paintable {
});
}
- private void submit() {
+ protected void submit() {
if (fu.getFilename().length() == 0 || submitted || !enabled) {
VConsole.log("Submit cancelled (disabled, no file or already submitted)");
return;
@@ -316,15 +277,12 @@ public class VUpload extends SimplePanel implements Paintable {
}
}
- private void ensureTargetFrame() {
+ protected void ensureTargetFrame() {
if (synthesizedFrame == null) {
// Attach a hidden IFrame to the form. This is the target iframe to
- // which
- // the form will be submitted. We have to create the iframe using
- // innerHTML,
- // because setting an iframe's 'name' property dynamically doesn't
- // work on
- // most browsers.
+ // which the form will be submitted. We have to create the iframe
+ // using innerHTML, because setting an iframe's 'name' property
+ // dynamically doesn't work on most browsers.
DivElement dummy = Document.get().createDivElement();
dummy.setInnerHTML("<iframe src=\"javascript:''\" name='"
+ getFrameName()
@@ -355,5 +313,4 @@ public class VUpload extends SimplePanel implements Paintable {
synthesizedFrame = null;
}
}
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VVideo.java b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java
index 8599ffb279..484000b8d1 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VVideo.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java
@@ -2,18 +2,16 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.video;
import com.google.gwt.dom.client.Document;
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.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ui.VMediaBase;
public class VVideo extends VMediaBase {
- public static final String ATTR_POSTER = "poster";
private static String CLASSNAME = "v-video";
@@ -27,22 +25,6 @@ public class VVideo extends VMediaBase {
updateDimensionsWhenMetadataLoaded(getElement());
}
- @Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
- super.updateFromUIDL(uidl, client);
- setPosterFromUIDL(uidl);
- }
-
- private void setPosterFromUIDL(UIDL uidl) {
- if (uidl.hasAttribute(ATTR_POSTER)) {
- video.setPoster(client.translateVaadinUri(uidl
- .getStringAttribute(ATTR_POSTER)));
- }
- }
-
/**
* Registers a listener that updates the dimensions of the widget when the
* video metadata has been loaded.
@@ -53,7 +35,7 @@ public class VVideo extends VMediaBase {
/*-{
var self = this;
el.addEventListener('loadedmetadata', $entry(function(e) {
- self.@com.vaadin.terminal.gwt.client.ui.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight);
+ self.@com.vaadin.terminal.gwt.client.ui.video.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight);
}), false);
}-*/;
@@ -74,4 +56,9 @@ public class VVideo extends VMediaBase {
protected String getDefaultAltHtml() {
return "Your browser does not support the <code>video</code> element.";
}
+
+ public void setPoster(String poster) {
+ video.setPoster(poster);
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/video/VideoConnector.java b/src/com/vaadin/terminal/gwt/client/ui/video/VideoConnector.java
new file mode 100644
index 0000000000..ec763fff07
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/video/VideoConnector.java
@@ -0,0 +1,46 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.video;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector;
+import com.vaadin.ui.Video;
+
+@Connect(Video.class)
+public class VideoConnector extends MediaBaseConnector {
+ public static final String ATTR_POSTER = "poster";
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ super.updateFromUIDL(uidl, client);
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+ super.updateFromUIDL(uidl, client);
+ setPosterFromUIDL(uidl);
+ }
+
+ private void setPosterFromUIDL(UIDL uidl) {
+ if (uidl.hasAttribute(ATTR_POSTER)) {
+ getWidget().setPoster(
+ getConnection().translateVaadinUri(
+ uidl.getStringAttribute(ATTR_POSTER)));
+ }
+ }
+
+ @Override
+ public VVideo getWidget() {
+ return (VVideo) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VVideo.class);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VWindow.java b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java
index 103979927a..d08387fc6d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VWindow.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java
@@ -2,57 +2,52 @@
@VaadinApache2LicenseForJavaFiles@
*/
-package com.vaadin.terminal.gwt.client.ui;
+package com.vaadin.terminal.gwt.client.ui.window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
-import java.util.Iterator;
-import java.util.Set;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.DomEvent.Type;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
-import com.google.gwt.event.shared.EventHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Frame;
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.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.Console;
-import com.vaadin.terminal.gwt.client.Container;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.Focusable;
-import com.vaadin.terminal.gwt.client.Paintable;
-import com.vaadin.terminal.gwt.client.RenderSpace;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.LayoutManager;
import com.vaadin.terminal.gwt.client.Util;
-import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
+import com.vaadin.terminal.gwt.client.ui.FocusableScrollPanel;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
+import com.vaadin.terminal.gwt.client.ui.VOverlay;
/**
* "Sub window" component.
*
* @author Vaadin Ltd
*/
-public class VWindow extends VOverlay implements Container,
- ShortcutActionHandlerOwner, ScrollHandler, KeyDownHandler,
- FocusHandler, BlurHandler, BeforeShortcutActionListener, Focusable {
+public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
+ ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
/**
* Minimum allowed height of a window. This refers to the content area, not
@@ -72,34 +67,21 @@ public class VWindow extends VOverlay implements Container,
public static final String CLASSNAME = "v-window";
- /**
- * Difference between offsetWidth and inner width for the content area.
- */
- private int contentAreaBorderPadding = -1;
- /**
- * Pixels used by inner borders and paddings horizontally (calculated only
- * once). This is the difference between the width of the root element and
- * the content area, such that if root element width is set to "XYZpx" the
- * inner width (width-border-padding) of the content area is
- * X-contentAreaRootDifference.
- */
- private int contentAreaToRootDifference = -1;
-
private static final int STACKING_OFFSET_PIXELS = 15;
public static final int Z_INDEX = 10000;
- private Paintable layout;
+ ComponentConnector layout;
- private Element contents;
+ Element contents;
- private Element header;
+ Element header;
- private Element footer;
+ Element footer;
private Element resizeBox;
- private final FocusableScrollPanel contentPanel = new FocusableScrollPanel();
+ final FocusableScrollPanel contentPanel = new FocusableScrollPanel();
private boolean dragging;
@@ -117,11 +99,11 @@ public class VWindow extends VOverlay implements Container,
private int origH;
- private Element closeBox;
+ Element closeBox;
protected ApplicationConnection client;
- private String id;
+ String id;
ShortcutActionHandler shortcutHandler;
@@ -131,13 +113,13 @@ public class VWindow extends VOverlay implements Container,
/** Last known positiony read from UIDL or updated to application connection */
private int uidlPositionY = -1;
- private boolean vaadinModality = false;
+ boolean vaadinModality = false;
- private boolean resizable = true;
+ boolean resizable = true;
private boolean draggable = true;
- private boolean resizeLazy = false;
+ boolean resizeLazy = false;
private Element modalityCurtain;
private Element draggingCurtain;
@@ -147,40 +129,18 @@ public class VWindow extends VOverlay implements Container,
private boolean closable = true;
- boolean dynamicWidth = false;
- boolean dynamicHeight = false;
- boolean layoutRelativeWidth = false;
- boolean layoutRelativeHeight = false;
-
// If centered (via UIDL), the window should stay in the centered -mode
// until a position is received from the server, or the user moves or
// resizes the window.
boolean centered = false;
- private RenderSpace renderSpace = new RenderSpace(MIN_CONTENT_AREA_WIDTH,
- MIN_CONTENT_AREA_HEIGHT, true);
-
- private String width;
+ boolean immediate;
- private String height;
+ private Element wrapper;
- private boolean immediate;
+ boolean visibilityChangesDisabled;
- private Element wrapper, wrapper2;
-
- private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
- VPanel.CLICK_EVENT_IDENTIFIER) {
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- private boolean visibilityChangesDisabled;
-
- private int bringToFrontSequence = -1;
+ int bringToFrontSequence = -1;
private VLazyExecutor delayedContentsSizeUpdater = new VLazyExecutor(200,
new ScheduledCommand() {
@@ -195,12 +155,7 @@ public class VWindow extends VOverlay implements Container,
// Different style of shadow for windows
setShadowStyle("window");
- final int order = windowOrder.size();
- setWindowOrder(order);
- windowOrder.add(this);
constructDOM();
- setPopupPosition(order * STACKING_OFFSET_PIXELS, order
- * STACKING_OFFSET_PIXELS);
contentPanel.addScrollHandler(this);
contentPanel.addKeyDownHandler(this);
contentPanel.addFocusHandler(this);
@@ -224,7 +179,26 @@ public class VWindow extends VOverlay implements Container,
* @return
*/
private boolean isActive() {
- return windowOrder.get(windowOrder.size() - 1).equals(this);
+ return equals(getTopmostWindow());
+ }
+
+ private static VWindow getTopmostWindow() {
+ return windowOrder.get(windowOrder.size() - 1);
+ }
+
+ void setWindowOrderAndPosition() {
+ // This cannot be done in the constructor as the widgets are created in
+ // a different order than on they should appear on screen
+ if (windowOrder.contains(this)) {
+ // Already set
+ return;
+ }
+ final int order = windowOrder.size();
+ setWindowOrder(order);
+ windowOrder.add(this);
+ setPopupPosition(order * STACKING_OFFSET_PIXELS, order
+ * STACKING_OFFSET_PIXELS);
+
}
private void setWindowOrder(int order) {
@@ -267,15 +241,11 @@ public class VWindow extends VOverlay implements Container,
wrapper = DOM.createDiv();
DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
- wrapper2 = DOM.createDiv();
- DOM.setElementProperty(wrapper2, "className", CLASSNAME + "-wrap2");
-
- DOM.appendChild(wrapper2, closeBox);
- DOM.appendChild(wrapper2, header);
+ DOM.appendChild(wrapper, header);
+ DOM.appendChild(wrapper, closeBox);
DOM.appendChild(header, headerText);
- DOM.appendChild(wrapper2, contents);
- DOM.appendChild(wrapper2, footer);
- DOM.appendChild(wrapper, wrapper2);
+ DOM.appendChild(wrapper, contents);
+ DOM.appendChild(wrapper, footer);
DOM.appendChild(super.getContainerElement(), wrapper);
sinkEvents(Event.MOUSEEVENTS | Event.TOUCHEVENTS | Event.ONCLICK
@@ -285,236 +255,12 @@ public class VWindow extends VOverlay implements Container,
}
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- id = uidl.getId();
- this.client = client;
-
- // Workaround needed for Testing Tools (GWT generates window DOM
- // slightly different in different browsers).
- DOM.setElementProperty(closeBox, "id", id + "_window_close");
-
- if (uidl.hasAttribute("invisible")) {
- hide();
- return;
- }
-
- if (!uidl.hasAttribute("cached")) {
- if (uidl.getBooleanAttribute("modal") != vaadinModality) {
- setVaadinModality(!vaadinModality);
- }
- if (!isAttached()) {
- setVisible(false); // hide until possible centering
- show();
- }
- if (uidl.getBooleanAttribute("resizable") != resizable) {
- setResizable(!resizable);
- }
- resizeLazy = uidl.hasAttribute(VView.RESIZE_LAZY);
-
- setDraggable(!uidl.hasAttribute("fixedposition"));
-
- // Caption must be set before required header size is measured. If
- // the caption attribute is missing the caption should be cleared.
- setCaption(uidl.getStringAttribute("caption"),
- uidl.getStringAttribute("icon"));
- }
-
- visibilityChangesDisabled = true;
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
- visibilityChangesDisabled = false;
-
- clickEventHandler.handleEventHandlerRegistration(client);
-
- immediate = uidl.hasAttribute("immediate");
-
- setClosable(!uidl.getBooleanAttribute("readonly"));
-
- // Initialize the position form UIDL
- int positionx = uidl.getIntVariable("positionx");
- int positiony = uidl.getIntVariable("positiony");
- if (positionx >= 0 || positiony >= 0) {
- if (positionx < 0) {
- positionx = 0;
- }
- if (positiony < 0) {
- positiony = 0;
- }
- setPopupPosition(positionx, positiony);
- }
-
- boolean showingUrl = false;
- int childIndex = 0;
- UIDL childUidl = uidl.getChildUIDL(childIndex++);
- while ("open".equals(childUidl.getTag())) {
- // TODO multiple opens with the same target will in practice just
- // open the last one - should we fix that somehow?
- final String parsedUri = client.translateVaadinUri(childUidl
- .getStringAttribute("src"));
- if (!childUidl.hasAttribute("name")) {
- final Frame frame = new Frame();
- DOM.setStyleAttribute(frame.getElement(), "width", "100%");
- DOM.setStyleAttribute(frame.getElement(), "height", "100%");
- DOM.setStyleAttribute(frame.getElement(), "border", "0px");
- frame.setUrl(parsedUri);
- contentPanel.setWidget(frame);
- showingUrl = true;
- } else {
- final String target = childUidl.getStringAttribute("name");
- Window.open(parsedUri, target, "");
- }
- childUidl = uidl.getChildUIDL(childIndex++);
- }
-
- final Paintable lo = client.getPaintable(childUidl);
- if (layout != null) {
- if (layout != lo) {
- // remove old
- client.unregisterPaintable(layout);
- contentPanel.remove((Widget) layout);
- // add new
- if (!showingUrl) {
- contentPanel.setWidget((Widget) lo);
- }
- layout = lo;
- }
- } else if (!showingUrl) {
- contentPanel.setWidget((Widget) lo);
- layout = lo;
- }
-
- dynamicWidth = !uidl.hasAttribute("width");
- dynamicHeight = !uidl.hasAttribute("height");
-
- layoutRelativeWidth = uidl.hasAttribute("layoutRelativeWidth");
- layoutRelativeHeight = uidl.hasAttribute("layoutRelativeHeight");
-
- if (dynamicWidth && layoutRelativeWidth) {
- /*
- * Relative layout width, fix window width before rendering (width
- * according to caption)
- */
- setNaturalWidth();
- }
-
- layout.updateFromUIDL(childUidl, client);
- if (!dynamicHeight && layoutRelativeWidth) {
- /*
- * Relative layout width, and fixed height. Must update the size to
- * be able to take scrollbars into account (layout gets narrower
- * space if it is higher than the window) -> only vertical scrollbar
- */
- client.runDescendentsLayout(this);
- }
-
- /*
- * No explicit width is set and the layout does not have relative width
- * so fix the size according to the layout.
- */
- if (dynamicWidth && !layoutRelativeWidth) {
- setNaturalWidth();
- }
-
- if (dynamicHeight && layoutRelativeHeight) {
- // Prevent resizing until height has been fixed
- resizable = false;
- }
-
- // we may have actions and notifications
- if (uidl.getChildCount() > 1) {
- final int cnt = uidl.getChildCount();
- for (int i = 1; i < cnt; i++) {
- childUidl = uidl.getChildUIDL(i);
- if (childUidl.getTag().equals("actions")) {
- if (shortcutHandler == null) {
- shortcutHandler = new ShortcutActionHandler(id, client);
- }
- shortcutHandler.updateActionMap(childUidl);
- } else if (childUidl.getTag().equals("notifications")) {
- for (final Iterator<?> it = childUidl.getChildIterator(); it
- .hasNext();) {
- final UIDL notification = (UIDL) it.next();
- VNotification.showNotification(client, notification);
- }
- }
- }
-
- }
-
- // setting scrollposition must happen after children is rendered
- contentPanel.setScrollPosition(uidl.getIntVariable("scrollTop"));
- contentPanel.setHorizontalScrollPosition(uidl
- .getIntVariable("scrollLeft"));
-
- // Center this window on screen if requested
- // This has to be here because we might not know the content size before
- // everything is painted into the window
- if (uidl.getBooleanAttribute("center")) {
- // mark as centered - this is unset on move/resize
- centered = true;
- center();
- } else {
- // don't try to center the window anymore
- centered = false;
- }
- updateShadowSizeAndPosition();
- setVisible(true);
-
- boolean sizeReduced = false;
- // ensure window is not larger than browser window
- if (getOffsetWidth() > Window.getClientWidth()) {
- setWidth(Window.getClientWidth() + "px");
- sizeReduced = true;
- }
- if (getOffsetHeight() > Window.getClientHeight()) {
- setHeight(Window.getClientHeight() + "px");
- sizeReduced = true;
- }
-
- if (dynamicHeight && layoutRelativeHeight) {
- /*
- * Window height is undefined, layout is 100% high so the layout
- * should define the initial window height but on resize the layout
- * should be as high as the window. We fix the height to deal with
- * this.
- */
-
- int h = contents.getOffsetHeight() + getExtraHeight();
- int w = getElement().getOffsetWidth();
-
- client.updateVariable(id, "height", h, false);
- client.updateVariable(id, "width", w, true);
- }
-
- if (sizeReduced) {
- // If we changed the size we need to update the size of the child
- // component if it is relative (#3407)
- client.runDescendentsLayout(this);
- }
-
- Util.runWebkitOverflowAutoFix(contentPanel.getElement());
-
- client.getView().scrollIntoView(uidl);
-
- if (uidl.hasAttribute("bringToFront")) {
- /*
- * Focus as a side-efect. Will be overridden by
- * ApplicationConnection if another component was focused by the
- * server side.
- */
- contentPanel.focus();
- bringToFrontSequence = uidl.getIntAttribute("bringToFront");
- deferOrdering();
- }
- }
-
/**
* Calling this method will defer ordering algorithm, to order windows based
* on servers bringToFront and modality instructions. Non changed windows
* will be left intact.
*/
- private static void deferOrdering() {
+ static void deferOrdering() {
if (!orderingDefered) {
orderingDefered = true;
Scheduler.get().scheduleFinally(new Command() {
@@ -569,7 +315,7 @@ public class VWindow extends VOverlay implements Container,
}
}
- private void setDraggable(boolean draggable) {
+ void setDraggable(boolean draggable) {
if (this.draggable == draggable) {
return;
}
@@ -589,69 +335,6 @@ public class VWindow extends VOverlay implements Container,
}
}
- private void setNaturalWidth() {
- /*
- * Use max(layout width, window width) i.e layout content width or
- * caption width. We remove the previous set width so the width is
- * allowed to shrink. All widths are measured as outer sizes, i.e. the
- * borderWidth is added to the content.
- */
-
- DOM.setStyleAttribute(getElement(), "width", "");
-
- String oldHeaderWidth = ""; // Only for IE6
- if (BrowserInfo.get().isIE6()) {
- /*
- * For some reason IE6 has title DIV set to width 100% which
- * interferes with the header measuring. Also IE6 has width set to
- * the contentPanel.
- */
- oldHeaderWidth = headerText.getStyle().getProperty("width");
- DOM.setStyleAttribute(contentPanel.getElement(), "width", "auto");
- DOM.setStyleAttribute(contentPanel.getElement(), "zoom", "1");
- headerText.getStyle().setProperty("width", "auto");
- }
-
- // Content
- int contentWidth = contentPanel.getElement().getScrollWidth();
- contentWidth += getContentAreaToRootDifference();
-
- // Window width (caption)
- int windowCaptionWidth = getOffsetWidth();
-
- int naturalWidth = (contentWidth > windowCaptionWidth ? contentWidth
- : windowCaptionWidth);
-
- if (BrowserInfo.get().isIE6()) {
- headerText.getStyle().setProperty("width", oldHeaderWidth);
- }
-
- setWidth(naturalWidth + "px");
- }
-
- private int getContentAreaToRootDifference() {
- if (contentAreaToRootDifference < 0) {
- measure();
- }
- return contentAreaToRootDifference;
- }
-
- private void measure() {
- if (!isAttached()) {
- return;
- }
-
- contentAreaBorderPadding = Util.measureHorizontalPaddingAndBorder(
- contents, 4);
- int wrapperPaddingBorder = Util.measureHorizontalPaddingAndBorder(
- wrapper, 0)
- + Util.measureHorizontalPaddingAndBorder(wrapper2, 0);
-
- contentAreaToRootDifference = wrapperPaddingBorder
- + contentAreaBorderPadding;
-
- }
-
/**
* Sets the closable state of the window. Additionally hides/shows the close
* button according to the new state.
@@ -697,42 +380,6 @@ public class VWindow extends VOverlay implements Container,
showModalityCurtain();
}
super.show();
-
- setFF2CaretFixEnabled(true);
- fixFF3OverflowBug();
- }
-
- /** Disable overflow auto with FF3 to fix #1837. */
- private void fixFF3OverflowBug() {
- if (BrowserInfo.get().isFF3()) {
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- DOM.setStyleAttribute(getElement(), "overflow", "");
- }
- });
- }
- }
-
- /**
- * Fix "missing cursor" browser bug workaround for FF2 in Windows and Linux.
- *
- * Calling this method has no effect on other browsers than the ones based
- * on Gecko 1.8
- *
- * @param enable
- */
- private void setFF2CaretFixEnabled(boolean enable) {
- if (BrowserInfo.get().isFF2()) {
- if (enable) {
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- DOM.setStyleAttribute(getElement(), "overflow", "auto");
- }
- });
- } else {
- DOM.setStyleAttribute(getElement(), "overflow", "");
- }
- }
}
@Override
@@ -747,7 +394,7 @@ public class VWindow extends VOverlay implements Container,
windowOrder.remove(this);
}
- private void setVaadinModality(boolean modality) {
+ void setVaadinModality(boolean modality) {
vaadinModality = modality;
if (vaadinModality) {
if (isAttached()) {
@@ -765,14 +412,6 @@ public class VWindow extends VOverlay implements Container,
}
private void showModalityCurtain() {
- if (BrowserInfo.get().isFF2()) {
- DOM.setStyleAttribute(
- getModalityCurtain(),
- "height",
- DOM.getElementPropertyInt(RootPanel.getBodyElement(),
- "offsetHeight") + "px");
- DOM.setStyleAttribute(getModalityCurtain(), "position", "absolute");
- }
DOM.setStyleAttribute(getModalityCurtain(), "zIndex",
"" + (windowOrder.indexOf(this) + Z_INDEX));
if (isShowing()) {
@@ -792,15 +431,11 @@ public class VWindow extends VOverlay implements Container,
* iframes (etc) do not steal event.
*/
private void showDraggingCurtain() {
- setFF2CaretFixEnabled(false); // makes FF2 slow
-
DOM.appendChild(RootPanel.getBodyElement(), getDraggingCurtain());
}
private void hideDraggingCurtain() {
if (draggingCurtain != null) {
- setFF2CaretFixEnabled(true); // makes FF2 slow
-
DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain);
}
}
@@ -810,15 +445,11 @@ public class VWindow extends VOverlay implements Container,
* that iframes (etc) do not steal event.
*/
private void showResizingCurtain() {
- setFF2CaretFixEnabled(false); // makes FF2 slow
-
DOM.appendChild(RootPanel.getBodyElement(), getResizingCurtain());
}
private void hideResizingCurtain() {
if (resizingCurtain != null) {
- setFF2CaretFixEnabled(true); // makes FF2 slow
-
DOM.removeChild(RootPanel.getBodyElement(), resizingCurtain);
}
}
@@ -854,7 +485,7 @@ public class VWindow extends VOverlay implements Container,
return curtain;
}
- private void setResizable(boolean resizability) {
+ void setResizable(boolean resizability) {
resizable = resizability;
if (resizability) {
DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
@@ -1044,13 +675,15 @@ public class VWindow extends VOverlay implements Container,
}
int w = Util.getTouchOrMouseClientX(event) - startX + origW;
- if (w < MIN_CONTENT_AREA_WIDTH + getContentAreaToRootDifference()) {
- w = MIN_CONTENT_AREA_WIDTH + getContentAreaToRootDifference();
+ int minWidth = getMinWidth();
+ if (w < minWidth) {
+ w = minWidth;
}
int h = Util.getTouchOrMouseClientY(event) - startY + origH;
- if (h < MIN_CONTENT_AREA_HEIGHT + getExtraHeight()) {
- h = MIN_CONTENT_AREA_HEIGHT + getExtraHeight();
+ int minHeight = getMinHeight();
+ if (h < minHeight) {
+ h = minHeight;
}
setWidth(w + "px");
@@ -1074,121 +707,30 @@ public class VWindow extends VOverlay implements Container,
private void updateContentsSize() {
// Update child widget dimensions
if (client != null) {
- client.handleComponentRelativeSize((Widget) layout);
- client.runDescendentsLayout((HasWidgets) layout);
+ client.handleComponentRelativeSize(layout.getWidget());
+ client.runDescendentsLayout((HasWidgets) layout.getWidget());
}
- Util.runWebkitOverflowAutoFix(contentPanel.getElement());
+ LayoutManager layoutManager = LayoutManager.get(client);
+ layoutManager.setNeedsMeasure(ConnectorMap.get(client).getConnector(
+ this));
+ layoutManager.layoutNow();
}
@Override
- /**
- * Width is set to the out-most element (v-window).
- *
- * This function should never be called with percentage values (it will
- * throw an exception)
- */
public void setWidth(String width) {
- this.width = width;
- if (!isAttached()) {
- return;
- }
- if (width != null && !"".equals(width)) {
- int rootPixelWidth = -1;
- if (width.indexOf("px") < 0) {
- /*
- * Convert non-pixel values to pixels by setting the width and
- * then measuring it. Updates the "width" variable with the
- * pixel width.
- */
- DOM.setStyleAttribute(getElement(), "width", width);
- rootPixelWidth = getElement().getOffsetWidth();
- width = rootPixelWidth + "px";
- } else {
- rootPixelWidth = Integer.parseInt(width.substring(0,
- width.indexOf("px")));
- }
-
- // "width" now contains the new width in pixels
-
- if (BrowserInfo.get().isIE6()) {
- getElement().getStyle().setProperty("overflow", "hidden");
- }
-
- // Apply the new pixel width
- getElement().getStyle().setProperty("width", width);
-
- // Caculate the inner width of the content area
- int contentAreaInnerWidth = rootPixelWidth
- - getContentAreaToRootDifference();
- if (contentAreaInnerWidth < MIN_CONTENT_AREA_WIDTH) {
- contentAreaInnerWidth = MIN_CONTENT_AREA_WIDTH;
- int rootWidth = contentAreaInnerWidth
- + getContentAreaToRootDifference();
- DOM.setStyleAttribute(getElement(), "width", rootWidth + "px");
- }
-
- // IE6 needs the actual inner content width on the content element,
- // otherwise it won't wrap the content properly (no scrollbars
- // appear, content flows out of window)
- if (BrowserInfo.get().isIE6()) {
- DOM.setStyleAttribute(contentPanel.getElement(), "width",
- contentAreaInnerWidth + "px");
- }
-
- renderSpace.setWidth(contentAreaInnerWidth);
-
- updateShadowSizeAndPosition();
- }
+ // Override PopupPanel which sets the width to the contents
+ getElement().getStyle().setProperty("width", width);
+ // Update v-has-width in case undefined window is resized
+ setStyleName("v-has-width", width != null && width.length() > 0);
}
@Override
- /**
- * Height is set to the out-most element (v-window).
- *
- * This function should never be called with percentage values (it will
- * throw an exception)
- *
- * @param height A CSS string specifying the new height of the window.
- * An empty string or null clears the height and lets
- * the browser to compute it based on the window contents.
- */
public void setHeight(String height) {
- if (!isAttached()
- || (height == null ? this.height == null : height
- .equals(this.height))) {
- return;
- }
- if (height == null || "".equals(height)) {
- getElement().getStyle().clearHeight();
- contentPanel.getElement().getStyle().clearHeight();
- // Reset to default, the exact value does not actually
- // matter as an undefined-height parent should not have
- // a relative-height child anyway.
- renderSpace.setHeight(MIN_CONTENT_AREA_HEIGHT);
- } else {
- getElement().getStyle().setProperty("height", height);
- int contentHeight = getElement().getOffsetHeight()
- - getExtraHeight();
- if (contentHeight < MIN_CONTENT_AREA_HEIGHT) {
- contentHeight = MIN_CONTENT_AREA_HEIGHT;
- int rootHeight = contentHeight + getExtraHeight();
- getElement().getStyle()
- .setProperty("height", rootHeight + "px");
- }
- renderSpace.setHeight(contentHeight);
- contentPanel.getElement().getStyle()
- .setProperty("height", contentHeight + "px");
- }
- this.height = height;
- updateShadowSizeAndPosition();
- }
-
- private int extraH = 0;
-
- private int getExtraHeight() {
- extraH = header.getOffsetHeight() + footer.getOffsetHeight();
- return extraH;
+ // Override PopupPanel which sets the height to the contents
+ getElement().getStyle().setProperty("height", height);
+ // Update v-has-height in case undefined window is resized
+ setStyleName("v-has-height", height != null && height.length() > 0);
}
private void onDragEvent(Event event) {
@@ -1264,22 +806,34 @@ public class VWindow extends VOverlay implements Container,
} else if (resizing) {
onResizeEvent(event);
return false;
- } else if (vaadinModality) {
- // return false when modal and outside window
- final Element target = event.getEventTarget().cast();
+ }
+
+ // TODO This is probably completely unnecessary as the modality curtain
+ // prevents events from reaching other windows and any security check
+ // must be done on the server side and not here.
+ // The code here is also run many times as each VWindow has an event
+ // preview but we cannot check only the current VWindow here (e.g.
+ // if(isTopMost) {...}) because PopupPanel will cause all events that
+ // are not cancelled here and target this window to be consume():d
+ // meaning the event won't be sent to the rest of the preview handlers.
+
+ if (getTopmostWindow().vaadinModality) {
+ // Topmost window is modal. Cancel the event if it targets something
+ // outside that window (except debug console...)
if (DOM.getCaptureElement() != null) {
// Allow events when capture is set
return true;
}
- if (!DOM.isOrHasChild(getElement(), target)) {
+ final Element target = event.getEventTarget().cast();
+ if (!DOM.isOrHasChild(getTopmostWindow().getElement(), target)) {
// not within the modal window, but let's see if it's in the
// debug window
Widget w = Util.findWidget(target, null);
while (w != null) {
if (w instanceof Console) {
return true; // allow debug-window clicks
- } else if (w instanceof Paintable) {
+ } else if (ConnectorMap.get(client).isConnector(w)) {
return false;
}
w = w.getParent();
@@ -1298,52 +852,6 @@ public class VWindow extends VOverlay implements Container,
true);
}
- @Override
- protected void onAttach() {
- super.onAttach();
-
- setWidth(width);
- setHeight(height);
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- if (child == layout) {
- return renderSpace;
- } else {
- // Exception ??
- return null;
- }
- }
-
- public boolean hasChildComponent(Widget component) {
- if (component == layout) {
- return true;
- } else {
- return false;
- }
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- contentPanel.setWidget(newComponent);
- }
-
- public boolean requestLayout(Set<Paintable> child) {
- if (dynamicWidth && !layoutRelativeWidth) {
- setNaturalWidth();
- }
- if (centered) {
- center();
- }
- updateShadowSizeAndPosition();
- // layout size change may affect its available space (scrollbars)
- client.handleComponentRelativeSize((Widget) layout);
- return true;
- }
-
- public void updateCaption(Paintable component, UIDL uidl) {
- // NOP, window has own caption, layout captio not rendered
- }
-
public ShortcutActionHandler getShortcutActionHandler() {
return shortcutHandler;
}
@@ -1376,13 +884,29 @@ public class VWindow extends VOverlay implements Container,
}
}
- public void onBeforeShortcutAction(Event e) {
- // NOP, nothing to update just avoid workaround ( causes excess
- // blur/focus )
- }
-
public void focus() {
contentPanel.focus();
}
+ public int getMinHeight() {
+ return MIN_CONTENT_AREA_HEIGHT + getDecorationHeight();
+ }
+
+ private int getDecorationHeight() {
+ LayoutManager lm = layout.getLayoutManager();
+ int headerHeight = lm.getOuterHeight(header);
+ int footerHeight = lm.getOuterHeight(footer);
+ return headerHeight + footerHeight;
+ }
+
+ public int getMinWidth() {
+ return MIN_CONTENT_AREA_WIDTH + getDecorationWidth();
+ }
+
+ private int getDecorationWidth() {
+ LayoutManager layoutManager = layout.getLayoutManager();
+ return layoutManager.getOuterWidth(getElement())
+ - contentPanel.getElement().getOffsetWidth();
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java
new file mode 100644
index 0000000000..6979982a9c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java
@@ -0,0 +1,305 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.window;
+
+import com.google.gwt.core.client.GWT;
+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;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
+import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
+import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
+import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren;
+
+@Connect(value = com.vaadin.ui.Window.class)
+public class WindowConnector extends AbstractComponentContainerConnector
+ implements Paintable, BeforeShortcutActionListener,
+ SimpleManagedLayout, PostLayoutListener, MayScrollChildren {
+
+ private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
+ @Override
+ protected void fireClick(NativeEvent event,
+ MouseEventDetails mouseDetails) {
+ rpc.click(mouseDetails);
+ }
+ };
+
+ private WindowServerRpc rpc;
+
+ boolean minWidthChecked = false;
+
+ @Override
+ public boolean delegateCaptionHandling() {
+ return false;
+ };
+
+ @Override
+ protected void init() {
+ super.init();
+ rpc = RpcProxy.create(WindowServerRpc.class, this);
+
+ getLayoutManager().registerDependency(this,
+ getWidget().contentPanel.getElement());
+ getLayoutManager().registerDependency(this, getWidget().header);
+ getLayoutManager().registerDependency(this, getWidget().footer);
+ }
+
+ @Override
+ public void onUnregister() {
+ LayoutManager lm = getLayoutManager();
+ VWindow window = getWidget();
+ lm.unregisterDependency(this, window.contentPanel.getElement());
+ lm.unregisterDependency(this, window.header);
+ lm.unregisterDependency(this, window.footer);
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ getWidget().id = getConnectorId();
+ getWidget().client = client;
+
+ // Workaround needed for Testing Tools (GWT generates window DOM
+ // slightly different in different browsers).
+ DOM.setElementProperty(getWidget().closeBox, "id", getConnectorId()
+ + "_window_close");
+
+ if (isRealUpdate(uidl)) {
+ if (getState().isModal() != getWidget().vaadinModality) {
+ getWidget().setVaadinModality(!getWidget().vaadinModality);
+ }
+ if (!getWidget().isAttached()) {
+ getWidget().setVisible(false); // hide until
+ // possible centering
+ getWidget().show();
+ }
+ if (getState().isResizable() != getWidget().resizable) {
+ getWidget().setResizable(getState().isResizable());
+ }
+ getWidget().resizeLazy = getState().isResizeLazy();
+
+ getWidget().setDraggable(getState().isDraggable());
+
+ // 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 (getState().getIcon() != null) {
+ iconURL = getState().getIcon().getURL();
+ }
+ getWidget().setCaption(getState().getCaption(), iconURL);
+ }
+
+ getWidget().visibilityChangesDisabled = true;
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+ getWidget().visibilityChangesDisabled = false;
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ getWidget().immediate = getState().isImmediate();
+
+ getWidget().setClosable(!isReadOnly());
+
+ // Initialize the position form UIDL
+ int positionx = getState().getPositionX();
+ int positiony = getState().getPositionY();
+ if (positionx >= 0 || positiony >= 0) {
+ if (positionx < 0) {
+ positionx = 0;
+ }
+ if (positiony < 0) {
+ positiony = 0;
+ }
+ getWidget().setPopupPosition(positionx, positiony);
+ }
+
+ int childIndex = 0;
+
+ // we may have actions
+ for (int i = 0; i < uidl.getChildCount(); i++) {
+ UIDL childUidl = uidl.getChildUIDL(i);
+ if (childUidl.getTag().equals("actions")) {
+ if (getWidget().shortcutHandler == null) {
+ getWidget().shortcutHandler = new ShortcutActionHandler(
+ getConnectorId(), client);
+ }
+ getWidget().shortcutHandler.updateActionMap(childUidl);
+ }
+
+ }
+
+ // setting scrollposition must happen after children is rendered
+ getWidget().contentPanel.setScrollPosition(getState().getScrollTop());
+ getWidget().contentPanel.setHorizontalScrollPosition(getState()
+ .getScrollLeft());
+
+ // Center this window on screen if requested
+ // This had to be here because we might not know the content size before
+ // everything is painted into the window
+
+ // centered is this is unset on move/resize
+ getWidget().centered = getState().isCentered();
+ getWidget().setVisible(true);
+
+ // ensure window is not larger than browser window
+ if (getWidget().getOffsetWidth() > Window.getClientWidth()) {
+ getWidget().setWidth(Window.getClientWidth() + "px");
+ }
+ if (getWidget().getOffsetHeight() > Window.getClientHeight()) {
+ getWidget().setHeight(Window.getClientHeight() + "px");
+ }
+
+ if (uidl.hasAttribute("bringToFront")) {
+ /*
+ * Focus as a side-effect. Will be overridden by
+ * ApplicationConnection if another component was focused by the
+ * server side.
+ */
+ getWidget().contentPanel.focus();
+ getWidget().bringToFrontSequence = uidl
+ .getIntAttribute("bringToFront");
+ VWindow.deferOrdering();
+ }
+ }
+
+ public void updateCaption(ComponentConnector component) {
+ // NOP, window has own caption, layout caption not rendered
+ }
+
+ public void onBeforeShortcutAction(Event e) {
+ // NOP, nothing to update just avoid workaround ( causes excess
+ // blur/focus )
+ }
+
+ @Override
+ public VWindow getWidget() {
+ return (VWindow) super.getWidget();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VWindow.class);
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ // We always have 1 child, unless the child is hidden
+ Widget newChildWidget = null;
+ ComponentConnector newChild = null;
+ if (getChildren().size() == 1) {
+ newChild = getChildren().get(0);
+ newChildWidget = newChild.getWidget();
+ }
+
+ getWidget().layout = newChild;
+ getWidget().contentPanel.setWidget(newChildWidget);
+ }
+
+ public void layout() {
+ LayoutManager lm = getLayoutManager();
+ VWindow window = getWidget();
+ ComponentConnector layout = window.layout;
+ Element contentElement = window.contentPanel.getElement();
+
+ if (!minWidthChecked) {
+ boolean needsMinWidth = !isUndefinedWidth()
+ || layout.isRelativeWidth();
+ int minWidth = window.getMinWidth();
+ if (needsMinWidth && lm.getInnerWidth(contentElement) < minWidth) {
+ minWidthChecked = true;
+ // Use minimum width if less than a certain size
+ window.setWidth(minWidth + "px");
+ }
+ minWidthChecked = true;
+ }
+
+ boolean needsMinHeight = !isUndefinedHeight()
+ || layout.isRelativeHeight();
+ int minHeight = window.getMinHeight();
+ if (needsMinHeight && lm.getInnerHeight(contentElement) < minHeight) {
+ // Use minimum height if less than a certain size
+ window.setHeight(minHeight + "px");
+ }
+
+ Style contentStyle = window.contents.getStyle();
+
+ int headerHeight = lm.getOuterHeight(window.header);
+ contentStyle.setPaddingTop(headerHeight, Unit.PX);
+ contentStyle.setMarginTop(-headerHeight, Unit.PX);
+
+ int footerHeight = lm.getOuterHeight(window.footer);
+ contentStyle.setPaddingBottom(footerHeight, Unit.PX);
+ contentStyle.setMarginBottom(-footerHeight, Unit.PX);
+
+ /*
+ * Must set absolute position if the child has relative height and
+ * there's a chance of horizontal scrolling as some browsers will
+ * otherwise not take the scrollbar into account when calculating the
+ * height.
+ */
+ Element layoutElement = layout.getWidget().getElement();
+ Style childStyle = layoutElement.getStyle();
+ if (layout.isRelativeHeight() && !BrowserInfo.get().isIE9()) {
+ childStyle.setPosition(Position.ABSOLUTE);
+
+ Style wrapperStyle = contentElement.getStyle();
+ if (window.getElement().getStyle().getWidth().length() == 0
+ && !layout.isRelativeWidth()) {
+ /*
+ * Need to lock width to make undefined width work even with
+ * absolute positioning
+ */
+ int contentWidth = lm.getOuterWidth(layoutElement);
+ wrapperStyle.setWidth(contentWidth, Unit.PX);
+ } else {
+ wrapperStyle.clearWidth();
+ }
+ } else {
+ childStyle.clearPosition();
+ }
+ }
+
+ public void postLayout() {
+ minWidthChecked = false;
+ VWindow window = getWidget();
+ if (window.centered) {
+ window.center();
+ }
+ window.updateShadowSizeAndPosition();
+ }
+
+ @Override
+ public WindowState getState() {
+ return (WindowState) super.getState();
+ }
+
+ /**
+ * Gives the WindowConnector an order number. As a side effect, moves the
+ * window according to its order number so the windows are stacked. This
+ * method should be called for each window in the order they should appear.
+ */
+ public void setWindowOrderAndPosition() {
+ getWidget().setWindowOrderAndPosition();
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java
new file mode 100644
index 0000000000..4723c55786
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java
@@ -0,0 +1,10 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.window;
+
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.ui.ClickRpc;
+
+public interface WindowServerRpc extends ClickRpc, ServerRpc {
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowState.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowState.java
new file mode 100644
index 0000000000..b057d76b16
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowState.java
@@ -0,0 +1,73 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui.window;
+
+import com.vaadin.terminal.gwt.client.ui.panel.PanelState;
+
+public class WindowState extends PanelState {
+ private boolean modal = false;
+ private boolean resizable = true;
+ private boolean resizeLazy = false;
+ private boolean draggable = true;
+ private boolean centered = false;;
+ private int positionX = -1;
+ private int positionY = -1;
+
+ public boolean isModal() {
+ return modal;
+ }
+
+ public void setModal(boolean modal) {
+ this.modal = modal;
+ }
+
+ public boolean isResizable() {
+ return resizable;
+ }
+
+ public void setResizable(boolean resizable) {
+ this.resizable = resizable;
+ }
+
+ public boolean isResizeLazy() {
+ return resizeLazy;
+ }
+
+ public void setResizeLazy(boolean resizeLazy) {
+ this.resizeLazy = resizeLazy;
+ }
+
+ public boolean isDraggable() {
+ return draggable;
+ }
+
+ public void setDraggable(boolean draggable) {
+ this.draggable = draggable;
+ }
+
+ public boolean isCentered() {
+ return centered;
+ }
+
+ public void setCentered(boolean centered) {
+ this.centered = centered;
+ }
+
+ public int getPositionX() {
+ return positionX;
+ }
+
+ public void setPositionX(int positionX) {
+ this.positionX = positionX;
+ }
+
+ public int getPositionY() {
+ return positionY;
+ }
+
+ public void setPositionY(int positionY) {
+ this.positionY = positionY;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
index a5923cb47f..58d6a18592 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
@@ -14,14 +14,10 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.security.GeneralSecurityException;
-import java.util.Date;
import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
-import java.util.logging.Level;
import java.util.logging.Logger;
import javax.portlet.ActionRequest;
@@ -30,19 +26,16 @@ import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.MimeResponse;
-import javax.portlet.PortalContext;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.PortletSession;
-import javax.portlet.PortletURL;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
-import javax.portlet.ResourceURL;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
@@ -50,12 +43,15 @@ import javax.servlet.http.HttpServletResponse;
import com.liferay.portal.kernel.util.PortalClassInvoker;
import com.liferay.portal.kernel.util.PropsUtil;
import com.vaadin.Application;
+import com.vaadin.Application.ApplicationStartEvent;
import com.vaadin.Application.SystemMessages;
-import com.vaadin.terminal.DownloadStream;
+import com.vaadin.RootRequiresMoreInformationException;
+import com.vaadin.terminal.DeploymentConfiguration;
import com.vaadin.terminal.Terminal;
-import com.vaadin.terminal.gwt.client.ApplicationConfiguration;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.ui.Window;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+import com.vaadin.terminal.gwt.server.AbstractCommunicationManager.Callback;
+import com.vaadin.ui.Root;
/**
* Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0
@@ -71,46 +67,256 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
private static final Logger logger = Logger
.getLogger(AbstractApplicationPortlet.class.getName());
+ private static class WrappedHttpAndPortletRequest extends
+ WrappedPortletRequest {
+
+ public WrappedHttpAndPortletRequest(PortletRequest request,
+ HttpServletRequest originalRequest,
+ DeploymentConfiguration deploymentConfiguration) {
+ super(request, deploymentConfiguration);
+ this.originalRequest = originalRequest;
+ }
+
+ private final HttpServletRequest originalRequest;
+
+ @Override
+ public String getParameter(String name) {
+ String parameter = super.getParameter(name);
+ if (parameter == null) {
+ parameter = originalRequest.getParameter(name);
+ }
+ return parameter;
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return originalRequest.getRemoteAddr();
+ }
+
+ @Override
+ public String getHeader(String name) {
+ String header = super.getHeader(name);
+ if (header == null) {
+ header = originalRequest.getHeader(name);
+ }
+ return header;
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ Map<String, String[]> parameterMap = super.getParameterMap();
+ if (parameterMap == null) {
+ parameterMap = originalRequest.getParameterMap();
+ }
+ return parameterMap;
+ }
+ }
+
+ private static class WrappedGateinRequest extends
+ WrappedHttpAndPortletRequest {
+ public WrappedGateinRequest(PortletRequest request,
+ DeploymentConfiguration deploymentConfiguration) {
+ super(request, getOriginalRequest(request), deploymentConfiguration);
+ }
+
+ private static final HttpServletRequest getOriginalRequest(
+ PortletRequest request) {
+ try {
+ Method getRealReq = request.getClass().getMethod(
+ "getRealRequest");
+ HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
+ .invoke(request);
+ return origRequest;
+ } catch (Exception e) {
+ throw new IllegalStateException("GateIn request not detected",
+ e);
+ }
+ }
+ }
+
+ private static class WrappedLiferayRequest extends
+ WrappedHttpAndPortletRequest {
+
+ public WrappedLiferayRequest(PortletRequest request,
+ DeploymentConfiguration deploymentConfiguration) {
+ super(request, getOriginalRequest(request), deploymentConfiguration);
+ }
+
+ @Override
+ public String getPortalProperty(String name) {
+ return PropsUtil.get(name);
+ }
+
+ private static HttpServletRequest getOriginalRequest(
+ PortletRequest request) {
+ try {
+ // httpRequest = PortalUtil.getHttpServletRequest(request);
+ HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
+ .invoke("com.liferay.portal.util.PortalUtil",
+ "getHttpServletRequest", request);
+
+ // httpRequest =
+ // PortalUtil.getOriginalServletRequest(httpRequest);
+ httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
+ "com.liferay.portal.util.PortalUtil",
+ "getOriginalServletRequest", httpRequest);
+ return httpRequest;
+ } catch (Exception e) {
+ throw new IllegalStateException("Liferay request not detected",
+ e);
+ }
+ }
+
+ }
+
+ private static class AbstractApplicationPortletWrapper implements Callback {
+
+ private final AbstractApplicationPortlet portlet;
+
+ public AbstractApplicationPortletWrapper(
+ AbstractApplicationPortlet portlet) {
+ this.portlet = portlet;
+ }
+
+ public void criticalNotification(WrappedRequest request,
+ WrappedResponse response, String cap, String msg,
+ String details, String outOfSyncURL) throws IOException {
+ PortletRequest portletRequest = WrappedPortletRequest.cast(request)
+ .getPortletRequest();
+ PortletResponse portletResponse = ((WrappedPortletResponse) response)
+ .getPortletResponse();
+ portlet.criticalNotification(portletRequest,
+ (MimeResponse) portletResponse, cap, msg, details,
+ outOfSyncURL);
+ }
+ }
+
/**
* This portlet parameter is used to add styles to the main element. E.g
* "height:500px" generates a style="height:500px" to the main element.
*/
public static final String PORTLET_PARAMETER_STYLE = "style";
- private static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme";
+ /**
+ * This portal parameter is used to define the name of the Vaadin theme that
+ * is used for all Vaadin applications in the portal.
+ */
+ public static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme";
// TODO some parts could be shared with AbstractApplicationServlet
// TODO Can we close the application when the portlet is removed? Do we know
// when the portlet is removed?
- // TODO What happens when the portlet window is resized? Do we know when the
- // window is resized?
-
private Properties applicationProperties;
private boolean productionMode = false;
+ private DeploymentConfiguration deploymentConfiguration = new DeploymentConfiguration() {
+ public String getConfiguredWidgetset(WrappedRequest request) {
+
+ String widgetset = getApplicationOrSystemProperty(
+ PARAMETER_WIDGETSET, null);
+
+ if (widgetset == null) {
+ // If no widgetset defined for the application, check the portal
+ // property
+ widgetset = WrappedPortletRequest.cast(request)
+ .getPortalProperty(PORTAL_PARAMETER_VAADIN_WIDGETSET);
+ }
+
+ if (widgetset == null) {
+ // If no widgetset defined for the portal, use the default
+ widgetset = DEFAULT_WIDGETSET;
+ }
+
+ return widgetset;
+ }
+
+ public String getConfiguredTheme(WrappedRequest request) {
+
+ // is the default theme defined by the portal?
+ String themeName = WrappedPortletRequest.cast(request)
+ .getPortalProperty(Constants.PORTAL_PARAMETER_VAADIN_THEME);
+
+ if (themeName == null) {
+ // no, using the default theme defined by Vaadin
+ themeName = DEFAULT_THEME_NAME;
+ }
+
+ return themeName;
+ }
+
+ public String getApplicationOrSystemProperty(String propertyName,
+ String defaultValue) {
+ return AbstractApplicationPortlet.this
+ .getApplicationOrSystemProperty(propertyName, defaultValue);
+ }
+
+ public boolean isStandalone(WrappedRequest request) {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.terminal.DeploymentConfiguration#getStaticFileLocation
+ * (com.vaadin.terminal.WrappedRequest)
+ *
+ * Return the URL from where static files, e.g. the widgetset and the
+ * theme, are served. In a standard configuration the VAADIN folder
+ * inside the returned folder is what is used for widgetsets and themes.
+ *
+ * @return The location of static resources (inside which there should
+ * be a VAADIN directory). Does not end with a slash (/).
+ */
+ public String getStaticFileLocation(WrappedRequest request) {
+ String staticFileLocation = WrappedPortletRequest.cast(request)
+ .getPortalProperty(
+ Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH);
+ if (staticFileLocation != null) {
+ // remove trailing slash if any
+ while (staticFileLocation.endsWith(".")) {
+ staticFileLocation = staticFileLocation.substring(0,
+ staticFileLocation.length() - 1);
+ }
+ return staticFileLocation;
+ } else {
+ // default for Liferay
+ return "/html";
+ }
+ }
+
+ public ClassLoader getClassLoader() {
+ // Custom class loaders not currently supported in portlets (see
+ // #8574)
+ return null;
+ }
+ };
+
@Override
public void init(PortletConfig config) throws PortletException {
super.init(config);
- // Stores the application parameters into Properties object
applicationProperties = new Properties();
- for (final Enumeration<String> e = config.getInitParameterNames(); e
+
+ // Read default parameters from the context
+ final PortletContext context = config.getPortletContext();
+ for (final Enumeration<String> e = context.getInitParameterNames(); e
.hasMoreElements();) {
final String name = e.nextElement();
applicationProperties.setProperty(name,
- config.getInitParameter(name));
+ context.getInitParameter(name));
}
- // Overrides with server.xml parameters
- final PortletContext context = config.getPortletContext();
- for (final Enumeration<String> e = context.getInitParameterNames(); e
+ // Override with application settings from portlet.xml
+ for (final Enumeration<String> e = config.getInitParameterNames(); e
.hasMoreElements();) {
final String name = e.nextElement();
applicationProperties.setProperty(name,
- context.getInitParameter(name));
+ config.getInitParameter(name));
}
+
checkProductionMode();
checkCrossSiteProtection();
}
@@ -133,25 +339,21 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
*
* @param request
*/
- private void checkWidgetsetVersion(PortletRequest request) {
- if (!AbstractApplicationServlet.VERSION.equals(getHTTPRequestParameter(
- request, "wsver"))) {
+ private void checkWidgetsetVersion(WrappedRequest request) {
+ if (!AbstractApplicationServlet.VERSION.equals(request
+ .getParameter("wsver"))) {
logger.warning(String.format(WIDGETSET_MISMATCH_INFO,
AbstractApplicationServlet.VERSION,
- getHTTPRequestParameter(request, "wsver")));
+ request.getParameter("wsver")));
}
}
private void checkProductionMode() {
+ // TODO Identical code in AbstractApplicationServlet -> refactor
// Check if the application is in production mode.
- // We are in production mode if Debug=false or productionMode=true
- if (getApplicationOrSystemProperty(SERVLET_PARAMETER_DEBUG, "true")
- .equals("false")) {
- // "Debug=true" is the old way and should no longer be used
- productionMode = true;
- } else if (getApplicationOrSystemProperty(
- SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) {
- // "productionMode=true" is the real way to do it
+ // We are in production mode if productionMode=true
+ if (getApplicationOrSystemProperty(SERVLET_PARAMETER_PRODUCTION_MODE,
+ "false").equals("true")) {
productionMode = true;
}
@@ -241,48 +443,24 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
return defaultValue;
}
- /**
- * Return the URL from where static files, e.g. the widgetset and the theme,
- * are served. In a standard configuration the VAADIN folder inside the
- * returned folder is what is used for widgetsets and themes.
- *
- * @param request
- * @return The location of static resources (inside which there should be a
- * VAADIN directory). Does not end with a slash (/).
- */
- protected String getStaticFilesLocation(PortletRequest request) {
- // TODO allow overriding on portlet level?
- String staticFileLocation = getPortalProperty(
- Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH,
- request.getPortalContext());
- if (staticFileLocation != null) {
- // remove trailing slash if any
- while (staticFileLocation.endsWith(".")) {
- staticFileLocation = staticFileLocation.substring(0,
- staticFileLocation.length() - 1);
- }
- return staticFileLocation;
- } else {
- // default for Liferay
- return "/html";
- }
- }
-
protected enum RequestType {
- FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN;
+ FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS;
}
protected RequestType getRequestType(PortletRequest request) {
if (request instanceof RenderRequest) {
return RequestType.RENDER;
} else if (request instanceof ResourceRequest) {
- if (isUIDLRequest((ResourceRequest) request)) {
+ ResourceRequest resourceRequest = (ResourceRequest) request;
+ if (isUIDLRequest(resourceRequest)) {
return RequestType.UIDL;
- } else if (isFileUploadRequest((ResourceRequest) request)) {
+ } else if (isBrowserDetailsRequeset(resourceRequest)) {
+ return RequestType.BROWSER_DETAILS;
+ } else if (isFileUploadRequest(resourceRequest)) {
return RequestType.FILE_UPLOAD;
- } else if (isApplicationResourceRequest((ResourceRequest) request)) {
+ } else if (isApplicationResourceRequest(resourceRequest)) {
return RequestType.APPLICATION_RESOURCE;
- } else if (isDummyRequest((ResourceRequest) request)) {
+ } else if (isDummyRequest(resourceRequest)) {
return RequestType.DUMMY;
} else {
return RequestType.STATIC_FILE;
@@ -295,6 +473,11 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
return RequestType.UNKNOWN;
}
+ private boolean isBrowserDetailsRequeset(ResourceRequest request) {
+ return request.getResourceID() != null
+ && request.getResourceID().equals("browserDetails");
+ }
+
private boolean isApplicationResourceRequest(ResourceRequest request) {
return request.getResourceID() != null
&& request.getResourceID().startsWith("APP");
@@ -326,8 +509,27 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
protected void handleRequest(PortletRequest request,
PortletResponse response) throws PortletException, IOException {
- RequestTimer.RequestWrapper wrappedRequest = new RequestTimer.RequestWrapper(
- request);
+ AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper(
+ this);
+
+ WrappedPortletRequest wrappedRequest;
+
+ String portalInfo = request.getPortalContext().getPortalInfo()
+ .toLowerCase();
+ if (portalInfo.contains("liferay")) {
+ wrappedRequest = new WrappedLiferayRequest(request,
+ getDeploymentConfiguration());
+ } else if (portalInfo.contains("gatein")) {
+ wrappedRequest = new WrappedGateinRequest(request,
+ getDeploymentConfiguration());
+ } else {
+ wrappedRequest = new WrappedPortletRequest(request,
+ getDeploymentConfiguration());
+ }
+
+ WrappedPortletResponse wrappedResponse = new WrappedPortletResponse(
+ response, getDeploymentConfiguration());
+
RequestTimer requestTimer = RequestTimer.get(wrappedRequest);
requestTimer.start(wrappedRequest);
@@ -360,10 +562,12 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
// TODO What about PARAM_UNLOADBURST & redirectToApplication??
/* Find out which application this request is related to */
- application = findApplicationInstance(request, requestType);
+ application = findApplicationInstance(wrappedRequest,
+ requestType);
if (application == null) {
return;
}
+ Application.setCurrentApplication(application);
/*
* Get or create an application context and an application
@@ -378,8 +582,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
.getApplicationManager(application);
/* Update browser information from request */
- updateBrowserProperties(applicationContext.getBrowser(),
- request);
+ applicationContext.getBrowser().updateRequestDetails(
+ wrappedRequest);
/*
* Call application requestStart before Application.init() is
@@ -404,20 +608,36 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
/* Notify listeners */
// Finds the window within the application
- Window window = null;
+ Root root = null;
synchronized (application) {
if (application.isRunning()) {
switch (requestType) {
+ case RENDER:
+ case ACTION:
+ // Both action requests and render requests are ok
+ // without a Root as they render the initial HTML
+ // and then do a second request
+ try {
+ root = application
+ .getRootForRequest(wrappedRequest);
+ } catch (RootRequiresMoreInformationException e) {
+ // Ignore problem and continue without root
+ }
+ break;
+ case BROWSER_DETAILS:
+ // Should not try to find a root here as the
+ // combined request details might change the root
+ break;
case FILE_UPLOAD:
// no window
break;
case APPLICATION_RESOURCE:
// use main window - should not need any window
- window = application.getMainWindow();
+ // root = application.getRoot();
break;
default:
- window = applicationManager.getApplicationWindow(
- request, this, application, null);
+ root = application
+ .getRootForRequest(wrappedRequest);
}
// if window not found, not a problem - use null
}
@@ -427,37 +647,39 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
// starts?
if (request instanceof RenderRequest) {
applicationContext.firePortletRenderRequest(application,
- window, (RenderRequest) request,
+ root, (RenderRequest) request,
(RenderResponse) response);
} else if (request instanceof ActionRequest) {
applicationContext.firePortletActionRequest(application,
- window, (ActionRequest) request,
+ root, (ActionRequest) request,
(ActionResponse) response);
} else if (request instanceof EventRequest) {
applicationContext.firePortletEventRequest(application,
- window, (EventRequest) request,
+ root, (EventRequest) request,
(EventResponse) response);
} else if (request instanceof ResourceRequest) {
applicationContext.firePortletResourceRequest(application,
- window, (ResourceRequest) request,
+ root, (ResourceRequest) request,
(ResourceResponse) response);
}
/* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) {
- applicationManager.handleFileUpload(
- (ResourceRequest) request,
- (ResourceResponse) response);
+ applicationManager.handleFileUpload(wrappedRequest,
+ wrappedResponse);
+ return;
+ } else if (requestType == RequestType.BROWSER_DETAILS) {
+ applicationManager.handleBrowserDetailsRequest(
+ wrappedRequest, wrappedResponse, application);
return;
} else if (requestType == RequestType.UIDL) {
// Handles AJAX UIDL requests
if (isRepaintAll(request)) {
// warn if versions do not match
- checkWidgetsetVersion(request);
+ checkWidgetsetVersion(wrappedRequest);
}
- applicationManager.handleUidlRequest(
- (ResourceRequest) request,
- (ResourceResponse) response, this, window);
+ applicationManager.handleUidlRequest(wrappedRequest,
+ wrappedResponse, portletWrapper, root);
return;
} else {
/*
@@ -468,8 +690,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
return;
}
- handleOtherRequest(request, response, requestType,
- application, window, applicationContext,
+ handleOtherRequest(wrappedRequest, wrappedResponse,
+ requestType, application, applicationContext,
applicationManager);
}
} catch (final SessionExpiredException e) {
@@ -490,10 +712,15 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
.endTransaction(application, request);
}
} finally {
- if (requestStarted) {
- ((PortletRequestListener) application).onRequestEnd(
- request, response);
+ try {
+ if (requestStarted) {
+ ((PortletRequestListener) application)
+ .onRequestEnd(request, response);
+ }
+ } finally {
+ Root.setCurrentRoot(null);
+ Application.setCurrentApplication(null);
}
requestTimer.stop();
@@ -503,6 +730,10 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
}
+ private DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+
private void handleUnknownRequest(PortletRequest request,
PortletResponse response) {
logger.warning("Unknown request type");
@@ -525,37 +756,18 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
* @throws IOException
* @throws MalformedURLException
*/
- private void handleOtherRequest(PortletRequest request,
- PortletResponse response, RequestType requestType,
- Application application, Window window,
+ private void handleOtherRequest(WrappedPortletRequest request,
+ WrappedResponse response, RequestType requestType,
+ Application application,
PortletApplicationContext2 applicationContext,
PortletCommunicationManager applicationManager)
throws PortletException, IOException, MalformedURLException {
- if (window == null) {
- throw new PortletException(ERROR_NO_WINDOW_FOUND);
- }
-
- /*
- * Sets terminal type for the window, if not already set
- */
- if (window.getTerminal() == null) {
- window.setTerminal(applicationContext.getBrowser());
- }
-
- /*
- * Handle parameters
- */
- final Map<String, String[]> parameters = request.getParameterMap();
- if (window != null && parameters != null) {
- window.handleParameters(parameters);
- }
-
- if (requestType == RequestType.APPLICATION_RESOURCE) {
- handleURI(applicationManager, window, (ResourceRequest) request,
- (ResourceResponse) response);
- } else if (requestType == RequestType.RENDER) {
- writeAjaxPage((RenderRequest) request, (RenderResponse) response,
- window, application);
+ if (requestType == RequestType.APPLICATION_RESOURCE
+ || requestType == RequestType.RENDER) {
+ if (!applicationManager.handleApplicationRequest(request, response)) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ "Not found");
+ }
} else if (requestType == RequestType.EVENT) {
// nothing to do, listeners do all the work
} else if (requestType == RequestType.ACTION) {
@@ -566,126 +778,12 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
}
- private void updateBrowserProperties(WebBrowser browser,
- PortletRequest request) {
- String userAgent = getHTTPHeader(request, "user-agent");
- browser.updateRequestDetails(request.getLocale(), null,
- request.isSecure(), userAgent);
- if (getHTTPRequestParameter(request, "repaintAll") != null) {
- browser.updateClientSideDetails(
- getHTTPRequestParameter(request, "sw"),
- getHTTPRequestParameter(request, "sh"),
- getHTTPRequestParameter(request, "cw"),
- getHTTPRequestParameter(request, "ch"),
- getHTTPRequestParameter(request, "tzo"),
- getHTTPRequestParameter(request, "rtzo"),
- getHTTPRequestParameter(request, "dstd"),
- getHTTPRequestParameter(request, "dstActive"),
- getHTTPRequestParameter(request, "curdate"),
- getHTTPRequestParameter(request, "td") != null);
- }
- }
-
@Override
public void processEvent(EventRequest request, EventResponse response)
throws PortletException, IOException {
handleRequest(request, response);
}
- private boolean handleURI(PortletCommunicationManager applicationManager,
- Window window, ResourceRequest request, ResourceResponse response)
- throws IOException {
- // Handles the URI
- DownloadStream download = applicationManager.handleURI(window, request,
- response, this);
-
- // A download request
- if (download != null) {
- // Client downloads an resource
- handleDownload(download, request, response);
- return true;
- }
-
- return false;
- }
-
- private void handleDownload(DownloadStream stream, ResourceRequest request,
- ResourceResponse response) throws IOException {
-
- if (stream.getParameter("Location") != null) {
- response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
- Integer.toString(HttpServletResponse.SC_MOVED_TEMPORARILY));
- response.setProperty("Location", stream.getParameter("Location"));
- return;
- }
-
- // Download from given stream
- final InputStream data = stream.getStream();
- if (data != null) {
-
- OutputStream out = null;
- try {
-
- // Sets content type
- response.setContentType(stream.getContentType());
-
- // Sets cache headers
- final long cacheTime = stream.getCacheTime();
- if (cacheTime <= 0) {
- response.setProperty("Cache-Control", "no-cache");
- response.setProperty("Pragma", "no-cache");
- response.setProperty("Expires", "0");
- } else {
- response.setProperty("Cache-Control", "max-age="
- + cacheTime / 1000);
- response.setProperty("Expires",
- "" + System.currentTimeMillis() + cacheTime);
- // Required to apply caching in some Tomcats
- response.setProperty("Pragma", "cache");
- }
-
- // Copy download stream parameters directly
- // to HTTP headers.
- final Iterator<String> i = stream.getParameterNames();
- if (i != null) {
- while (i.hasNext()) {
- final String param = i.next();
- response.setProperty(param, stream.getParameter(param));
- }
- }
-
- // suggest local filename from DownloadStream if
- // Content-Disposition
- // not explicitly set
- String contentDispositionValue = stream
- .getParameter("Content-Disposition");
- if (contentDispositionValue == null) {
- contentDispositionValue = "filename=\""
- + stream.getFileName() + "\"";
- response.setProperty("Content-Disposition",
- contentDispositionValue);
- }
-
- int bufferSize = stream.getBufferSize();
- if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
- bufferSize = DEFAULT_BUFFER_SIZE;
- }
- final byte[] buffer = new byte[bufferSize];
- int bytesRead = 0;
-
- out = response.getPortletOutputStream();
-
- while ((bytesRead = data.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
- out.flush();
- }
- } finally {
- AbstractCommunicationManager.tryToCloseStream(data);
- AbstractCommunicationManager.tryToCloseStream(out);
- }
- }
- }
-
private void serveStaticResources(ResourceRequest request,
ResourceResponse response) throws IOException, PortletException {
final String resourceID = request.getResourceID();
@@ -774,7 +872,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
Locale locale = request.getLocale();
application.setLocale(locale);
// No application URL when running inside a portlet
- application.start(null, applicationProperties, context);
+ application.start(new ApplicationStartEvent(null,
+ applicationProperties, context, isProductionMode()));
}
}
@@ -788,9 +887,11 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
// Do not send any redirects when running inside a portlet.
}
- private Application findApplicationInstance(PortletRequest request,
- RequestType requestType) throws PortletException,
- SessionExpiredException, MalformedURLException {
+ private Application findApplicationInstance(
+ WrappedPortletRequest wrappedRequest, RequestType requestType)
+ throws PortletException, SessionExpiredException,
+ MalformedURLException {
+ PortletRequest request = wrappedRequest.getPortletRequest();
boolean requestCanCreateApplication = requestCanCreateApplication(
request, requestType);
@@ -805,10 +906,10 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
* user not specifically requested to close or restart it.
*/
- final boolean restartApplication = (getHTTPRequestParameter(
- request, URL_PARAMETER_RESTART_APPLICATION) != null);
- final boolean closeApplication = (getHTTPRequestParameter(request,
- URL_PARAMETER_CLOSE_APPLICATION) != null);
+ final boolean restartApplication = (wrappedRequest
+ .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null);
+ final boolean closeApplication = (wrappedRequest
+ .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null);
if (restartApplication) {
closeApplication(application, request.getPortletSession(false));
@@ -878,436 +979,6 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
return null;
}
- /**
- * Returns the URL from which the widgetset is served on the portal.
- *
- * @param widgetset
- * @param request
- * @return
- */
- protected String getWidgetsetURL(String widgetset, PortletRequest request) {
- return getStaticFilesLocation(request) + "/" + WIDGETSET_DIRECTORY_PATH
- + widgetset + "/" + widgetset + ".nocache.js?"
- + new Date().getTime();
- }
-
- /**
- * Returns the theme URI for the named theme on the portal.
- *
- * Note that this is not the only location referring to the theme URI - also
- * e.g. PortletCommunicationManager uses its own way to access the portlet
- * 2.0 theme resources.
- *
- * @param themeName
- * @param request
- * @return
- */
- protected String getThemeURI(String themeName, PortletRequest request) {
- return getStaticFilesLocation(request) + "/" + THEME_DIRECTORY_PATH
- + themeName;
- }
-
- /**
- * Writes the html host page (aka kickstart page) that starts the actual
- * Vaadin application.
- *
- * If one needs to override parts of the portlet HTML contents creation, it
- * is suggested that one overrides one of several submethods including:
- * <ul>
- * <li>
- * {@link #writeAjaxPageHtmlMainDiv(RenderRequest, RenderResponse, BufferedWriter, String)}
- * <li>
- * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)}
- * <li>
- * {@link #writeAjaxPageHtmlVaadinScripts(RenderRequest, RenderResponse, BufferedWriter, Application, String)}
- * </ul>
- *
- * @param request
- * the portlet request.
- * @param response
- * the portlet response to write to.
- * @param window
- * @param application
- * @throws IOException
- * if the writing failed due to input/output error.
- * @throws MalformedURLException
- * if the application is denied access the persistent data store
- * represented by the given URL.
- * @throws PortletException
- */
- protected void writeAjaxPage(RenderRequest request,
- RenderResponse response, Window window, Application application)
- throws IOException, MalformedURLException, PortletException {
-
- response.setContentType("text/html");
- final BufferedWriter page = new BufferedWriter(new OutputStreamWriter(
- response.getPortletOutputStream(), "UTF-8"));
-
- // TODO Currently, we can only load widgetsets and themes from the
- // portal
-
- String themeName = getThemeForWindow(request, window);
-
- writeAjaxPageHtmlVaadinScripts(request, response, page, application,
- themeName);
-
- /*- Add classnames;
- * .v-app
- * .v-app-loading
- * .v-app-<simpleName for app class>
- * .v-theme-<themeName, remove non-alphanum>
- */
- String appClass = "v-app-";
- try {
- appClass += getApplicationClass().getSimpleName();
- } catch (ClassNotFoundException e) {
- appClass += "unknown";
- logger.log(Level.SEVERE, "Could not find application class", e);
- }
- String themeClass = "v-theme-"
- + themeName.replaceAll("[^a-zA-Z0-9]", "");
-
- String classNames = "v-app " + themeClass + " " + appClass;
-
- String style = getApplicationProperty(PORTLET_PARAMETER_STYLE);
- String divStyle = "";
- if (style != null) {
- divStyle = "style=\"" + style + "\"";
- }
-
- writeAjaxPageHtmlMainDiv(request, response, page,
- getApplicationDomId(request), classNames, divStyle);
-
- page.close();
- }
-
- /**
- * Creates and returns a unique ID for the DIV where the application is to
- * be rendered. We need to generate a unique ID because some portals already
- * create a DIV with the portlet's Window ID as the DOM ID.
- *
- * @param request
- * PortletRequest
- * @return the id to use in the DOM
- */
- private String getApplicationDomId(PortletRequest request) {
- return "v-" + request.getWindowID();
- }
-
- /**
- * This method writes the scripts to load the widgetset and the themes as
- * well as define Vaadin configuration parameters on the HTML fragment that
- * starts the actual Vaadin application.
- *
- * @param request
- * @param response
- * @param writer
- * @param application
- * @param themeName
- * @throws IOException
- * @throws PortletException
- */
- protected void writeAjaxPageHtmlVaadinScripts(RenderRequest request,
- RenderResponse response, final BufferedWriter writer,
- Application application, String themeName) throws IOException,
- PortletException {
- String themeURI = getThemeURI(themeName, request);
-
- // fixed base theme to use - all portal pages with Vaadin
- // applications will load this exactly once
- String portalTheme = getPortalProperty(PORTAL_PARAMETER_VAADIN_THEME,
- request.getPortalContext());
-
- writer.write("<script type=\"text/javascript\">\n");
- writer.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n "
- + "if(!vaadin) { var vaadin = {}} \n"
- + "vaadin.vaadinConfigurations = {};\n"
- + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n");
- if (!isProductionMode()) {
- writer.write("vaadin.debug = true;\n");
- }
-
- writeAjaxPageScriptWidgetset(request, response, writer);
-
- Map<String, String> config = getVaadinConfigurationMap(request,
- response, application, themeURI);
- writeAjaxPageScriptConfigurations(request, response, writer, config);
-
- writer.write("</script>\n");
-
- writeAjaxPageHtmlTheme(request, writer, themeName, themeURI,
- portalTheme);
-
- // TODO Warn if widgetset has not been loaded after 15 seconds
- }
-
- /**
- * Writes the script to load the widgetset on the HTML fragment created by
- * the portlet.
- *
- * @param request
- * @param response
- * @param writer
- * @throws IOException
- */
- protected void writeAjaxPageScriptWidgetset(RenderRequest request,
- RenderResponse response, final BufferedWriter writer)
- throws IOException {
- String requestWidgetset = getApplicationOrSystemProperty(
- PARAMETER_WIDGETSET, null);
- String sharedWidgetset = getPortalProperty(
- PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext());
-
- String widgetset;
- if (requestWidgetset != null) {
- widgetset = requestWidgetset;
- } else if (sharedWidgetset != null) {
- widgetset = sharedWidgetset;
- } else {
- widgetset = DEFAULT_WIDGETSET;
- }
- String widgetsetURL = getWidgetsetURL(widgetset, request);
- writer.write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
- + "style=\"position:absolute;width:0;height:0;border:0;overflow:"
- + "hidden;opacity:0;top:-100px;left:-100px;\" src=\"javascript:false\"></iframe>');\n");
- writer.write("document.write(\"<script language='javascript' src='"
- + widgetsetURL + "'><\\/script>\");\n}\n");
- }
-
- /**
- * Returns the configuration parameters to pass to the client.
- *
- * To add configuration parameters for the client, override, call the super
- * method and then modify the map. Overriding this method may also require
- * client side changes in {@link ApplicationConnection} and
- * {@link ApplicationConfiguration}.
- *
- * Note that this method must escape and quote the values when appropriate.
- *
- * The map returned is typically a {@link LinkedHashMap} to preserve
- * insertion order, but it is not guaranteed to be one.
- *
- * @param request
- * @param response
- * @param application
- * @param themeURI
- * @return modifiable Map from parameter name to its full value
- * @throws PortletException
- */
- protected Map<String, String> getVaadinConfigurationMap(
- RenderRequest request, RenderResponse response,
- Application application, String themeURI) throws PortletException {
- Map<String, String> config = new LinkedHashMap<String, String>();
-
- /*
- * We need this in order to get uploads to work. TODO this is not needed
- * for uploads anymore, check if this is needed for some other things
- */
- PortletURL appUri = response.createActionURL();
- config.put("appUri", "'" + appUri.toString() + "'");
- config.put("usePortletURLs", "true");
- ResourceURL uidlUrlBase = response.createResourceURL();
- uidlUrlBase.setResourceID("UIDL");
- config.put("portletUidlURLBase", "'" + uidlUrlBase.toString() + "'");
- config.put("pathInfo", "''");
- config.put("themeUri", "'" + themeURI + "'");
-
- String versionInfo = "{vaadinVersion:\""
- + AbstractApplicationServlet.VERSION
- + "\",applicationVersion:\"" + application.getVersion() + "\"}";
- config.put("versionInfo", versionInfo);
-
- // Get system messages
- Application.SystemMessages systemMessages = null;
- try {
- systemMessages = getSystemMessages();
- } catch (SystemMessageException e) {
- // failing to get the system messages is always a problem
- throw new PortletException("Failed to obtain system messages!", e);
- }
- if (systemMessages != null) {
- // Write the CommunicationError -message to client
- String caption = systemMessages.getCommunicationErrorCaption();
- if (caption != null) {
- caption = "\"" + caption + "\"";
- }
- String message = systemMessages.getCommunicationErrorMessage();
- if (message != null) {
- message = "\"" + message + "\"";
- }
- String url = systemMessages.getCommunicationErrorURL();
- if (url != null) {
- url = "\"" + url + "\"";
- }
-
- config.put("\"comErrMsg\"", "{" + "\"caption\":" + caption + ","
- + "\"message\" : " + message + "," + "\"url\" : " + url
- + "}");
-
- // Write the AuthenticationError -message to client
- caption = systemMessages.getAuthenticationErrorCaption();
- if (caption != null) {
- caption = "\"" + caption + "\"";
- }
- message = systemMessages.getAuthenticationErrorMessage();
- if (message != null) {
- message = "\"" + message + "\"";
- }
- url = systemMessages.getAuthenticationErrorURL();
- if (url != null) {
- url = "\"" + url + "\"";
- }
-
- config.put("\"authErrMsg\"", "{" + "\"caption\":" + caption + ","
- + "\"message\" : " + message + "," + "\"url\" : " + url
- + "}");
- }
-
- return config;
- }
-
- /**
- * Constructs the Vaadin configuration section for
- * {@link ApplicationConnection} and {@link ApplicationConfiguration}.
- *
- * Typically this method should not be overridden. Instead, modify
- * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)}
- * .
- *
- * @param request
- * @param response
- * @param writer
- * @param config
- * @throws IOException
- * @throws PortletException
- */
- protected void writeAjaxPageScriptConfigurations(RenderRequest request,
- RenderResponse response, final BufferedWriter writer,
- Map<String, String> config) throws IOException, PortletException {
-
- writer.write("vaadin.vaadinConfigurations[\""
- + getApplicationDomId(request) + "\"] = {");
-
- Iterator<String> keyIt = config.keySet().iterator();
- while (keyIt.hasNext()) {
- String key = keyIt.next();
- writer.write(key + ": " + config.get(key));
- if (keyIt.hasNext()) {
- writer.write(", ");
- }
- }
-
- writer.write("};\n");
- }
-
- /**
- * Writes the Vaadin theme loading section of the portlet HTML. Loads both
- * the portal theme and the portlet theme in this order, skipping loading of
- * themes that are already loaded (matched by name).
- *
- * @param request
- * @param writer
- * @param themeName
- * @param themeURI
- * @param portalTheme
- * @throws IOException
- */
- protected void writeAjaxPageHtmlTheme(RenderRequest request,
- final BufferedWriter writer, String themeName, String themeURI,
- String portalTheme) throws IOException {
- writer.write("<script type=\"text/javascript\">\n");
-
- if (portalTheme == null) {
- portalTheme = DEFAULT_THEME_NAME;
- }
-
- writer.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n");
- writer.write("var defaultStylesheet = document.createElement('link');\n");
- writer.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n");
- writer.write("defaultStylesheet.setAttribute('type', 'text/css');\n");
- writer.write("defaultStylesheet.setAttribute('href', '"
- + getThemeURI(portalTheme, request) + "/styles.css');\n");
- writer.write("document.getElementsByTagName('head')[0].appendChild(defaultStylesheet);\n");
- writer.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n");
-
- if (!portalTheme.equals(themeName)) {
- writer.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n");
- writer.write("var stylesheet = document.createElement('link');\n");
- writer.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
- writer.write("stylesheet.setAttribute('type', 'text/css');\n");
- writer.write("stylesheet.setAttribute('href', '" + themeURI
- + "/styles.css');\n");
- writer.write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
- writer.write("vaadin.themesLoaded['" + themeName
- + "'] = true;\n}\n");
- }
-
- writer.write("</script>\n");
- }
-
- /**
- * Method to write the div element into which that actual Vaadin application
- * is rendered.
- * <p>
- * Override this method if you want to add some custom html around around
- * the div element into which the actual Vaadin application will be
- * rendered.
- *
- * @param request
- * @param response
- * @param writer
- * @param id
- * @param classNames
- * @param divStyle
- * @throws IOException
- */
- protected void writeAjaxPageHtmlMainDiv(RenderRequest request,
- RenderResponse response, final BufferedWriter writer, String id,
- String classNames, String divStyle) throws IOException {
- writer.write("<div id=\"" + id + "\" class=\"" + classNames + "\" "
- + divStyle + ">");
- writer.write("<div class=\"v-app-loading\"></div>");
- writer.write("</div>\n");
- writer.write("<noscript>" + getNoScriptMessage() + "</noscript>");
- }
-
- /**
- * Returns a message printed for browsers without scripting support or if
- * browsers scripting support is disabled.
- */
- protected String getNoScriptMessage() {
- return "You have to enable javascript in your browser to use an application built with Vaadin.";
- }
-
- /**
- * Returns the theme for given request/window
- *
- * @param request
- * @param window
- * @return
- */
- protected String getThemeForWindow(PortletRequest request, Window window) {
- // Finds theme name
- String themeName;
-
- // theme defined for the window?
- themeName = window.getTheme();
-
- if (themeName == null) {
- // no, is the default theme defined by the portal?
- themeName = getPortalProperty(
- Constants.PORTAL_PARAMETER_VAADIN_THEME,
- request.getPortalContext());
- }
-
- if (themeName == null) {
- // no, using the default theme defined by Vaadin
- themeName = DEFAULT_THEME_NAME;
- }
-
- return themeName;
- }
-
protected abstract Class<? extends Application> getApplicationClass()
throws ClassNotFoundException;
@@ -1315,6 +986,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
throws PortletException {
try {
final Application application = getApplicationClass().newInstance();
+ application.setRootPreserved(true);
return application;
} catch (final IllegalAccessException e) {
throw new PortletException("getNewApplication failed", e);
@@ -1462,162 +1134,6 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}
/**
- * Returns a portal configuration property.
- *
- * Liferay is handled separately as
- * {@link PortalContext#getProperty(String)} does not return portal
- * properties from e.g. portal-ext.properties .
- *
- * @param name
- * @param context
- * @return
- */
- protected static String getPortalProperty(String name, PortalContext context) {
- boolean isLifeRay = context.getPortalInfo().toLowerCase()
- .contains("liferay");
-
- // TODO test on non-LifeRay platforms
-
- String value;
- if (isLifeRay) {
- value = getLifeRayPortalProperty(name);
- } else {
- value = context.getProperty(name);
- }
-
- return value;
- }
-
- private static String getLifeRayPortalProperty(String name) {
- String value;
- try {
- value = PropsUtil.get(name);
- } catch (Exception e) {
- value = null;
- }
- return value;
- }
-
- /**
- * Try to get an HTTP header value from a request using portal specific
- * APIs.
- *
- * @param name
- * HTTP header name
- * @return the value of the header (empty string if defined without a value,
- * null if the parameter is not present or retrieving it failed)
- */
- private static String getHTTPHeader(PortletRequest request, String name) {
- String value = null;
- String portalInfo = request.getPortalContext().getPortalInfo()
- .toLowerCase();
- if (portalInfo.contains("liferay")) {
- value = getLiferayHTTPHeader(request, name);
- } else if (portalInfo.contains("gatein")) {
- value = getGateInHTTPHeader(request, name);
- }
- return value;
- }
-
- /**
- * Try to get the value of a HTTP request parameter from a portlet request
- * using portal specific APIs. It is not possible to get the HTTP request
- * parameters using the official Portlet 2.0 API.
- *
- * @param name
- * HTTP request parameter name
- * @return the value of the parameter (empty string if parameter defined
- * without a value, null if the parameter is not present or
- * retrieving it failed)
- */
- private static String getHTTPRequestParameter(PortletRequest request,
- String name) {
- String value = request.getParameter(name);
- if (value == null) {
- String portalInfo = request.getPortalContext().getPortalInfo()
- .toLowerCase();
- if (portalInfo.contains("liferay")) {
- value = getLiferayHTTPRequestParameter(request, name);
- } else if (portalInfo.contains("gatein")) {
- value = getGateInHTTPRequestParameter(request, name);
- }
- }
- return value;
- }
-
- private static String getGateInHTTPRequestParameter(PortletRequest request,
- String name) {
- String value = null;
- try {
- Method getRealReq = request.getClass().getMethod("getRealRequest");
- HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
- .invoke(request);
- value = origRequest.getParameter(name);
- } catch (Exception e) {
- // do nothing - not on GateIn simple-portal
- }
- return value;
- }
-
- private static String getLiferayHTTPRequestParameter(
- PortletRequest request, String name) {
- try {
- // httpRequest = PortalUtil.getHttpServletRequest(request);
- HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
- .invoke("com.liferay.portal.util.PortalUtil",
- "getHttpServletRequest", request);
-
- // httpRequest =
- // PortalUtil.getOriginalServletRequest(httpRequest);
- httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
- "com.liferay.portal.util.PortalUtil",
- "getOriginalServletRequest", httpRequest);
- if (httpRequest != null) {
- return httpRequest.getParameter(name);
- }
- } catch (Exception e) {
- // ignore and return null - unable to get the original request
- }
- return null;
- }
-
- private static String getGateInHTTPHeader(PortletRequest request,
- String name) {
- String value = null;
- try {
- Method getRealReq = request.getClass().getMethod("getRealRequest");
- HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
- .invoke(request);
- value = origRequest.getHeader(name);
- } catch (Exception e) {
- // do nothing - not on GateIn simple-portal
- }
- return value;
- }
-
- private static String getLiferayHTTPHeader(PortletRequest request,
- String name) {
- try {
- // httpRequest = PortalUtil.getHttpServletRequest(request);
- HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker
- .invoke("com.liferay.portal.util.PortalUtil",
- "getHttpServletRequest", request);
-
- // httpRequest =
- // PortalUtil.getOriginalServletRequest(httpRequest);
- httpRequest = (HttpServletRequest) PortalClassInvoker.invoke(
- "com.liferay.portal.util.PortalUtil",
- "getOriginalServletRequest", httpRequest);
- if (httpRequest != null) {
- return httpRequest.getHeader(name);
- }
- } catch (Exception e) {
- // ignore and return null - unable to get the original request
- }
- return null;
- }
-
- /**
*
* Gets the application context for a PortletSession. If no context is
* currently stored in a session a new context is created and stored in the
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
index 04ea423004..799271b979 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
@@ -15,15 +15,14 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
-import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -37,14 +36,16 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.vaadin.Application;
+import com.vaadin.Application.ApplicationStartEvent;
import com.vaadin.Application.SystemMessages;
-import com.vaadin.terminal.DownloadStream;
-import com.vaadin.terminal.ParameterHandler;
+import com.vaadin.terminal.DeploymentConfiguration;
import com.vaadin.terminal.Terminal;
import com.vaadin.terminal.ThemeResource;
-import com.vaadin.terminal.URIHandler;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.ui.Window;
+import com.vaadin.terminal.gwt.server.AbstractCommunicationManager.Callback;
+import com.vaadin.ui.Root;
/**
* Abstract implementation of the ApplicationServlet which handles all
@@ -64,6 +65,25 @@ import com.vaadin.ui.Window;
public abstract class AbstractApplicationServlet extends HttpServlet implements
Constants {
+ private static class AbstractApplicationServletWrapper implements Callback {
+
+ private final AbstractApplicationServlet servlet;
+
+ public AbstractApplicationServletWrapper(
+ AbstractApplicationServlet servlet) {
+ this.servlet = servlet;
+ }
+
+ public void criticalNotification(WrappedRequest request,
+ WrappedResponse response, String cap, String msg,
+ String details, String outOfSyncURL) throws IOException {
+ servlet.criticalNotification(
+ WrappedHttpServletRequest.cast(request),
+ ((WrappedHttpServletResponse) response), cap, msg, details,
+ outOfSyncURL);
+ }
+ }
+
// TODO Move some (all?) of the constants to a separate interface (shared
// with portlet)
@@ -115,67 +135,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
}
- /**
- * If the attribute is present in the request, a html fragment will be
- * written instead of a whole page.
- *
- * It is set to "true" by the {@link ApplicationPortlet} (Portlet 1.0) and
- * read by {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_FRAGMENT = ApplicationServlet.class
- .getName() + ".fragment";
- /**
- * This request attribute forces widgetsets to be loaded from under the
- * specified base path; e.g shared widgetset for all portlets in a portal.
- *
- * It is set by the {@link ApplicationPortlet} (Portlet 1.0) based on
- * {@link Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH} and read by
- * {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_VAADIN_STATIC_FILE_PATH = ApplicationServlet.class
- .getName() + ".widgetsetPath";
- /**
- * This request attribute forces widgetset used; e.g for portlets that can
- * not have different widgetsets.
- *
- * It is set by the {@link ApplicationPortlet} (Portlet 1.0) based on
- * {@link ApplicationPortlet.PORTLET_PARAMETER_WIDGETSET} and read by
- * {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_WIDGETSET = ApplicationServlet.class
- .getName() + ".widgetset";
- /**
- * This request attribute indicates the shared widgetset (e.g. portal-wide
- * default widgetset).
- *
- * It is set by the {@link ApplicationPortlet} (Portlet 1.0) based on
- * {@link Constants.PORTAL_PARAMETER_VAADIN_WIDGETSET} and read by
- * {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_SHARED_WIDGETSET = ApplicationServlet.class
- .getName() + ".sharedWidgetset";
- /**
- * If set, do not load the default theme but assume that loading it is
- * handled e.g. by ApplicationPortlet.
- *
- * It is set by the {@link ApplicationPortlet} (Portlet 1.0) based on
- * {@link Constants.PORTAL_PARAMETER_VAADIN_THEME} and read by
- * {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_DEFAULT_THEME = ApplicationServlet.class
- .getName() + ".defaultThemeUri";
- /**
- * This request attribute is used to add styles to the main element. E.g
- * "height:500px" generates a style="height:500px" to the main element,
- * useful from some embedding situations (e.g portlet include.)
- *
- * It is typically set by the {@link ApplicationPortlet} (Portlet 1.0) based
- * on {@link ApplicationPortlet.PORTLET_PARAMETER_STYLE} and read by
- * {@link AbstractApplicationServlet}.
- */
- public static final String REQUEST_APPSTYLE = ApplicationServlet.class
- .getName() + ".style";
-
private Properties applicationProperties;
private boolean productionMode = false;
@@ -183,6 +142,45 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
private final String resourcePath = null;
private int resourceCacheTime = 3600;
+
+ private DeploymentConfiguration deploymentConfiguration = new DeploymentConfiguration() {
+ public String getStaticFileLocation(WrappedRequest request) {
+ HttpServletRequest servletRequest = WrappedHttpServletRequest
+ .cast(request);
+ return AbstractApplicationServlet.this
+ .getStaticFilesLocation(servletRequest);
+ }
+
+ public String getConfiguredWidgetset(WrappedRequest request) {
+ return getApplicationOrSystemProperty(
+ AbstractApplicationServlet.PARAMETER_WIDGETSET,
+ AbstractApplicationServlet.DEFAULT_WIDGETSET);
+ }
+
+ public String getConfiguredTheme(WrappedRequest request) {
+ // Use the default
+ return AbstractApplicationServlet.getDefaultTheme();
+ }
+
+ public String getApplicationOrSystemProperty(String propertyName,
+ String defaultValue) {
+ return AbstractApplicationServlet.this
+ .getApplicationOrSystemProperty(propertyName, defaultValue);
+ }
+
+ public boolean isStandalone(WrappedRequest request) {
+ return true;
+ }
+
+ public ClassLoader getClassLoader() {
+ try {
+ return AbstractApplicationServlet.this.getClassLoader();
+ } catch (ServletException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
static final String UPLOAD_URL_PREFIX = "APP/UPLOAD/";
/**
@@ -201,17 +199,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
public void init(javax.servlet.ServletConfig servletConfig)
throws javax.servlet.ServletException {
super.init(servletConfig);
-
- // Stores the application parameters into Properties object
applicationProperties = new Properties();
- for (final Enumeration<String> e = servletConfig
- .getInitParameterNames(); e.hasMoreElements();) {
- final String name = e.nextElement();
- applicationProperties.setProperty(name,
- servletConfig.getInitParameter(name));
- }
- // Overrides with server.xml parameters
+ // Read default parameters from server.xml
final ServletContext context = servletConfig.getServletContext();
for (final Enumeration<String> e = context.getInitParameterNames(); e
.hasMoreElements();) {
@@ -219,6 +209,15 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
applicationProperties.setProperty(name,
context.getInitParameter(name));
}
+
+ // Override with application config from web.xml
+ for (final Enumeration<String> e = servletConfig
+ .getInitParameterNames(); e.hasMoreElements();) {
+ final String name = e.nextElement();
+ applicationProperties.setProperty(name,
+ servletConfig.getInitParameter(name));
+ }
+
checkProductionMode();
checkCrossSiteProtection();
checkResourceCacheTime();
@@ -251,14 +250,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
private void checkProductionMode() {
// Check if the application is in production mode.
- // We are in production mode if Debug=false or productionMode=true
- if (getApplicationOrSystemProperty(SERVLET_PARAMETER_DEBUG, "true")
- .equals("false")) {
- // "Debug=true" is the old way and should no longer be used
- productionMode = true;
- } else if (getApplicationOrSystemProperty(
- SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) {
- // "productionMode=true" is the real way to do it
+ // We are in production mode if productionMode=true
+ if (getApplicationOrSystemProperty(SERVLET_PARAMETER_PRODUCTION_MODE,
+ "false").equals("true")) {
productionMode = true;
}
@@ -341,7 +335,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* the Default to be used.
* @return String value or default if not found
*/
- private String getApplicationOrSystemProperty(String parameterName,
+ String getApplicationOrSystemProperty(String parameterName,
String defaultValue) {
String val = null;
@@ -397,14 +391,20 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* @throws IOException
* if the request for the TRACE cannot be handled.
*/
- @SuppressWarnings("unchecked")
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
- RequestTimer.RequestWrapper wrappedRequest = new RequestTimer.RequestWrapper(
- request);
- RequestTimer requestTimer = RequestTimer.get(wrappedRequest);
- requestTimer.start(wrappedRequest);
+ service(createWrappedRequest(request), createWrappedResponse(response));
+ }
+
+ private void service(WrappedHttpServletRequest request,
+ WrappedHttpServletResponse response) throws ServletException,
+ IOException {
+ AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
+ this);
+
+ RequestTimer requestTimer = RequestTimer.get(request);
+ requestTimer.start(request);
RequestType requestType = getRequestType(request);
if (!ensureCookiesEnabled(requestType, request, response)) {
@@ -449,6 +449,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
if (application == null) {
return;
}
+ Application.setCurrentApplication(application);
/*
* Get or create a WebApplicationContext and an ApplicationManager
@@ -460,7 +461,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
.getApplicationManager(application, this);
/* Update browser information from the request */
- updateBrowserProperties(webApplicationContext.getBrowser(), request);
+ webApplicationContext.getBrowser().updateRequestDetails(request);
/*
* Call application requestStart before Application.init() is called
@@ -472,7 +473,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
requestStarted = true;
}
- // Start the newly created application
+ // Start the application if it's newly created
startApplication(request, application, webApplicationContext);
/*
@@ -484,14 +485,21 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
/* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) {
- applicationManager.handleFileUpload(request, response);
+ applicationManager.handleFileUpload(application, request,
+ response);
return;
} else if (requestType == RequestType.UIDL) {
// Handles AJAX UIDL requests
- Window window = applicationManager.getApplicationWindow(
- request, this, application, null);
- applicationManager.handleUidlRequest(request, response, this,
- window);
+ Root root = application.getRootForRequest(request);
+ if (root == null) {
+ throw new ServletException(ERROR_NO_WINDOW_FOUND);
+ }
+ applicationManager.handleUidlRequest(request, response,
+ servletWrapper, root);
+ return;
+ } else if (requestType == RequestType.BROWSER_DETAILS) {
+ applicationManager.handleBrowserDetailsRequest(request,
+ response, application);
return;
}
@@ -502,34 +510,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
return;
}
- // Finds the window within the application
- Window window = getApplicationWindow(request, applicationManager,
- application);
- if (window == null) {
- throw new ServletException(ERROR_NO_WINDOW_FOUND);
- }
-
- // Sets terminal type for the window, if not already set
- if (window.getTerminal() == null) {
- window.setTerminal(webApplicationContext.getBrowser());
- }
-
- // Handle parameters
- final Map<String, String[]> parameters = request.getParameterMap();
- if (window != null && parameters != null) {
- window.handleParameters(parameters);
- }
-
- /*
- * Call the URI handlers and if this turns out to be a download
- * request, send the file to the client
- */
- if (handleURI(applicationManager, window, request, response)) {
+ if (applicationManager.handleApplicationRequest(request, response)) {
return;
}
-
- // Send initial AJAX page that kickstarts a Vaadin application
- writeAjaxPage(request, response, window, application);
+ // TODO Should return 404 error here and not do anything more
} catch (final SessionExpiredException e) {
// Session has expired, notify user
@@ -548,18 +532,53 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
} finally {
- if (requestStarted) {
- ((HttpServletRequestListener) application).onRequestEnd(
- request, response);
+ try {
+ if (requestStarted) {
+ ((HttpServletRequestListener) application)
+ .onRequestEnd(request, response);
+ }
+ } finally {
+ Root.setCurrentRoot(null);
+ Application.setCurrentApplication(null);
}
requestTimer.stop();
- RequestTimer.set(wrappedRequest, requestTimer);
+ RequestTimer.set(request, requestTimer);
}
}
}
+ private WrappedHttpServletResponse createWrappedResponse(
+ HttpServletResponse response) {
+ WrappedHttpServletResponse wrappedResponse = new WrappedHttpServletResponse(
+ response, getDeploymentConfiguration());
+ return wrappedResponse;
+ }
+
+ /**
+ * Create a wrapped request for a http servlet request. This method can be
+ * overridden if the wrapped request should have special properties.
+ *
+ * @param request
+ * the original http servlet request
+ * @return a wrapped request for the original request
+ */
+ protected WrappedHttpServletRequest createWrappedRequest(
+ HttpServletRequest request) {
+ return new WrappedHttpServletRequest(request,
+ getDeploymentConfiguration());
+ }
+
+ /**
+ * Gets a the deployment configuration for this servlet.
+ *
+ * @return the deployment configuration
+ */
+ protected DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+
/**
* Check that cookie support is enabled in the browser. Only checks UIDL
* requests.
@@ -593,23 +612,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
return true;
}
- private void updateBrowserProperties(WebBrowser browser,
- HttpServletRequest request) {
- // request based details updated always
- browser.updateRequestDetails(request.getLocale(),
- request.getRemoteAddr(), request.isSecure(),
- request.getHeader("user-agent"));
- if (request.getParameter("repaintAll") != null) {
- browser.updateClientSideDetails(request.getParameter("sw"),
- request.getParameter("sh"), request.getParameter("cw"),
- request.getParameter("ch"), request.getParameter("tzo"),
- request.getParameter("rtzo"), request.getParameter("dstd"),
- request.getParameter("dston"),
- request.getParameter("curdate"),
- request.getParameter("td") != null);
- }
- }
-
protected ClassLoader getClassLoader() throws ServletException {
// Gets custom class loader
final String classLoaderName = getApplicationOrSystemProperty(
@@ -818,7 +820,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
/*
* UIDL request contains valid repaintAll=1 event, the user probably
* wants to initiate a new application through a custom index.html
- * without using writeAjaxPage.
+ * without using the bootstrap page.
*/
return true;
@@ -864,101 +866,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
/**
- * Handles the requested URI. An application can add handlers to do special
- * processing, when a certain URI is requested. The handlers are invoked
- * before any windows URIs are processed and if a DownloadStream is returned
- * it is sent to the client.
- *
- * @param stream
- * the download stream.
- *
- * @param request
- * the HTTP request instance.
- * @param response
- * the HTTP response to write to.
- * @throws IOException
- *
- * @see com.vaadin.terminal.URIHandler
- */
- private void handleDownload(DownloadStream stream,
- HttpServletRequest request, HttpServletResponse response)
- throws IOException {
-
- if (stream.getParameter("Location") != null) {
- response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
- response.addHeader("Location", stream.getParameter("Location"));
- return;
- }
-
- // Download from given stream
- final InputStream data = stream.getStream();
- if (data != null) {
-
- OutputStream out = null;
- try {
- // Sets content type
- response.setContentType(stream.getContentType());
-
- // Sets cache headers
- final long cacheTime = stream.getCacheTime();
- if (cacheTime <= 0) {
- response.setHeader("Cache-Control", "no-cache");
- response.setHeader("Pragma", "no-cache");
- response.setDateHeader("Expires", 0);
- } else {
- response.setHeader("Cache-Control", "max-age=" + cacheTime
- / 1000);
- response.setDateHeader("Expires",
- System.currentTimeMillis() + cacheTime);
- response.setHeader("Pragma", "cache"); // Required to apply
- // caching in some
- // Tomcats
- }
-
- // Copy download stream parameters directly
- // to HTTP headers.
- final Iterator<String> i = stream.getParameterNames();
- if (i != null) {
- while (i.hasNext()) {
- final String param = i.next();
- response.setHeader(param, stream.getParameter(param));
- }
- }
-
- // suggest local filename from DownloadStream if
- // Content-Disposition
- // not explicitly set
- String contentDispositionValue = stream
- .getParameter("Content-Disposition");
- if (contentDispositionValue == null) {
- contentDispositionValue = "filename=\""
- + stream.getFileName() + "\"";
- response.setHeader("Content-Disposition",
- contentDispositionValue);
- }
-
- int bufferSize = stream.getBufferSize();
- if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
- bufferSize = DEFAULT_BUFFER_SIZE;
- }
- final byte[] buffer = new byte[bufferSize];
- int bytesRead = 0;
-
- out = response.getOutputStream();
-
- while ((bytesRead = data.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
- out.flush();
- }
- } finally {
- AbstractCommunicationManager.tryToCloseStream(out);
- AbstractCommunicationManager.tryToCloseStream(data);
- }
- }
-
- }
-
- /**
* Creates a new application and registers it into WebApplicationContext
* (aka session). This is not meant to be overridden. Override
* getNewApplication to create the application instance in a custom way.
@@ -1002,42 +909,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
/**
- * Returns the theme for given request/window
- *
- * @param request
- * @param window
- * @return
- */
- private String getThemeForWindow(HttpServletRequest request, Window window) {
- // Finds theme name
- String themeName;
-
- if (request.getParameter(URL_PARAMETER_THEME) != null) {
- themeName = request.getParameter(URL_PARAMETER_THEME);
- } else {
- themeName = window.getTheme();
- }
-
- if (themeName == null) {
- // no explicit theme for window defined
- if (request.getAttribute(REQUEST_DEFAULT_THEME) != null) {
- // the default theme is defined in request (by portal)
- themeName = (String) request
- .getAttribute(REQUEST_DEFAULT_THEME);
- } else {
- // using the default theme defined by Vaadin
- themeName = getDefaultTheme();
- }
- }
-
- // XSS preventation, theme names shouldn't contain special chars anyway.
- // The servlet denies them via url parameter.
- themeName = stripSpecialChars(themeName);
-
- return themeName;
- }
-
- /**
* A helper method to strip away characters that might somehow be used for
* XSS attacs. Leaves at least alphanumeric characters intact. Also removes
* eg. ( and ), so values should be safe in javascript too.
@@ -1070,34 +941,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
return DEFAULT_THEME_NAME;
}
- /**
- * Calls URI handlers for the request. If an URI handler returns a
- * DownloadStream the stream is passed to the client for downloading.
- *
- * @param applicationManager
- * @param window
- * @param request
- * @param response
- * @return true if an DownloadStream was sent to the client, false otherwise
- * @throws IOException
- */
- protected boolean handleURI(CommunicationManager applicationManager,
- Window window, HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- // Handles the URI
- DownloadStream download = applicationManager.handleURI(window, request,
- response, this);
-
- // A download request
- if (download != null) {
- // Client downloads an resource
- handleDownload(download, request, response);
- return true;
- }
-
- return false;
- }
-
void handleServiceSessionExpired(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
@@ -1205,8 +1048,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// Initial locale comes from the request
Locale locale = request.getLocale();
application.setLocale(locale);
- application.start(applicationUrl, applicationProperties,
- webApplicationContext);
+ application.start(new ApplicationStartEvent(applicationUrl,
+ applicationProperties, webApplicationContext,
+ isProductionMode()));
}
}
@@ -1293,8 +1137,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
// Find the modification timestamp
long lastModifiedTime = 0;
+ URLConnection connection = null;
try {
- lastModifiedTime = resourceUrl.openConnection().getLastModified();
+ connection = resourceUrl.openConnection();
+ lastModifiedTime = connection.getLastModified();
// Remove milliseconds to avoid comparison problems (milliseconds
// are not returned by the browser in the "If-Modified-Since"
// header).
@@ -1310,6 +1156,21 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
Level.FINEST,
"Failed to find out last modified timestamp. Continuing without it.",
e);
+ } finally {
+ if (connection instanceof URLConnection) {
+ try {
+ // Explicitly close the input stream to prevent it
+ // from remaining hanging
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4257700
+ InputStream is = connection.getInputStream();
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ logger.log(Level.INFO,
+ "Error closing URLConnection input stream", e);
+ }
+ }
}
// Set type mime type if we can determine it based on the filename
@@ -1441,12 +1302,14 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
protected enum RequestType {
- FILE_UPLOAD, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE;
+ FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE;
}
protected RequestType getRequestType(HttpServletRequest request) {
if (isFileUploadRequest(request)) {
return RequestType.FILE_UPLOAD;
+ } else if (isBrowserDetailsRequest(request)) {
+ return RequestType.BROWSER_DETAILS;
} else if (isUIDLRequest(request)) {
return RequestType.UIDL;
} else if (isStaticResourceRequest(request)) {
@@ -1460,6 +1323,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
+ private static boolean isBrowserDetailsRequest(HttpServletRequest request) {
+ return "POST".equals(request.getMethod())
+ && request.getParameter("browserDetails") != null;
+ }
+
private boolean isApplicationRequest(HttpServletRequest request) {
String path = getRequestPathInfo(request);
if (path != null && path.startsWith("/APP/")) {
@@ -1526,13 +1394,25 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
* @return
*/
protected SystemMessages getSystemMessages() {
+ Class<? extends Application> appCls = null;
try {
- Class<? extends Application> appCls = getApplicationClass();
- Method m = appCls.getMethod("getSystemMessages", (Class[]) null);
- return (Application.SystemMessages) m.invoke(null, (Object[]) null);
+ appCls = getApplicationClass();
} catch (ClassNotFoundException e) {
- // This should never happen
+ // Previous comment claimed that this should never happen
throw new SystemMessageException(e);
+ }
+ return getSystemMessages(appCls);
+ }
+
+ public static SystemMessages getSystemMessages(
+ Class<? extends Application> appCls) {
+ try {
+ if (appCls != null) {
+ Method m = appCls
+ .getMethod("getSystemMessages", (Class[]) null);
+ return (Application.SystemMessages) m.invoke(null,
+ (Object[]) null);
+ }
} catch (SecurityException e) {
throw new SystemMessageException(
"Application.getSystemMessage() should be static public", e);
@@ -1568,15 +1448,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
*/
protected String getStaticFilesLocation(HttpServletRequest request) {
- // request may have an attribute explicitly telling location (portal
- // case)
- String staticFileLocation = (String) request
- .getAttribute(REQUEST_VAADIN_STATIC_FILE_PATH);
- if (staticFileLocation != null) {
- // TODO remove trailing slash if any?
- return staticFileLocation;
- }
-
return getWebApplicationsStaticFileLocation(request);
}
@@ -1659,484 +1530,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
/**
- * This method writes the html host page (aka kickstart page) that starts
- * the actual Vaadin application.
- * <p>
- * If one needs to override parts of the host page, it is suggested that one
- * overrides on of several submethods which are called by this method:
- * <ul>
- * <li> {@link #setAjaxPageHeaders(HttpServletResponse)}
- * <li>
- * {@link #writeAjaxPageHtmlHeadStart(BufferedWriter, HttpServletRequest)}
- * <li>
- * {@link #writeAjaxPageHtmlHeader(BufferedWriter, String, String, HttpServletRequest)}
- * <li>
- * {@link #writeAjaxPageHtmlBodyStart(BufferedWriter, HttpServletRequest)}
- * <li>
- * {@link #writeAjaxPageHtmlVaadinScripts(Window, String, Application, BufferedWriter, String, String, String, HttpServletRequest)}
- * <li>
- * {@link #writeAjaxPageHtmlMainDiv(BufferedWriter, String, String, String, HttpServletRequest)}
- * <li> {@link #writeAjaxPageHtmlBodyEnd(BufferedWriter)}
- * </ul>
- *
- * @param request
- * the HTTP request.
- * @param response
- * the HTTP response to write to.
- * @param out
- * @param unhandledParameters
- * @param window
- * @param terminalType
- * @param theme
- * @throws IOException
- * if the writing failed due to input/output error.
- * @throws MalformedURLException
- * if the application is denied access the persistent data store
- * represented by the given URL.
- */
- protected void writeAjaxPage(HttpServletRequest request,
- HttpServletResponse response, Window window, Application application)
- throws IOException, MalformedURLException, ServletException {
-
- // e.g portlets only want a html fragment
- boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null);
- if (fragment) {
- // if this is a fragment request, the actual application is put to
- // request so ApplicationPortlet can save it for a later use
- request.setAttribute(Application.class.getName(), application);
- }
-
- final BufferedWriter page = new BufferedWriter(new OutputStreamWriter(
- response.getOutputStream(), "UTF-8"));
-
- String title = ((window.getCaption() == null) ? "Vaadin 6" : window
- .getCaption());
-
- /* Fetch relative url to application */
- // don't use server and port in uri. It may cause problems with some
- // virtual server configurations which lose the server name
- String appUrl = getApplicationUrl(request).getPath();
- if (appUrl.endsWith("/")) {
- appUrl = appUrl.substring(0, appUrl.length() - 1);
- }
-
- String themeName = getThemeForWindow(request, window);
-
- String themeUri = getThemeUri(themeName, request);
-
- if (!fragment) {
- setAjaxPageHeaders(response);
- writeAjaxPageHtmlHeadStart(page, request);
- writeAjaxPageHtmlHeader(page, title, themeUri, request);
- writeAjaxPageHtmlBodyStart(page, request);
- }
-
- String appId = appUrl;
- if ("".equals(appUrl)) {
- appId = "ROOT";
- }
- appId = appId.replaceAll("[^a-zA-Z0-9]", "");
- // Add hashCode to the end, so that it is still (sort of) predictable,
- // but indicates that it should not be used in CSS and such:
- int hashCode = appId.hashCode();
- if (hashCode < 0) {
- hashCode = -hashCode;
- }
- appId = appId + "-" + hashCode;
-
- writeAjaxPageHtmlVaadinScripts(window, themeName, application, page,
- appUrl, themeUri, appId, request);
-
- /*- Add classnames;
- * .v-app
- * .v-app-loading
- * .v-app-<simpleName for app class>
- * .v-theme-<themeName, remove non-alphanum>
- */
-
- String appClass = "v-app-" + getApplicationCSSClassName();
-
- String themeClass = "";
- if (themeName != null) {
- themeClass = "v-theme-" + themeName.replaceAll("[^a-zA-Z0-9]", "");
- } else {
- themeClass = "v-theme-"
- + getDefaultTheme().replaceAll("[^a-zA-Z0-9]", "");
- }
-
- String classNames = "v-app " + themeClass + " " + appClass;
-
- String divStyle = null;
- if (request.getAttribute(REQUEST_APPSTYLE) != null) {
- divStyle = "style=\"" + request.getAttribute(REQUEST_APPSTYLE)
- + "\"";
- }
-
- writeAjaxPageHtmlMainDiv(page, appId, classNames, divStyle, request);
-
- if (!fragment) {
- page.write("</body>\n</html>\n");
- }
-
- page.close();
-
- }
-
- /**
- * Returns the application class identifier for use in the application CSS
- * class name in the root DIV. The application CSS class name is of form
- * "v-app-"+getApplicationCSSClassName().
- *
- * This method should normally not be overridden.
- *
- * @return The CSS class name to use in combination with "v-app-".
- */
- protected String getApplicationCSSClassName() {
- try {
- return getApplicationClass().getSimpleName();
- } catch (ClassNotFoundException e) {
- logger.log(Level.WARNING, "getApplicationCSSClassName failed", e);
- return "unknown";
- }
- }
-
- /**
- * Get the URI for the application theme.
- *
- * A portal-wide default theme is fetched from the portal shared resource
- * directory (if any), other themes from the portlet.
- *
- * @param themeName
- * @param request
- * @return
- */
- private String getThemeUri(String themeName, HttpServletRequest request) {
- final String staticFilePath;
- if (themeName.equals(request.getAttribute(REQUEST_DEFAULT_THEME))) {
- // our window theme is the portal wide default theme, make it load
- // from portals directory is defined
- staticFilePath = getStaticFilesLocation(request);
- } else {
- /*
- * theme is a custom theme, which is not necessarily located in
- * portals VAADIN directory. Let the default servlet conf decide
- * (omitting request parameter) the location. Note that theme can
- * still be placed to portal directory with servlet parameter.
- */
- staticFilePath = getWebApplicationsStaticFileLocation(request);
- }
- return staticFilePath + "/" + THEME_DIRECTORY_PATH + themeName;
- }
-
- /**
- * Method to write the div element into which that actual Vaadin application
- * is rendered.
- * <p>
- * Override this method if you want to add some custom html around around
- * the div element into which the actual Vaadin application will be
- * rendered.
- *
- * @param page
- * @param appId
- * @param classNames
- * @param divStyle
- * @param request
- * @throws IOException
- */
- protected void writeAjaxPageHtmlMainDiv(final BufferedWriter page,
- String appId, String classNames, String divStyle,
- HttpServletRequest request) throws IOException {
- page.write("<div id=\"" + appId + "\" class=\"" + classNames + "\" "
- + (divStyle != null ? divStyle : "") + ">");
- page.write("<div class=\"v-app-loading\"></div>");
- page.write("</div>\n");
- page.write("<noscript>" + getNoScriptMessage() + "</noscript>");
- }
-
- /**
- * Method to write the script part of the page which loads needed Vaadin
- * scripts and themes.
- * <p>
- * Override this method if you want to add some custom html around scripts.
- *
- * @param window
- * @param themeName
- * @param application
- * @param page
- * @param appUrl
- * @param themeUri
- * @param appId
- * @param request
- * @throws ServletException
- * @throws IOException
- */
- protected void writeAjaxPageHtmlVaadinScripts(Window window,
- String themeName, Application application,
- final BufferedWriter page, String appUrl, String themeUri,
- String appId, HttpServletRequest request) throws ServletException,
- IOException {
-
- // request widgetset takes precedence (e.g portlet include)
- String requestWidgetset = (String) request
- .getAttribute(REQUEST_WIDGETSET);
- String sharedWidgetset = (String) request
- .getAttribute(REQUEST_SHARED_WIDGETSET);
- if (requestWidgetset == null && sharedWidgetset == null) {
- // Use the value from configuration or DEFAULT_WIDGETSET.
- // If no shared widgetset is specified, the default widgetset is
- // assumed to be in the servlet/portlet itself.
- requestWidgetset = getApplicationOrSystemProperty(
- PARAMETER_WIDGETSET, DEFAULT_WIDGETSET);
- }
-
- String widgetset;
- String widgetsetBasePath;
- if (requestWidgetset != null) {
- widgetset = requestWidgetset;
- widgetsetBasePath = getWebApplicationsStaticFileLocation(request);
- } else {
- widgetset = sharedWidgetset;
- widgetsetBasePath = getStaticFilesLocation(request);
- }
-
- widgetset = stripSpecialChars(widgetset);
-
- final String widgetsetFilePath = widgetsetBasePath + "/"
- + WIDGETSET_DIRECTORY_PATH + widgetset + "/" + widgetset
- + ".nocache.js" + createPreventCachingQueryString();
-
- // Get system messages
- Application.SystemMessages systemMessages = null;
- try {
- systemMessages = getSystemMessages();
- } catch (SystemMessageException e) {
- // failing to get the system messages is always a problem
- throw new ServletException("CommunicationError!", e);
- }
-
- page.write("<script type=\"text/javascript\">\n");
- page.write("//<![CDATA[\n");
- page.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n "
- + "if(!vaadin) { var vaadin = {}} \n"
- + "vaadin.vaadinConfigurations = {};\n"
- + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n");
- if (!isProductionMode()) {
- page.write("vaadin.debug = true;\n");
- }
- page.write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
- + "style=\"position:absolute;width:0;height:0;border:0;overflow:"
- + "hidden;\" src=\"javascript:false\"></iframe>');\n");
- page.write("document.write(\"<script language='javascript' src='"
- + widgetsetFilePath + "'><\\/script>\");\n}\n");
-
- page.write("vaadin.vaadinConfigurations[\"" + appId + "\"] = {");
- page.write("appUri:'" + appUrl + "', ");
-
- if (window != application.getMainWindow()) {
- page.write("windowName: \""
- + JsonPaintTarget.escapeJSON(window.getName()) + "\", ");
- }
- if (isStandalone()) {
- page.write("standalone: true, ");
- }
- page.write("themeUri:");
- page.write(themeUri != null ? "\"" + themeUri + "\"" : "null");
- page.write(", versionInfo : {vaadinVersion:\"");
- page.write(VERSION);
- page.write("\",applicationVersion:\"");
- page.write(JsonPaintTarget.escapeJSON(application.getVersion()));
- page.write("\"}");
- if (systemMessages != null) {
- // Write the CommunicationError -message to client
- String caption = systemMessages.getCommunicationErrorCaption();
- if (caption != null) {
- caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\"";
- }
- String message = systemMessages.getCommunicationErrorMessage();
- if (message != null) {
- message = "\"" + JsonPaintTarget.escapeJSON(message) + "\"";
- }
- String url = systemMessages.getCommunicationErrorURL();
- if (url != null) {
- url = "\"" + JsonPaintTarget.escapeJSON(url) + "\"";
- }
-
- page.write(",\"comErrMsg\": {" + "\"caption\":" + caption + ","
- + "\"message\" : " + message + "," + "\"url\" : " + url
- + "}");
-
- // Write the AuthenticationError -message to client
- caption = systemMessages.getAuthenticationErrorCaption();
- if (caption != null) {
- caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\"";
- }
- message = systemMessages.getAuthenticationErrorMessage();
- if (message != null) {
- message = "\"" + JsonPaintTarget.escapeJSON(message) + "\"";
- }
- url = systemMessages.getAuthenticationErrorURL();
- if (url != null) {
- url = "\"" + JsonPaintTarget.escapeJSON(url) + "\"";
- }
-
- page.write(",\"authErrMsg\": {" + "\"caption\":" + caption + ","
- + "\"message\" : " + message + "," + "\"url\" : " + url
- + "}");
- }
- page.write("};\n//]]>\n</script>\n");
-
- if (themeName != null) {
- // Custom theme's stylesheet, load only once, in different
- // script
- // tag to be dominate styles injected by widget
- // set
- page.write("<script type=\"text/javascript\">\n");
- page.write("//<![CDATA[\n");
- page.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n");
- page.write("var stylesheet = document.createElement('link');\n");
- page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
- page.write("stylesheet.setAttribute('type', 'text/css');\n");
- page.write("stylesheet.setAttribute('href', '" + themeUri
- + "/styles.css');\n");
- page.write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
- page.write("vaadin.themesLoaded['" + themeName + "'] = true;\n}\n");
- page.write("//]]>\n</script>\n");
- }
-
- // Warn if the widgetset has not been loaded after 15 seconds on
- // inactivity
- page.write("<script type=\"text/javascript\">\n");
- page.write("//<![CDATA[\n");
- page.write("setTimeout('if (typeof " + widgetset.replace('.', '_')
- + " == \"undefined\") {alert(\"Failed to load the widgetset: "
- + widgetsetFilePath + "\")};',15000);\n" + "//]]>\n</script>\n");
- }
-
- /**
- * To ensure the GWT kickstart scritp is downloaded each time (even if
- * server caching is not set up right), we add a unique query parameter to
- * the end of the script file.
- *
- * @return
- */
- protected String createPreventCachingQueryString() {
- return "?" + new Date().getTime();
- }
-
- /**
- * @return true if the served application is considered to be the only or
- * main content of the host page. E.g. various embedding solutions
- * should override this to false.
- */
- protected boolean isStandalone() {
- return true;
- }
-
- /**
- *
- * Method to open the body tag of the html kickstart page.
- * <p>
- * This method is responsible for closing the head tag and opening the body
- * tag.
- * <p>
- * Override this method if you want to add some custom html to the page.
- *
- * @param page
- * @param request
- * @throws IOException
- */
- protected void writeAjaxPageHtmlBodyStart(final BufferedWriter page,
- final HttpServletRequest request) throws IOException {
- page.write("\n</head>\n<body scroll=\"auto\" class=\""
- + ApplicationConnection.GENERATED_BODY_CLASSNAME + "\">\n");
- }
-
- /**
- * Method to write the contents of head element in html kickstart page.
- * <p>
- * Override this method if you want to add some custom html to the header of
- * the page.
- *
- * @param page
- * @param title
- * @param themeUri
- * @param request
- * @throws IOException
- */
- protected void writeAjaxPageHtmlHeader(final BufferedWriter page,
- String title, String themeUri, final HttpServletRequest request)
- throws IOException {
- page.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n");
-
- WebBrowser browser = getApplicationContext(request.getSession())
- .getBrowser();
- if (browser.isIE()) {
- // Chrome frame in all versions of IE (only if Chrome frame is
- // installed)
- page.write("<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\"/>\n");
- }
-
- page.write("<style type=\"text/css\">"
- + "html, body {height:100%;margin:0;}</style>");
-
- // Add favicon links
- page.write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
- page.write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
- + themeUri + "/favicon.ico\" />");
-
- page.write("<title>" + safeEscapeForHtml(title) + "</title>");
- }
-
- /**
- * Method to write the beginning of the html page.
- * <p>
- * This method is responsible for writing appropriate doc type declarations
- * and to open html and head tags.
- * <p>
- * Override this method if you want to add some custom html to the very
- * beginning of the page.
- *
- * @param page
- * @param request
- * @throws IOException
- */
- protected void writeAjaxPageHtmlHeadStart(final BufferedWriter page,
- final HttpServletRequest request) throws IOException {
- // write html header
- page.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
- + "XHTML 1.0 Transitional//EN\" "
- + "\"http://www.w3.org/TR/xhtml1/"
- + "DTD/xhtml1-transitional.dtd\">\n");
-
- page.write("<html xmlns=\"http://www.w3.org/1999/xhtml\""
- + ">\n<head>\n");
- }
-
- /**
- * Method to set http request headers for the Vaadin kickstart page.
- * <p>
- * Override this method if you need to customize http headers of the page.
- *
- * @param response
- */
- protected void setAjaxPageHeaders(HttpServletResponse response) {
- // Window renders are not cacheable
- response.setHeader("Cache-Control", "no-cache");
- response.setHeader("Pragma", "no-cache");
- response.setDateHeader("Expires", 0);
- response.setContentType("text/html; charset=UTF-8");
- }
-
- /**
- * Returns a message printed for browsers without scripting support or if
- * browsers scripting support is disabled.
- */
- protected String getNoScriptMessage() {
- return "You have to enable javascript in your browser to use an application built with Vaadin.";
- }
-
- /**
* Gets the current application URL from request.
*
* @param request
@@ -2264,52 +1657,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}
/**
- * Gets the existing application or create a new one. Get a window within an
- * application based on the requested URI.
- *
- * @param request
- * the HTTP Request.
- * @param application
- * the Application to query for window.
- * @return Window matching the given URI or null if not found.
- * @throws ServletException
- * if an exception has occurred that interferes with the
- * servlet's normal operation.
- */
- protected Window getApplicationWindow(HttpServletRequest request,
- CommunicationManager applicationManager, Application application)
- throws ServletException {
-
- // Finds the window where the request is handled
- Window assumedWindow = null;
- String path = getRequestPathInfo(request);
-
- // Main window as the URI is empty
- if (!(path == null || path.length() == 0 || path.equals("/"))) {
- if (path.startsWith("/APP/")) {
- // Use main window for application resources
- return application.getMainWindow();
- }
- String windowName = null;
- if (path.charAt(0) == '/') {
- path = path.substring(1);
- }
- final int index = path.indexOf('/');
- if (index < 0) {
- windowName = path;
- path = "";
- } else {
- windowName = path.substring(0, index);
- }
- assumedWindow = application.getWindow(windowName);
-
- }
-
- return applicationManager.getApplicationWindow(request, this,
- application, assumedWindow);
- }
-
- /**
* Returns the path info; note that this _can_ be different than
* request.getPathInfo(). Examples where this might be useful:
* <ul>
@@ -2379,75 +1726,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
return WebApplicationContext.getApplicationContext(session);
}
- /**
- * Implementation of ParameterHandler.ErrorEvent interface.
- */
- public class ParameterHandlerErrorImpl implements
- ParameterHandler.ErrorEvent, Serializable {
-
- private ParameterHandler owner;
-
- private Throwable throwable;
-
- /**
- * Gets the contained throwable.
- *
- * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return throwable;
- }
-
- /**
- * Gets the source ParameterHandler.
- *
- * @see com.vaadin.terminal.ParameterHandler.ErrorEvent#getParameterHandler()
- */
- public ParameterHandler getParameterHandler() {
- return owner;
- }
-
- }
-
- /**
- * Implementation of URIHandler.ErrorEvent interface.
- */
- public class URIHandlerErrorImpl implements URIHandler.ErrorEvent,
- Serializable {
-
- private final URIHandler owner;
-
- private final Throwable throwable;
-
- /**
- *
- * @param owner
- * @param throwable
- */
- private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
- this.owner = owner;
- this.throwable = throwable;
- }
-
- /**
- * Gets the contained throwable.
- *
- * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return throwable;
- }
-
- /**
- * Gets the source URIHandler.
- *
- * @see com.vaadin.terminal.URIHandler.ErrorEvent#getURIHandler()
- */
- public URIHandler getURIHandler() {
- return owner;
- }
- }
-
public class RequestError implements Terminal.ErrorEvent, Serializable {
private final Throwable throwable;
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index 9a6bccebb8..b780f66a23 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -14,9 +14,10 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
+import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.net.URL;
+import java.lang.reflect.Type;
import java.security.GeneralSecurityException;
import java.text.CharacterIterator;
import java.text.DateFormat;
@@ -37,36 +38,43 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.portlet.PortletRequest;
-import javax.portlet.PortletResponse;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
import com.vaadin.Application;
import com.vaadin.Application.SystemMessages;
-import com.vaadin.terminal.ApplicationResource;
-import com.vaadin.terminal.DownloadStream;
+import com.vaadin.RootRequiresMoreInformationException;
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.CombinedRequest;
+import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.terminal.Paintable.RepaintRequestEvent;
+import com.vaadin.terminal.RequestHandler;
import com.vaadin.terminal.StreamVariable;
import com.vaadin.terminal.StreamVariable.StreamingEndEvent;
import com.vaadin.terminal.StreamVariable.StreamingErrorEvent;
import com.vaadin.terminal.Terminal.ErrorEvent;
import com.vaadin.terminal.Terminal.ErrorListener;
-import com.vaadin.terminal.URIHandler;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.BootstrapHandler.BootstrapContext;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Component;
+import com.vaadin.ui.DirtyConnectorTracker;
+import com.vaadin.ui.HasComponents;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.Root;
import com.vaadin.ui.Window;
/**
@@ -75,196 +83,30 @@ import com.vaadin.ui.Window;
* JavaScript) and the server side components. Its client side counterpart is
* {@link ApplicationConnection}.
*
- * A server side component sends its state to the client in a paint request (see
- * {@link Paintable} and {@link PaintTarget} on the server side). The client
- * widget receives these paint requests as calls to
- * {@link com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL()}. The client
- * component communicates back to the server by sending a list of variable
- * changes (see {@link ApplicationConnection#updateVariable()} and
- * {@link VariableOwner#changeVariables(Object, Map)}).
- *
* TODO Document better!
*/
@SuppressWarnings("serial")
-public abstract class AbstractCommunicationManager implements
- Paintable.RepaintRequestListener, Serializable {
+public abstract class AbstractCommunicationManager implements Serializable {
private static final String DASHDASH = "--";
private static final Logger logger = Logger
.getLogger(AbstractCommunicationManager.class.getName());
- /**
- * Generic interface of a (HTTP or Portlet) request to the application.
- *
- * This is a wrapper interface that allows
- * {@link AbstractCommunicationManager} to use a unified API.
- *
- * @see javax.servlet.ServletRequest
- * @see javax.portlet.PortletRequest
- *
- * @author peholmst
- */
- public interface Request {
-
- /**
- * Gets a {@link Session} wrapper implementation representing the
- * session for which this request was sent.
- *
- * Multiple Vaadin applications can be associated with a single session.
- *
- * @return Session
- */
- public Session getSession();
-
- /**
- * Are the applications in this session running in a portlet or directly
- * as servlets.
- *
- * @return true if in a portlet
- */
- public boolean isRunningInPortlet();
-
- /**
- * Get the named HTTP or portlet request parameter.
- *
- * @see javax.servlet.ServletRequest#getParameter(String)
- * @see javax.portlet.PortletRequest#getParameter(String)
- *
- * @param name
- * @return
- */
- public String getParameter(String name);
-
- /**
- * Returns the length of the request content that can be read from the
- * input stream returned by {@link #getInputStream()}.
- *
- * @return content length in bytes
- */
- public int getContentLength();
-
- /**
- * Returns an input stream from which the request content can be read.
- * The request content length can be obtained with
- * {@link #getContentLength()} without reading the full stream contents.
- *
- * @return
- * @throws IOException
- */
- public InputStream getInputStream() throws IOException;
-
- /**
- * Returns the request identifier that identifies the target Vaadin
- * window for the request.
- *
- * @return String identifier for the request target window
- */
- public String getRequestID();
-
- /**
- * @see javax.servlet.ServletRequest#getAttribute(String)
- * @see javax.portlet.PortletRequest#getAttribute(String)
- */
- public Object getAttribute(String name);
-
- /**
- * @see javax.servlet.ServletRequest#setAttribute(String, Object)
- * @see javax.portlet.PortletRequest#setAttribute(String, Object)
- */
- public void setAttribute(String name, Object value);
-
- /**
- * Gets the underlying request object. The request is typically either a
- * {@link ServletRequest} or a {@link PortletRequest}.
- *
- * @return wrapped request object
- */
- public Object getWrappedRequest();
+ private static final RequestHandler APP_RESOURCE_HANDLER = new ApplicationResourceHandler();
- }
-
- /**
- * Generic interface of a (HTTP or Portlet) response from the application.
- *
- * This is a wrapper interface that allows
- * {@link AbstractCommunicationManager} to use a unified API.
- *
- * @see javax.servlet.ServletResponse
- * @see javax.portlet.PortletResponse
- *
- * @author peholmst
- */
- public interface Response {
-
- /**
- * Gets the output stream to which the response can be written.
- *
- * @return
- * @throws IOException
- */
- public OutputStream getOutputStream() throws IOException;
-
- /**
- * Sets the MIME content type for the response to be communicated to the
- * browser.
- *
- * @param type
- */
- public void setContentType(String type);
-
- /**
- * Gets the wrapped response object, usually a class implementing either
- * {@link ServletResponse} or {@link PortletResponse}.
- *
- * @return wrapped request object
- */
- public Object getWrappedResponse();
-
- }
-
- /**
- * Generic wrapper interface for a (HTTP or Portlet) session.
- *
- * Several applications can be associated with a single session.
- *
- * TODO Document me!
- *
- * @see javax.servlet.http.HttpSession
- * @see javax.portlet.PortletSession
- *
- * @author peholmst
- */
- protected interface Session {
-
- public boolean isNew();
-
- public Object getAttribute(String name);
-
- public void setAttribute(String name, Object o);
-
- public int getMaxInactiveInterval();
-
- public Object getWrappedSession();
-
- }
+ private static final RequestHandler UNSUPPORTED_BROWSER_HANDLER = new UnsupportedBrowserHandler();
/**
* TODO Document me!
*
* @author peholmst
*/
- public interface Callback {
-
- public void criticalNotification(Request request, Response response,
- String cap, String msg, String details, String outOfSyncURL)
- throws IOException;
-
- public String getRequestPathInfo(Request request);
-
- public InputStream getThemeResourceAsStream(String themeName,
- String resource) throws IOException;
+ public interface Callback extends Serializable {
+ public void criticalNotification(WrappedRequest request,
+ WrappedResponse response, String cap, String msg,
+ String details, String outOfSyncURL) throws IOException;
}
static class UploadInterruptedException extends Exception {
@@ -280,33 +122,11 @@ public abstract class AbstractCommunicationManager implements
private static final String WRITE_SECURITY_TOKEN_FLAG = "writeSecurityToken";
/* Variable records indexes */
- private static final int VAR_PID = 1;
- private static final int VAR_NAME = 2;
- private static final int VAR_TYPE = 3;
- private static final int VAR_VALUE = 0;
-
- private static final char VTYPE_PAINTABLE = 'p';
- private static final char VTYPE_BOOLEAN = 'b';
- private static final char VTYPE_DOUBLE = 'd';
- private static final char VTYPE_FLOAT = 'f';
- private static final char VTYPE_LONG = 'l';
- private static final char VTYPE_INTEGER = 'i';
- private static final char VTYPE_STRING = 's';
- private static final char VTYPE_ARRAY = 'a';
- private static final char VTYPE_STRINGARRAY = 'c';
- private static final char VTYPE_MAP = 'm';
-
- private static final char VAR_RECORD_SEPARATOR = '\u001e';
-
- private static final char VAR_FIELD_SEPARATOR = '\u001f';
-
public static final char VAR_BURST_SEPARATOR = '\u001d';
- public static final char VAR_ARRAYITEM_SEPARATOR = '\u001c';
-
public static final char VAR_ESCAPE_CHARACTER = '\u001b';
- private final HashMap<String, OpenWindowCache> currentlyOpenWindowsInClient = new HashMap<String, OpenWindowCache>();
+ private final HashMap<Integer, ClientCache> rootToClientCache = new HashMap<Integer, ClientCache>();
private static final int MAX_BUFFER_SIZE = 64 * 1024;
@@ -315,20 +135,8 @@ public abstract class AbstractCommunicationManager implements
private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
- private final ArrayList<Paintable> dirtyPaintables = new ArrayList<Paintable>();
-
- private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>();
-
- private final HashMap<String, Paintable> idPaintableMap = new HashMap<String, Paintable>();
-
- private int idSequence = 0;
-
private final Application application;
- // Note that this is only accessed from synchronized block and
- // thus should be thread-safe.
- private String closingWindowName = null;
-
private List<String> locales;
private int pendingLocalesIndex;
@@ -341,7 +149,7 @@ public abstract class AbstractCommunicationManager implements
private int maxInactiveInterval;
- private static int nextUnusedWindowSuffix = 1;
+ private Connector highlightedConnector;
/**
* TODO New constructor - document me!
@@ -350,6 +158,9 @@ public abstract class AbstractCommunicationManager implements
*/
public AbstractCommunicationManager(Application application) {
this.application = application;
+ application.addRequestHandler(getBootstrapHandler());
+ application.addRequestHandler(APP_RESOURCE_HANDLER);
+ application.addRequestHandler(UNSUPPORTED_BROWSER_HANDLER);
requireLocale(application.getLocale().toString());
}
@@ -365,8 +176,6 @@ public abstract class AbstractCommunicationManager implements
private static final String GET_PARAM_HIGHLIGHT_COMPONENT = "highlightComponent";
- private Paintable highLightedPaintable;
-
private static String readLine(InputStream stream) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
int readByte = stream.read();
@@ -390,9 +199,9 @@ public abstract class AbstractCommunicationManager implements
* @param boundary
* @throws IOException
*/
- protected void doHandleSimpleMultipartFileUpload(Request request,
- Response response, StreamVariable streamVariable,
- String variableName, VariableOwner owner, String boundary)
+ protected void doHandleSimpleMultipartFileUpload(WrappedRequest request,
+ WrappedResponse response, StreamVariable streamVariable,
+ String variableName, Connector owner, String boundary)
throws IOException {
// multipart parsing, supports only one file for request, but that is
// fine for our current terminal
@@ -489,9 +298,10 @@ public abstract class AbstractCommunicationManager implements
* @param contentLength
* @throws IOException
*/
- protected void doHandleXhrFilePost(Request request, Response response,
- StreamVariable streamVariable, String variableName,
- VariableOwner owner, int contentLength) throws IOException {
+ protected void doHandleXhrFilePost(WrappedRequest request,
+ WrappedResponse response, StreamVariable streamVariable,
+ String variableName, Connector owner, int contentLength)
+ throws IOException {
// These are unknown in filexhr ATM, maybe add to Accept header that
// is accessible in portlets
@@ -628,17 +438,6 @@ public abstract class AbstractCommunicationManager implements
}
}
- static void tryToCloseStream(InputStream in) {
- try {
- // try to close output stream (e.g. file handle)
- if (in != null) {
- in.close();
- }
- } catch (IOException e1) {
- // NOP
- }
- }
-
/**
* Removes any possible path information from the filename and returns the
* filename. Separators / and \\ are used.
@@ -661,8 +460,8 @@ public abstract class AbstractCommunicationManager implements
* @param response
* @throws IOException
*/
- protected void sendUploadResponse(Request request, Response response)
- throws IOException {
+ protected void sendUploadResponse(WrappedRequest request,
+ WrappedResponse response) throws IOException {
response.setContentType("text/html");
final OutputStream out = response.getOutputStream();
final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
@@ -676,7 +475,7 @@ public abstract class AbstractCommunicationManager implements
* Internally process a UIDL request from the client.
*
* This method calls
- * {@link #handleVariables(Request, Response, Callback, Application, Window)}
+ * {@link #handleVariables(WrappedRequest, WrappedResponse, Callback, Application, Root)}
* to process any changes to variables by the client and then repaints
* affected components using {@link #paintAfterVariableChanges()}.
*
@@ -690,18 +489,18 @@ public abstract class AbstractCommunicationManager implements
* @param request
* @param response
* @param callback
- * @param window
+ * @param root
* target window for the UIDL request, can be null if target not
* found
* @throws IOException
* @throws InvalidUIDLSecurityKeyException
*/
- protected void doHandleUidlRequest(Request request, Response response,
- Callback callback, Window window) throws IOException,
- InvalidUIDLSecurityKeyException {
+ public void handleUidlRequest(WrappedRequest request,
+ WrappedResponse response, Callback callback, Root root)
+ throws IOException, InvalidUIDLSecurityKeyException {
requestThemeName = request.getParameter("theme");
- maxInactiveInterval = request.getSession().getMaxInactiveInterval();
+ maxInactiveInterval = request.getSessionMaxInactiveInterval();
// repaint requested or session has timed out and new one is created
boolean repaintAll;
final OutputStream out;
@@ -718,8 +517,8 @@ public abstract class AbstractCommunicationManager implements
if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) {
String pid = request
.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT);
- highLightedPaintable = idPaintableMap.get(pid);
- highlightPaintable(highLightedPaintable);
+ highlightedConnector = root.getApplication().getConnector(pid);
+ highlightConnector(highlightedConnector);
}
}
@@ -734,11 +533,10 @@ public abstract class AbstractCommunicationManager implements
// Finds the window within the application
if (application.isRunning()) {
// Returns if no window found
- if (window == null) {
+ if (root == null) {
// This should not happen, no windows exists but
// application is still open.
- logger.warning("Could not get window for application with request ID "
- + request.getRequestID());
+ logger.warning("Could not get root for application");
return;
}
} else {
@@ -748,8 +546,7 @@ public abstract class AbstractCommunicationManager implements
}
// Change all variables based on request parameters
- if (!handleVariables(request, response, callback, application,
- window)) {
+ if (!handleVariables(request, response, callback, application, root)) {
// var inconsistency; the client is probably out-of-sync
SystemMessages ci = null;
@@ -780,27 +577,36 @@ public abstract class AbstractCommunicationManager implements
}
paintAfterVariableChanges(request, response, callback, repaintAll,
- outWriter, window, analyzeLayouts);
-
- if (closingWindowName != null) {
- currentlyOpenWindowsInClient.remove(closingWindowName);
- closingWindowName = null;
- }
+ outWriter, root, analyzeLayouts);
+ postPaint(root);
}
outWriter.close();
requestThemeName = null;
}
- protected void highlightPaintable(Paintable highLightedPaintable2) {
+ /**
+ * Method called after the paint phase while still being synchronized on the
+ * application
+ *
+ * @param root
+ *
+ */
+ protected void postPaint(Root root) {
+ // Remove connectors that have been detached from the application during
+ // handling of the request
+ root.getApplication().cleanConnectorMap();
+ }
+
+ protected void highlightConnector(Connector highlightedConnector) {
StringBuilder sb = new StringBuilder();
sb.append("*** Debug details of a component: *** \n");
sb.append("Type: ");
- sb.append(highLightedPaintable2.getClass().getName());
- if (highLightedPaintable2 instanceof AbstractComponent) {
- AbstractComponent component = (AbstractComponent) highLightedPaintable2;
+ sb.append(highlightedConnector.getClass().getName());
+ if (highlightedConnector instanceof AbstractComponent) {
+ AbstractComponent component = (AbstractComponent) highlightedConnector;
sb.append("\nId:");
- sb.append(paintableIdMap.get(component));
+ sb.append(highlightedConnector.getConnectorId());
if (component.getCaption() != null) {
sb.append("\nCaption:");
sb.append(component.getCaption());
@@ -864,14 +670,10 @@ public abstract class AbstractCommunicationManager implements
* @throws PaintException
* @throws IOException
*/
- private void paintAfterVariableChanges(Request request, Response response,
- Callback callback, boolean repaintAll, final PrintWriter outWriter,
- Window window, boolean analyzeLayouts) throws PaintException,
- IOException {
-
- if (repaintAll) {
- makeAllPaintablesDirty(window);
- }
+ private void paintAfterVariableChanges(WrappedRequest request,
+ WrappedResponse response, Callback callback, boolean repaintAll,
+ final PrintWriter outWriter, Root root, boolean analyzeLayouts)
+ throws PaintException, IOException {
// Removes application if it has stopped during variable changes
if (!application.isRunning()) {
@@ -886,180 +688,242 @@ public abstract class AbstractCommunicationManager implements
.getAttribute(WRITE_SECURITY_TOKEN_FLAG);
if (writeSecurityTokenFlag != null) {
- String seckey = (String) request.getSession().getAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
- if (seckey == null) {
- seckey = UUID.randomUUID().toString();
- request.getSession().setAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID, seckey);
- }
- outWriter.print("\"" + ApplicationConnection.UIDL_SECURITY_TOKEN_ID
- + "\":\"");
- outWriter.print(seckey);
- outWriter.print("\",");
+ outWriter.print(getSecurityKeyUIDL(request));
}
- // If the browser-window has been closed - we do not need to paint it at
- // all
- if (window.getName().equals(closingWindowName)) {
- outWriter.print("\"changes\":[]");
- } else {
- // re-get window - may have been changed
- Window newWindow = doGetApplicationWindow(request, callback,
- application, window);
- if (newWindow != window) {
- window = newWindow;
- repaintAll = true;
- }
-
- writeUidlResponse(request, callback, repaintAll, outWriter, window,
- analyzeLayouts);
+ writeUidlResponse(request, repaintAll, outWriter, root, analyzeLayouts);
- }
closeJsonMessage(outWriter);
outWriter.close();
}
- public void writeUidlResponse(Request request, Callback callback,
- boolean repaintAll, final PrintWriter outWriter, Window window,
- boolean analyzeLayouts) throws PaintException {
- outWriter.print("\"changes\":[");
-
- ArrayList<Paintable> paintables = null;
-
- List<InvalidLayout> invalidComponentRelativeSizes = null;
+ /**
+ * Gets the security key (and generates one if needed) as UIDL.
+ *
+ * @param request
+ * @return the security key UIDL or "" if the feature is turned off
+ */
+ public String getSecurityKeyUIDL(WrappedRequest request) {
+ final String seckey = getSecurityKey(request);
+ if (seckey != null) {
+ return "\"" + ApplicationConnection.UIDL_SECURITY_TOKEN_ID
+ + "\":\"" + seckey + "\",";
+ } else {
+ return "";
+ }
+ }
- JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter,
- !repaintAll);
- OpenWindowCache windowCache = currentlyOpenWindowsInClient.get(window
- .getName());
- if (windowCache == null) {
- windowCache = new OpenWindowCache();
- currentlyOpenWindowsInClient.put(window.getName(), windowCache);
+ /**
+ * Gets the security key (and generates one if needed).
+ *
+ * @param request
+ * @return the security key
+ */
+ protected String getSecurityKey(WrappedRequest request) {
+ String seckey = null;
+ seckey = (String) request
+ .getSessionAttribute(ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
+ if (seckey == null) {
+ seckey = UUID.randomUUID().toString();
+ request.setSessionAttribute(
+ ApplicationConnection.UIDL_SECURITY_TOKEN_ID, seckey);
}
+ return seckey;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void writeUidlResponse(WrappedRequest request, boolean repaintAll,
+ final PrintWriter outWriter, Root root, boolean analyzeLayouts)
+ throws PaintException {
+ ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
+ Application application = root.getApplication();
// Paints components
+ DirtyConnectorTracker rootConnectorTracker = root
+ .getDirtyConnectorTracker();
+ logger.log(Level.FINE, "* Creating response to client");
if (repaintAll) {
- paintables = new ArrayList<Paintable>();
- paintables.add(window);
+ getClientCache(root).clear();
+ rootConnectorTracker.markAllComponentsDirty();
// Reset sent locales
locales = null;
requireLocale(application.getLocale().toString());
+ }
- } else {
- // remove detached components from paintableIdMap so they
- // can be GC'ed
- /*
- * TODO figure out if we could move this beyond the painting phase,
- * "respond as fast as possible, then do the cleanup". Beware of
- * painting the dirty detatched components.
- */
- for (Iterator<Paintable> it = paintableIdMap.keySet().iterator(); it
- .hasNext();) {
- Component p = (Component) it.next();
- if (p.getApplication() == null) {
- unregisterPaintable(p);
- // Take into account that some other component may have
- // reused p's ID by now (this can happen when manually
- // assigning IDs with setDebugId().) See #8090.
- String pid = paintableIdMap.get(p);
- if (idPaintableMap.get(pid) == p) {
- idPaintableMap.remove(pid);
- }
- it.remove();
- dirtyPaintables.remove(p);
+ dirtyVisibleConnectors
+ .addAll(getDirtyVisibleComponents(rootConnectorTracker));
+
+ logger.log(Level.FINE, "Found " + dirtyVisibleConnectors.size()
+ + " dirty connectors to paint");
+ for (ClientConnector connector : dirtyVisibleConnectors) {
+ if (connector instanceof Component) {
+ ((Component) connector).updateState();
+ }
+ }
+ rootConnectorTracker.markAllComponentsClean();
+
+ outWriter.print("\"changes\":[");
+
+ List<InvalidLayout> invalidComponentRelativeSizes = null;
+
+ JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter,
+ !repaintAll);
+ legacyPaint(paintTarget, dirtyVisibleConnectors);
+
+ if (analyzeLayouts) {
+ invalidComponentRelativeSizes = ComponentSizeValidator
+ .validateComponentRelativeSizes(root.getContent(), null,
+ null);
+
+ // Also check any existing subwindows
+ if (root.getWindows() != null) {
+ for (Window subWindow : root.getWindows()) {
+ invalidComponentRelativeSizes = ComponentSizeValidator
+ .validateComponentRelativeSizes(
+ subWindow.getContent(),
+ invalidComponentRelativeSizes, null);
}
}
- paintables = getDirtyVisibleComponents(window);
- }
- if (paintables != null) {
-
- // We need to avoid painting children before parent.
- // This is ensured by ordering list by depth in component
- // tree
- Collections.sort(paintables, new Comparator<Paintable>() {
- public int compare(Paintable o1, Paintable o2) {
- Component c1 = (Component) o1;
- Component c2 = (Component) o2;
- int d1 = 0;
- while (c1.getParent() != null) {
- d1++;
- c1 = c1.getParent();
- }
- int d2 = 0;
- while (c2.getParent() != null) {
- d2++;
- c2 = c2.getParent();
- }
- if (d1 < d2) {
- return -1;
- }
- if (d1 > d2) {
- return 1;
- }
- return 0;
+ }
+
+ paintTarget.close();
+ outWriter.print("], "); // close changes
+
+ // send shared state to client
+
+ // for now, send the complete state of all modified and new
+ // components
+
+ // Ideally, all this would be sent before "changes", but that causes
+ // complications with legacy components that create sub-components
+ // in their paint phase. Nevertheless, this will be processed on the
+ // client after component creation but before legacy UIDL
+ // processing.
+ JSONObject sharedStates = new JSONObject();
+ for (Connector connector : dirtyVisibleConnectors) {
+ SharedState state = connector.getState();
+ if (null != state) {
+ // encode and send shared state
+ try {
+ // FIXME Use declared type
+ JSONArray stateJsonArray = JsonCodec.encode(state,
+ state.getClass(), application);
+ sharedStates
+ .put(connector.getConnectorId(), stateJsonArray);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to serialize shared state for connector "
+ + connector.getClass().getName() + " ("
+ + connector.getConnectorId() + "): "
+ + e.getMessage());
}
- });
+ }
+ }
+ outWriter.print("\"state\":");
+ outWriter.append(sharedStates.toString());
+ outWriter.print(", "); // close states
- for (final Iterator<Paintable> i = paintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
+ // TODO This should be optimized. The type only needs to be
+ // sent once for each connector id + on refresh. Use the same cache as
+ // widget mapping
- // TODO CLEAN
- if (p instanceof Window) {
- final Window w = (Window) p;
- if (w.getTerminal() == null) {
- w.setTerminal(application.getMainWindow().getTerminal());
+ JSONObject connectorTypes = new JSONObject();
+ for (ClientConnector connector : dirtyVisibleConnectors) {
+ String connectorType = paintTarget.getTag(connector);
+ try {
+ connectorTypes.put(connector.getConnectorId(), connectorType);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to send connector type for connector "
+ + connector.getConnectorId() + ": "
+ + e.getMessage());
+ }
+ }
+ outWriter.print("\"types\":");
+ outWriter.append(connectorTypes.toString());
+ outWriter.print(", "); // close states
+
+ // Send update hierarchy information to the client.
+
+ // This could be optimized aswell to send only info if hierarchy has
+ // actually changed. Much like with the shared state. Note though
+ // that an empty hierarchy is information aswell (e.g. change from 1
+ // child to 0 children)
+
+ outWriter.print("\"hierarchy\":");
+
+ JSONObject hierarchyInfo = new JSONObject();
+ for (Connector connector : dirtyVisibleConnectors) {
+ if (connector instanceof HasComponents) {
+ HasComponents parent = (HasComponents) connector;
+ String parentConnectorId = parent.getConnectorId();
+ JSONArray children = new JSONArray();
+
+ for (Component child : getChildComponents(parent)) {
+ if (isVisible(child)) {
+ children.put(child.getConnectorId());
}
}
- /*
- * This does not seem to happen in tk5, but remember this case:
- * else if (p instanceof Component) { if (((Component)
- * p).getParent() == null || ((Component) p).getApplication() ==
- * null) { // Component requested repaint, but is no // longer
- * attached: skip paintablePainted(p); continue; } }
- */
+ try {
+ hierarchyInfo.put(parentConnectorId, children);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to send hierarchy information about "
+ + parentConnectorId + " to the client: "
+ + e.getMessage());
+ }
+ }
+ }
+ outWriter.append(hierarchyInfo.toString());
+ outWriter.print(", "); // close hierarchy
- // TODO we may still get changes that have been
- // rendered already (changes with only cached flag)
- if (paintTarget.needsToBePainted(p)) {
- paintTarget.startTag("change");
- paintTarget.addAttribute("format", "uidl");
- final String pid = getPaintableId(p);
- paintTarget.addAttribute("pid", pid);
+ // send server to client RPC calls for components in the root, in call
+ // order
- p.paint(paintTarget);
+ // collect RPC calls from components in the root in the order in
+ // which they were performed, remove the calls from components
- paintTarget.endTag("change");
- }
- paintablePainted(p);
+ LinkedList<ClientConnector> rpcPendingQueue = new LinkedList<ClientConnector>(
+ dirtyVisibleConnectors);
+ List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(dirtyVisibleConnectors);
- if (analyzeLayouts) {
- Window w = (Window) p;
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(w.getContent(),
- null, null);
-
- // Also check any existing subwindows
- if (w.getChildWindows() != null) {
- for (Window subWindow : w.getChildWindows()) {
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(
- subWindow.getContent(),
- invalidComponentRelativeSizes, null);
- }
- }
+ JSONArray rpcCalls = new JSONArray();
+ for (ClientMethodInvocation invocation : pendingInvocations) {
+ // add invocation to rpcCalls
+ try {
+ JSONArray invocationJson = new JSONArray();
+ invocationJson.put(invocation.getConnector().getConnectorId());
+ invocationJson.put(invocation.getInterfaceName());
+ invocationJson.put(invocation.getMethodName());
+ JSONArray paramJson = new JSONArray();
+ for (int i = 0; i < invocation.getParameterTypes().length; ++i) {
+ paramJson.put(JsonCodec.encode(
+ invocation.getParameters()[i],
+ invocation.getParameterTypes()[i], application));
}
+ invocationJson.put(paramJson);
+ rpcCalls.put(invocationJson);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to serialize RPC method call parameters for connector "
+ + invocation.getConnector().getConnectorId()
+ + " method " + invocation.getInterfaceName()
+ + "." + invocation.getMethodName() + ": "
+ + e.getMessage());
}
+
}
- paintTarget.close();
- outWriter.print("]"); // close changes
+ if (rpcCalls.length() > 0) {
+ outWriter.print("\"rpc\" : ");
+ outWriter.append(rpcCalls.toString());
+ outWriter.print(", "); // close rpc
+ }
- outWriter.print(", \"meta\" : {");
+ outWriter.print("\"meta\" : {");
boolean metaOpen = false;
if (repaintAll) {
@@ -1081,11 +945,11 @@ public abstract class AbstractCommunicationManager implements
}
outWriter.write("]");
}
- if (highLightedPaintable != null) {
+ if (highlightedConnector != null) {
outWriter.write(", \"hl\":\"");
- outWriter.write(paintableIdMap.get(highLightedPaintable));
+ outWriter.write(highlightedConnector.getConnectorId());
outWriter.write("\"");
- highLightedPaintable = null;
+ highlightedConnector = null;
}
}
@@ -1140,8 +1004,7 @@ public abstract class AbstractCommunicationManager implements
final String resource = (String) i.next();
InputStream is = null;
try {
- is = callback.getThemeResourceAsStream(getTheme(window),
- resource);
+ is = getThemeResourceAsStream(root, getTheme(root), resource);
} catch (final Exception e) {
// FIXME: Handle exception
logger.log(Level.FINER, "Failed to get theme resource stream.",
@@ -1175,11 +1038,13 @@ public abstract class AbstractCommunicationManager implements
}
outWriter.print("}");
- Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget
- .getUsedPaintableTypes();
+ Collection<Class<? extends ClientConnector>> usedClientConnectors = paintTarget
+ .getUsedClientConnectors();
boolean typeMappingsOpen = false;
- for (Class<? extends Paintable> class1 : usedPaintableTypes) {
- if (windowCache.cache(class1)) {
+ ClientCache clientCache = getClientCache(root);
+
+ for (Class<? extends ClientConnector> class1 : usedClientConnectors) {
+ if (clientCache.cache(class1)) {
// client does not know the mapping key for this type, send
// mapping to client
if (!typeMappingsOpen) {
@@ -1199,6 +1064,32 @@ public abstract class AbstractCommunicationManager implements
outWriter.print(" }");
}
+ boolean typeInheritanceMapOpen = false;
+ if (typeMappingsOpen) {
+ // send the whole type inheritance map if any new mappings
+ for (Class<? extends ClientConnector> class1 : usedClientConnectors) {
+ if (!ClientConnector.class.isAssignableFrom(class1
+ .getSuperclass())) {
+ continue;
+ }
+ if (!typeInheritanceMapOpen) {
+ typeInheritanceMapOpen = true;
+ outWriter.print(", \"typeInheritanceMap\" : { ");
+ } else {
+ outWriter.print(" , ");
+ }
+ outWriter.print("\"");
+ outWriter.print(getTagForType(class1));
+ outWriter.print("\" : ");
+ outWriter
+ .print(getTagForType((Class<? extends ClientConnector>) class1
+ .getSuperclass()));
+ }
+ if (typeInheritanceMapOpen) {
+ outWriter.print(" }");
+ }
+ }
+
// add any pending locale definitions requested by the client
printLocaleDeclarations(outWriter);
@@ -1213,7 +1104,7 @@ public abstract class AbstractCommunicationManager implements
* Adds the performance timing data used by TestBench 3 to the UIDL
* response.
*/
- private void writePerformanceDataForTestBench(final Request request,
+ private void writePerformanceDataForTestBench(final WrappedRequest request,
final PrintWriter outWriter) {
Long totalTime = (Long) request.getAttribute("TOTAL");
Long lastRequestTime = (Long) request.getAttribute("LASTREQUEST");
@@ -1221,12 +1112,174 @@ public abstract class AbstractCommunicationManager implements
lastRequestTime));
}
+ private void legacyPaint(PaintTarget paintTarget,
+ ArrayList<ClientConnector> dirtyVisibleConnectors)
+ throws PaintException {
+ List<Vaadin6Component> legacyComponents = new ArrayList<Vaadin6Component>();
+ for (Connector connector : dirtyVisibleConnectors) {
+ // All Components that want to use paintContent must implement
+ // Vaadin6Component
+ if (connector instanceof Vaadin6Component) {
+ legacyComponents.add((Vaadin6Component) connector);
+ }
+ }
+ sortByHierarchy((List) legacyComponents);
+ for (Vaadin6Component c : legacyComponents) {
+ logger.fine("Painting Vaadin6Component " + c.getClass().getName()
+ + "@" + Integer.toHexString(c.hashCode()));
+ paintTarget.startTag("change");
+ final String pid = c.getConnectorId();
+ paintTarget.addAttribute("pid", pid);
+ LegacyPaint.paint(c, paintTarget);
+ paintTarget.endTag("change");
+ }
+
+ }
+
+ private void sortByHierarchy(List<Component> paintables) {
+ // Vaadin 6 requires parents to be painted before children as component
+ // containers rely on that their updateFromUIDL method has been called
+ // before children start calling e.g. updateCaption
+ Collections.sort(paintables, new Comparator<Component>() {
+ public int compare(Component c1, Component c2) {
+ int depth1 = 0;
+ while (c1.getParent() != null) {
+ depth1++;
+ c1 = c1.getParent();
+ }
+ int depth2 = 0;
+ while (c2.getParent() != null) {
+ depth2++;
+ c2 = c2.getParent();
+ }
+ if (depth1 < depth2) {
+ return -1;
+ }
+ if (depth1 > depth2) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+
+ }
+
+ private ClientCache getClientCache(Root root) {
+ Integer rootId = Integer.valueOf(root.getRootId());
+ ClientCache cache = rootToClientCache.get(rootId);
+ if (cache == null) {
+ cache = new ClientCache();
+ rootToClientCache.put(rootId, cache);
+ }
+ return cache;
+ }
+
+ /**
+ * Checks if the component is visible in context, i.e. returns false if the
+ * child is hidden, the parent is hidden or the parent says the child should
+ * not be rendered (using
+ * {@link HasComponents#isComponentVisible(Component)}
+ *
+ * @param child
+ * The child to check
+ * @return true if the child is visible to the client, false otherwise
+ */
+ static boolean isVisible(Component child) {
+ HasComponents parent = child.getParent();
+ if (parent == null || !child.isVisible()) {
+ return child.isVisible();
+ }
+
+ return parent.isComponentVisible(child) && isVisible(parent);
+ }
+
+ private static class NullIterator<E> implements Iterator<E> {
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public E next() {
+ return null;
+ }
+
+ public void remove() {
+ }
+
+ }
+
+ public static Iterable<Component> getChildComponents(HasComponents cc) {
+ // TODO This must be moved to Root/Panel
+ if (cc instanceof Root) {
+ Root root = (Root) cc;
+ List<Component> children = new ArrayList<Component>();
+ if (root.getContent() != null) {
+ children.add(root.getContent());
+ }
+ for (Window w : root.getWindows()) {
+ children.add(w);
+ }
+ return children;
+ } else if (cc instanceof Panel) {
+ // This is so wrong.. (#2924)
+ if (((Panel) cc).getContent() == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singleton((Component) ((Panel) cc)
+ .getContent());
+ }
+ }
+ return cc;
+ }
+
+ /**
+ * Collects all pending RPC calls from listed {@link ClientConnector}s and
+ * clears their RPC queues.
+ *
+ * @param rpcPendingQueue
+ * list of {@link ClientConnector} of interest
+ * @return ordered list of pending RPC calls
+ */
+ private List<ClientMethodInvocation> collectPendingRpcCalls(
+ List<ClientConnector> rpcPendingQueue) {
+ List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ for (ClientConnector connector : rpcPendingQueue) {
+ List<ClientMethodInvocation> paintablePendingRpc = connector
+ .retrievePendingRpcCalls();
+ if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) {
+ List<ClientMethodInvocation> oldPendingRpc = pendingInvocations;
+ int totalCalls = pendingInvocations.size()
+ + paintablePendingRpc.size();
+ pendingInvocations = new ArrayList<ClientMethodInvocation>(
+ totalCalls);
+
+ // merge two ordered comparable lists
+ for (int destIndex = 0, oldIndex = 0, paintableIndex = 0; destIndex < totalCalls; destIndex++) {
+ if (paintableIndex >= paintablePendingRpc.size()
+ || (oldIndex < oldPendingRpc.size() && ((Comparable<ClientMethodInvocation>) oldPendingRpc
+ .get(oldIndex))
+ .compareTo(paintablePendingRpc
+ .get(paintableIndex)) <= 0)) {
+ pendingInvocations.add(oldPendingRpc.get(oldIndex++));
+ } else {
+ pendingInvocations.add(paintablePendingRpc
+ .get(paintableIndex++));
+ }
+ }
+ }
+ }
+ return pendingInvocations;
+ }
+
+ protected abstract InputStream getThemeResourceAsStream(Root root,
+ String themeName, String resource);
+
private int getTimeoutInterval() {
return maxInactiveInterval;
}
- private String getTheme(Window window) {
- String themeName = window.getTheme();
+ private String getTheme(Root root) {
+ String themeName = root.getApplication().getThemeForRoot(root);
String requestThemeName = getRequestTheme();
if (requestThemeName != null) {
@@ -1242,32 +1295,16 @@ public abstract class AbstractCommunicationManager implements
return requestThemeName;
}
- public void makeAllPaintablesDirty(Window window) {
- // If repaint is requested, clean all ids in this root window
- for (final Iterator<String> it = idPaintableMap.keySet().iterator(); it
- .hasNext();) {
- final Component c = (Component) idPaintableMap.get(it.next());
- if (isChildOf(window, c)) {
- it.remove();
- paintableIdMap.remove(c);
- }
- }
- // clean WindowCache
- OpenWindowCache openWindowCache = currentlyOpenWindowsInClient
- .get(window.getName());
- if (openWindowCache != null) {
- openWindowCache.clear();
- }
- }
-
/**
- * Called when communication manager stops listening for repaints for given
- * component.
+ * Returns false if the cross site request forgery protection is turned off.
*
- * @param p
+ * @param application
+ * @return false if the XSRF is turned off, true otherwise
*/
- protected void unregisterPaintable(Component p) {
- p.removeListener(this);
+ public boolean isXSRFEnabled(Application application) {
+ return !"true"
+ .equals(application
+ .getProperty(AbstractApplicationServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION));
}
/**
@@ -1279,9 +1316,10 @@ public abstract class AbstractCommunicationManager implements
*
* @return true if successful, false if there was an inconsistency
*/
- private boolean handleVariables(Request request, Response response,
- Callback callback, Application application2, Window window)
- throws IOException, InvalidUIDLSecurityKeyException {
+ private boolean handleVariables(WrappedRequest request,
+ WrappedResponse response, Callback callback,
+ Application application2, Root root) throws IOException,
+ InvalidUIDLSecurityKeyException {
boolean success = true;
String changes = getRequestPayload(request);
@@ -1293,9 +1331,7 @@ public abstract class AbstractCommunicationManager implements
// Security: double cookie submission pattern unless disabled by
// property
- if (!"true"
- .equals(application2
- .getProperty(AbstractApplicationServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION))) {
+ if (isXSRFEnabled(application2)) {
if (bursts.length == 1 && "init".equals(bursts[0])) {
// init request; don't handle any variables, key sent in
// response.
@@ -1304,8 +1340,8 @@ public abstract class AbstractCommunicationManager implements
} else {
// ApplicationServlet has stored the security token in the
// session; check that it matched the one sent in the UIDL
- String sessId = (String) request.getSession().getAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
+ String sessId = (String) request
+ .getSessionAttribute(ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
if (sessId == null || !sessId.equals(bursts[0])) {
throw new InvalidUIDLSecurityKeyException(
@@ -1316,9 +1352,9 @@ public abstract class AbstractCommunicationManager implements
}
for (int bi = 1; bi < bursts.length; bi++) {
- final String burst = bursts[bi];
- success = handleVariableBurst(request, application2, success,
- burst);
+ // unescape any encoded separator characters in the burst
+ final String burst = unescapeBurst(bursts[bi]);
+ success &= handleBurst(request, application2, burst);
// In case that there were multiple bursts, we know that this is
// a special synchronous case for closing window. Thus we are
@@ -1333,7 +1369,7 @@ public abstract class AbstractCommunicationManager implements
new CharArrayWriter());
paintAfterVariableChanges(request, response, callback,
- true, outWriter, window, false);
+ true, outWriter, root, false);
}
@@ -1345,127 +1381,250 @@ public abstract class AbstractCommunicationManager implements
* we don't have the required logic implemented on the server side. E.g.
* a component is removed in a previous burst.
*/
- return success || closingWindowName != null;
+ return success;
}
- public boolean handleVariableBurst(Object source, Application app,
- boolean success, final String burst) {
- // extract variables to two dim string array
- final String[] tmp = burst.split(String.valueOf(VAR_RECORD_SEPARATOR));
- final String[][] variableRecords = new String[tmp.length][4];
- for (int i = 0; i < tmp.length; i++) {
- variableRecords[i] = tmp[i].split(String
- .valueOf(VAR_FIELD_SEPARATOR));
- }
-
- for (int i = 0; i < variableRecords.length; i++) {
- String[] variable = variableRecords[i];
- String[] nextVariable = null;
- if (i + 1 < variableRecords.length) {
- nextVariable = variableRecords[i + 1];
+ /**
+ * Processes a message burst received from the client.
+ *
+ * A burst can contain any number of RPC calls, including legacy variable
+ * change calls that are processed separately.
+ *
+ * Consecutive changes to the value of the same variable are combined and
+ * changeVariables() is only called once for them. This preserves the Vaadin
+ * 6 semantics for components and add-ons that do not use Vaadin 7 RPC
+ * directly.
+ *
+ * @param source
+ * @param app
+ * application receiving the burst
+ * @param burst
+ * the content of the burst as a String to be parsed
+ * @return true if the processing of the burst was successful and there were
+ * no messages to non-existent components
+ */
+ public boolean handleBurst(Object source, Application app,
+ final String burst) {
+ boolean success = true;
+ try {
+ Set<Connector> enabledConnectors = new HashSet<Connector>();
+
+ List<MethodInvocation> invocations = parseInvocations(burst);
+ for (MethodInvocation invocation : invocations) {
+ final ClientConnector connector = getConnector(app,
+ invocation.getConnectorId());
+
+ if (connector != null && connector.isConnectorEnabled()) {
+ enabledConnectors.add(connector);
+ }
}
- final VariableOwner owner = getVariableOwner(variable[VAR_PID]);
- if (owner != null && owner.isEnabled()) {
- Map<String, Object> m;
- if (nextVariable != null
- && variable[VAR_PID].equals(nextVariable[VAR_PID])) {
- // we have more than one value changes in row for
- // one variable owner, collect them in HashMap
- m = new HashMap<String, Object>();
- m.put(variable[VAR_NAME],
- convertVariableValue(variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
- } else {
- // use optimized single value map
- m = Collections.singletonMap(
- variable[VAR_NAME],
- convertVariableValue(variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
+ for (int i = 0; i < invocations.size(); i++) {
+ MethodInvocation invocation = invocations.get(i);
+
+ final ClientConnector connector = getConnector(app,
+ invocation.getConnectorId());
+
+ if (connector == null) {
+ logger.log(
+ Level.WARNING,
+ "RPC call to " + invocation.getInterfaceName()
+ + "." + invocation.getMethodName()
+ + " received for connector "
+ + invocation.getConnectorId()
+ + " but no such connector could be found");
+ continue;
}
- // collect following variable changes for this owner
- while (nextVariable != null
- && variable[VAR_PID].equals(nextVariable[VAR_PID])) {
- i++;
- variable = nextVariable;
- if (i + 1 < variableRecords.length) {
- nextVariable = variableRecords[i + 1];
- } else {
- nextVariable = null;
- }
- m.put(variable[VAR_NAME],
- convertVariableValue(variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
- }
- try {
- changeVariables(source, owner, m);
-
- // Special-case of closing browser-level windows:
- // track browser-windows currently open in client
- if (owner instanceof Window
- && ((Window) owner).getParent() == null) {
- final Boolean close = (Boolean) m.get("close");
- if (close != null && close.booleanValue()) {
- closingWindowName = ((Window) owner).getName();
+ if (!enabledConnectors.contains(connector)) {
+
+ if (invocation instanceof LegacyChangeVariablesInvocation) {
+ LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation;
+ // TODO convert window close to a separate RPC call and
+ // handle above - not a variable change
+
+ // Handle special case where window-close is called
+ // after the window has been removed from the
+ // application or the application has closed
+ Map<String, Object> changes = legacyInvocation
+ .getVariableChanges();
+ if (changes.size() == 1 && changes.containsKey("close")
+ && Boolean.TRUE.equals(changes.get("close"))) {
+ // Silently ignore this
+ continue;
}
}
- } catch (Exception e) {
- Component errorComponent = null;
- if (owner instanceof Component) {
- errorComponent = (Component) owner;
- } else if (owner instanceof DragAndDropService) {
- if (m.get("dhowner") instanceof Component) {
- errorComponent = (Component) m.get("dhowner");
+
+ // Connector is disabled, log a warning and move to the next
+ String msg = "Ignoring RPC call for disabled connector "
+ + connector.getClass().getName();
+ if (connector instanceof Component) {
+ String caption = ((Component) connector).getCaption();
+ if (caption != null) {
+ msg += ", caption=" + caption;
}
}
-
- handleChangeVariablesError(app, errorComponent, e, m);
- }
- } else {
-
- // Handle special case where window-close is called
- // after the window has been removed from the
- // application or the application has closed
- if ("close".equals(variable[VAR_NAME])
- && "true".equals(variable[VAR_VALUE])) {
- // Silently ignore this
+ logger.warning(msg);
continue;
}
- // Ignore variable change
- String msg = "Warning: Ignoring variable change for ";
- if (owner != null) {
- msg += "disabled component " + owner.getClass();
- String caption = ((Component) owner).getCaption();
- if (caption != null) {
- msg += ", caption=" + caption;
- }
+ if (invocation instanceof ServerRpcMethodInvocation) {
+ ServerRpcManager.applyInvocation(connector,
+ (ServerRpcMethodInvocation) invocation);
} else {
- msg += "non-existent component, VAR_PID="
- + variable[VAR_PID];
- success = false;
+
+ // All code below is for legacy variable changes
+ LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation;
+ Map<String, Object> changes = legacyInvocation
+ .getVariableChanges();
+ try {
+ changeVariables(source, (VariableOwner) connector,
+ changes);
+ } catch (Exception e) {
+ Component errorComponent = null;
+ if (connector instanceof Component) {
+ errorComponent = (Component) connector;
+ } else if (connector instanceof DragAndDropService) {
+ Object dropHandlerOwner = changes.get("dhowner");
+ if (dropHandlerOwner instanceof Component) {
+ errorComponent = (Component) dropHandlerOwner;
+ }
+ }
+ handleChangeVariablesError(app, errorComponent, e,
+ changes);
+
+ }
}
- logger.warning(msg);
- continue;
}
+
+ } catch (JSONException e) {
+ logger.warning("Unable to parse RPC call from the client: "
+ + e.getMessage());
+ // TODO or return success = false?
+ throw new RuntimeException(e);
}
+
return success;
}
+ /**
+ * Parse a message burst from the client into a list of MethodInvocation
+ * instances.
+ *
+ * @param burst
+ * message string (JSON)
+ * @return list of MethodInvocation to perform
+ * @throws JSONException
+ */
+ private List<MethodInvocation> parseInvocations(final String burst)
+ throws JSONException {
+ JSONArray invocationsJson = new JSONArray(burst);
+
+ ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
+
+ MethodInvocation previousInvocation = null;
+ // parse JSON to MethodInvocations
+ for (int i = 0; i < invocationsJson.length(); ++i) {
+
+ JSONArray invocationJson = invocationsJson.getJSONArray(i);
+
+ MethodInvocation invocation = parseInvocation(invocationJson,
+ previousInvocation);
+ if (invocation != null) {
+ // Can be null iff the invocation was a legacy invocation and it
+ // was merged with the previous one
+ invocations.add(invocation);
+ previousInvocation = invocation;
+ }
+ }
+ return invocations;
+ }
+
+ private MethodInvocation parseInvocation(JSONArray invocationJson,
+ MethodInvocation previousInvocation) throws JSONException {
+ String connectorId = invocationJson.getString(0);
+ String interfaceName = invocationJson.getString(1);
+ String methodName = invocationJson.getString(2);
+
+ JSONArray parametersJson = invocationJson.getJSONArray(3);
+
+ if (LegacyChangeVariablesInvocation.isLegacyVariableChange(
+ interfaceName, methodName)) {
+ if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) {
+ previousInvocation = null;
+ }
+
+ return parseLegacyChangeVariablesInvocation(connectorId,
+ interfaceName, methodName,
+ (LegacyChangeVariablesInvocation) previousInvocation,
+ parametersJson);
+ } else {
+ return parseServerRpcInvocation(connectorId, interfaceName,
+ methodName, parametersJson);
+ }
+
+ }
+
+ private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation(
+ String connectorId, String interfaceName, String methodName,
+ LegacyChangeVariablesInvocation previousInvocation,
+ JSONArray parametersJson) throws JSONException {
+ if (parametersJson.length() != 2) {
+ throw new JSONException(
+ "Invalid parameters in legacy change variables call. Expected 2, was "
+ + parametersJson.length());
+ }
+ String variableName = (String) JsonCodec
+ .decodeInternalType(String.class, true,
+ parametersJson.getJSONArray(0), application);
+ Object value = JsonCodec.decodeInternalType(
+ parametersJson.getJSONArray(1), application);
+
+ if (previousInvocation != null
+ && previousInvocation.getConnectorId().equals(connectorId)) {
+ previousInvocation.setVariableChange(variableName, value);
+ return null;
+ } else {
+ return new LegacyChangeVariablesInvocation(connectorId,
+ variableName, value);
+ }
+ }
+
+ private ServerRpcMethodInvocation parseServerRpcInvocation(
+ String connectorId, String interfaceName, String methodName,
+ JSONArray parametersJson) throws JSONException {
+ ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
+ connectorId, interfaceName, methodName, parametersJson.length());
+
+ Object[] parameters = new Object[parametersJson.length()];
+ Type[] declaredRpcMethodParameterTypes = invocation.getMethod()
+ .getGenericParameterTypes();
+
+ for (int j = 0; j < parametersJson.length(); ++j) {
+ JSONArray parameterJson = parametersJson.getJSONArray(j);
+ Type parameterType = declaredRpcMethodParameterTypes[j];
+ parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
+ parameterJson, application);
+ }
+ invocation.setParameters(parameters);
+ return invocation;
+ }
+
protected void changeVariables(Object source, final VariableOwner owner,
Map<String, Object> m) {
owner.changeVariables(source, m);
}
- protected VariableOwner getVariableOwner(String string) {
- VariableOwner owner = (VariableOwner) idPaintableMap.get(string);
- if (owner == null && string.startsWith("DD")) {
+ protected ClientConnector getConnector(Application app, String connectorId) {
+ ClientConnector c = app.getConnector(connectorId);
+ if (c == null
+ && connectorId.equals(getDragAndDropService().getConnectorId())) {
return getDragAndDropService();
}
- return owner;
+
+ return c;
}
- private VariableOwner getDragAndDropService() {
+ private DragAndDropService getDragAndDropService() {
if (dragAndDropService == null) {
dragAndDropService = new DragAndDropService(this);
}
@@ -1480,7 +1639,8 @@ public abstract class AbstractCommunicationManager implements
* @return
* @throws IOException
*/
- protected String getRequestPayload(Request request) throws IOException {
+ protected String getRequestPayload(WrappedRequest request)
+ throws IOException {
int requestLength = request.getContentLength();
if (requestLength == 0) {
@@ -1544,7 +1704,7 @@ public abstract class AbstractCommunicationManager implements
if (owner instanceof AbstractField) {
try {
- handled = ((AbstractField) owner).handleError(errorEvent);
+ handled = ((AbstractField<?>) owner).handleError(errorEvent);
} catch (Exception handlerException) {
/*
* If there is an error in the component error handler we pass
@@ -1563,111 +1723,15 @@ public abstract class AbstractCommunicationManager implements
}
- private Object convertVariableValue(char variableType, String strValue) {
- Object val = null;
- switch (variableType) {
- case VTYPE_ARRAY:
- val = convertArray(strValue);
- break;
- case VTYPE_MAP:
- val = convertMap(strValue);
- break;
- case VTYPE_STRINGARRAY:
- val = convertStringArray(strValue);
- break;
- case VTYPE_STRING:
- // decode encoded separators
- val = decodeVariableValue(strValue);
- break;
- case VTYPE_INTEGER:
- val = Integer.valueOf(strValue);
- break;
- case VTYPE_LONG:
- val = Long.valueOf(strValue);
- break;
- case VTYPE_FLOAT:
- val = Float.valueOf(strValue);
- break;
- case VTYPE_DOUBLE:
- val = Double.valueOf(strValue);
- break;
- case VTYPE_BOOLEAN:
- val = Boolean.valueOf(strValue);
- break;
- case VTYPE_PAINTABLE:
- val = idPaintableMap.get(strValue);
- break;
- }
-
- return val;
- }
-
- private Object convertMap(String strValue) {
- String[] parts = strValue
- .split(String.valueOf(VAR_ARRAYITEM_SEPARATOR));
- HashMap<String, Object> map = new HashMap<String, Object>();
- for (int i = 0; i < parts.length; i += 2) {
- String key = parts[i];
- if (key.length() > 0) {
- char variabletype = key.charAt(0);
- // decode encoded separators
- String decodedValue = decodeVariableValue(parts[i + 1]);
- String decodedKey = decodeVariableValue(key.substring(1));
- Object value = convertVariableValue(variabletype, decodedValue);
- map.put(decodedKey, value);
- }
- }
- return map;
- }
-
- private String[] convertStringArray(String strValue) {
- // need to return delimiters and filter them out; otherwise empty
- // strings are lost
- // an extra empty delimiter at the end is automatically eliminated
- final String arrayItemSeparator = String
- .valueOf(VAR_ARRAYITEM_SEPARATOR);
- StringTokenizer tokenizer = new StringTokenizer(strValue,
- arrayItemSeparator, true);
- List<String> tokens = new ArrayList<String>();
- String prevToken = arrayItemSeparator;
- while (tokenizer.hasMoreTokens()) {
- String token = tokenizer.nextToken();
- if (!arrayItemSeparator.equals(token)) {
- // decode encoded separators
- tokens.add(decodeVariableValue(token));
- } else if (arrayItemSeparator.equals(prevToken)) {
- tokens.add("");
- }
- prevToken = token;
- }
- return tokens.toArray(new String[tokens.size()]);
- }
-
- private Object convertArray(String strValue) {
- String[] val = strValue.split(String.valueOf(VAR_ARRAYITEM_SEPARATOR));
- if (val.length == 0 || (val.length == 1 && val[0].length() == 0)) {
- return new Object[0];
- }
- Object[] values = new Object[val.length];
- for (int i = 0; i < values.length; i++) {
- String string = val[i];
- // first char of string is type
- char variableType = string.charAt(0);
- values[i] = convertVariableValue(variableType, string.substring(1));
- }
- return values;
- }
-
/**
- * Decode encoded burst, record, field and array item separator characters
- * in a variable value String received from the client. This protects from
- * separator injection attacks.
+ * Unescape encoded burst separator characters in a burst received from the
+ * client. This protects from separator injection attacks.
*
* @param encodedValue
* to decode
* @return decoded value
*/
- protected String decodeVariableValue(String encodedValue) {
+ protected String unescapeBurst(String encodedValue) {
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(
encodedValue);
@@ -1681,9 +1745,6 @@ public abstract class AbstractCommunicationManager implements
result.append(VAR_ESCAPE_CHARACTER);
break;
case VAR_BURST_SEPARATOR + 0x30:
- case VAR_RECORD_SEPARATOR + 0x30:
- case VAR_FIELD_SEPARATOR + 0x30:
- case VAR_ARRAYITEM_SEPARATOR + 0x30:
// +0x30 makes these letters for easier reading
result.append((char) (character - 0x30));
break;
@@ -1843,108 +1904,6 @@ public abstract class AbstractCommunicationManager implements
}
/**
- * TODO New method - document me!
- *
- * @param request
- * @param callback
- * @param application
- * @param assumedWindow
- * @return
- */
- protected Window doGetApplicationWindow(Request request, Callback callback,
- Application application, Window assumedWindow) {
-
- Window window = null;
-
- // If the client knows which window to use, use it if possible
- String windowClientRequestedName = request.getParameter("windowName");
-
- if (assumedWindow != null
- && application.getWindows().contains(assumedWindow)) {
- windowClientRequestedName = assumedWindow.getName();
- }
- if (windowClientRequestedName != null) {
- window = application.getWindow(windowClientRequestedName);
- if (window != null) {
- return window;
- }
- }
-
- // If client does not know what window it wants
- if (window == null && !request.isRunningInPortlet()) {
- // This is only supported if the application is running inside a
- // servlet
-
- // Get the path from URL
- String path = callback.getRequestPathInfo(request);
-
- /*
- * If the path is specified, create name from it.
- *
- * An exception is if UIDL request have come this far. This happens
- * if main window is refreshed. In that case we always return main
- * window (infamous hacky support for refreshes if only main window
- * is used). However we are not returning with main window here (we
- * will later if things work right), because the code is so cryptic
- * that nobody really knows what it does.
- */
- boolean pathMayContainWindowName = path != null
- && path.length() > 0 && !path.equals("/");
- if (pathMayContainWindowName) {
- boolean uidlRequest = path.startsWith("/UIDL");
- if (!uidlRequest) {
- String windowUrlName = null;
- if (path.charAt(0) == '/') {
- path = path.substring(1);
- }
- final int index = path.indexOf('/');
- if (index < 0) {
- windowUrlName = path;
- path = "";
- } else {
- windowUrlName = path.substring(0, index);
- path = path.substring(index + 1);
- }
-
- window = application.getWindow(windowUrlName);
- }
- }
-
- }
-
- // By default, use mainwindow
- if (window == null) {
- window = application.getMainWindow();
- // Return null if no main window was found
- if (window == null) {
- return null;
- }
- }
-
- // If the requested window is already open, resolve conflict
- if (currentlyOpenWindowsInClient.containsKey(window.getName())) {
- String newWindowName = window.getName();
-
- synchronized (AbstractCommunicationManager.class) {
- while (currentlyOpenWindowsInClient.containsKey(newWindowName)) {
- newWindowName = window.getName() + "_"
- + nextUnusedWindowSuffix++;
- }
- }
-
- window = application.getWindow(newWindowName);
-
- // If everything else fails, use main window even in case of
- // conflicts
- if (window == null) {
- window = application.getMainWindow();
- }
- }
-
- return window;
- }
-
- /**
* Ends the Application.
*
* The browser is redirected to the Application logout URL set with
@@ -1960,8 +1919,9 @@ public abstract class AbstractCommunicationManager implements
* @throws IOException
* if the writing failed due to input/output error.
*/
- private void endApplication(Request request, Response response,
- Application application) throws IOException {
+ private void endApplication(WrappedRequest request,
+ WrappedResponse response, Application application)
+ throws IOException {
String logoutUrl = application.getLogoutURL();
if (logoutUrl == null) {
@@ -1992,7 +1952,8 @@ public abstract class AbstractCommunicationManager implements
* @param outWriter
* @param response
*/
- protected void openJsonMessage(PrintWriter outWriter, Response response) {
+ protected void openJsonMessage(PrintWriter outWriter,
+ WrappedResponse response) {
// Sets the response type
response.setContentType("application/json; charset=UTF-8");
// some dirt to prevent cross site scripting
@@ -2000,47 +1961,6 @@ public abstract class AbstractCommunicationManager implements
}
/**
- * Gets the Paintable Id. If Paintable has debug id set it will be used
- * prefixed with "PID_S". Otherwise a sequenced ID is created.
- *
- * @param paintable
- * @return the paintable Id.
- */
- public String getPaintableId(Paintable paintable) {
-
- String id = paintableIdMap.get(paintable);
- if (id == null) {
- // use testing identifier as id if set
- String debugId = paintable.getDebugId();
- if (debugId == null) {
- id = "PID" + idSequence++;
- } else {
- id = "PID_S" + debugId;
- for (int seq = 0;; ++seq) {
- // In case of a duplicate debug id, uniquify the PID by
- // inserting a sequential integer. Try successive numbers
- // until finding a PID that is either not used at all or
- // used by a detached component. See #5109.
- Paintable old = idPaintableMap.get(id);
- if (old == null
- || (old instanceof Component && ((Component) old)
- .getApplication() == null)) {
- break;
- }
- id = "PID_" + seq + "S" + debugId;
- }
- }
- idPaintableMap.put(id, paintable);
- paintableIdMap.put(paintable, id);
- }
- return id;
- }
-
- public boolean hasPaintableId(Paintable paintable) {
- return paintableIdMap.containsKey(paintable);
- }
-
- /**
* Returns dirty components which are in given window. Components in an
* invisible subtrees are omitted.
*
@@ -2048,110 +1968,16 @@ public abstract class AbstractCommunicationManager implements
* root window for which dirty components is to be fetched
* @return
*/
- private ArrayList<Paintable> getDirtyVisibleComponents(Window w) {
- final ArrayList<Paintable> resultset = new ArrayList<Paintable>(
- dirtyPaintables);
-
- // The following algorithm removes any components that would be painted
- // as a direct descendant of other components from the dirty components
- // list. The result is that each component should be painted exactly
- // once and any unmodified components will be painted as "cached=true".
-
- for (final Iterator<Paintable> i = dirtyPaintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
- if (p instanceof Component) {
- final Component component = (Component) p;
- if (component.getApplication() == null) {
- // component is detached after requestRepaint is called
- resultset.remove(p);
- i.remove();
- } else {
- Window componentsRoot = component.getWindow();
- if (componentsRoot == null) {
- // This should not happen unless somebody has overriden
- // getApplication or getWindow in an illegal way.
- throw new IllegalStateException(
- "component.getWindow() returned null for a component attached to the application");
- }
- if (componentsRoot.getParent() != null) {
- // this is a subwindow
- componentsRoot = componentsRoot.getParent();
- }
- if (componentsRoot != w) {
- resultset.remove(p);
- } else if (component.getParent() != null
- && !component.getParent().isVisible()) {
- /*
- * Do not return components in an invisible subtree.
- *
- * Components that are invisible in visible subree, must
- * be rendered (to let client know that they need to be
- * hidden).
- */
- resultset.remove(p);
- }
- }
+ private ArrayList<Component> getDirtyVisibleComponents(
+ DirtyConnectorTracker dirtyConnectorTracker) {
+ ArrayList<Component> dirtyComponents = new ArrayList<Component>();
+ for (Component c : dirtyConnectorTracker.getDirtyComponents()) {
+ if (isVisible(c)) {
+ dirtyComponents.add(c);
}
}
- return resultset;
- }
-
- /**
- * @see com.vaadin.terminal.Paintable.RepaintRequestListener#repaintRequested(com.vaadin.terminal.Paintable.RepaintRequestEvent)
- */
- public void repaintRequested(RepaintRequestEvent event) {
- final Paintable p = event.getPaintable();
- if (!dirtyPaintables.contains(p)) {
- dirtyPaintables.add(p);
- }
- }
-
- /**
- * Internally mark a {@link Paintable} as painted and start collecting new
- * repaint requests for it.
- *
- * @param paintable
- */
- private void paintablePainted(Paintable paintable) {
- dirtyPaintables.remove(paintable);
- paintable.requestRepaintRequests();
- }
-
- /**
- * Implementation of {@link URIHandler.ErrorEvent} interface.
- */
- public class URIHandlerErrorImpl implements URIHandler.ErrorEvent,
- Serializable {
-
- private final URIHandler owner;
-
- private final Throwable throwable;
-
- /**
- *
- * @param owner
- * @param throwable
- */
- private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
- this.owner = owner;
- this.throwable = throwable;
- }
-
- /**
- * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return throwable;
- }
-
- /**
- * @see com.vaadin.terminal.URIHandler.ErrorEvent#getURIHandler()
- */
- public URIHandler getURIHandler() {
- return owner;
- }
+ return dirtyComponents;
}
/**
@@ -2196,23 +2022,6 @@ public abstract class AbstractCommunicationManager implements
}
}
- /**
- * Helper method to test if a component contains another
- *
- * @param parent
- * @param child
- */
- private static boolean isChildOf(Component parent, Component child) {
- Component p = child.getParent();
- while (p != null) {
- if (parent == p) {
- return true;
- }
- p = p.getParent();
- }
- return false;
- }
-
protected class InvalidUIDLSecurityKeyException extends
GeneralSecurityException {
@@ -2222,87 +2031,19 @@ public abstract class AbstractCommunicationManager implements
}
- /**
- * Calls the Window URI handler for a request and returns the
- * {@link DownloadStream} returned by the handler.
- *
- * If the window is the main window of an application, the (deprecated)
- * {@link Application#handleURI(java.net.URL, String)} is called first to
- * handle {@link ApplicationResource}s, and the window handler is only
- * called if it returns null.
- *
- * @param window
- * the target window of the request
- * @param request
- * the request instance
- * @param response
- * the response to write to
- * @return DownloadStream if the request was handled and further processing
- * should be suppressed, null otherwise.
- * @see com.vaadin.terminal.URIHandler
- */
- @SuppressWarnings("deprecation")
- protected DownloadStream handleURI(Window window, Request request,
- Response response, Callback callback) {
-
- String uri = callback.getRequestPathInfo(request);
-
- // If no URI is available
- if (uri == null) {
- uri = "";
- } else {
- // Removes the leading /
- while (uri.startsWith("/") && uri.length() > 0) {
- uri = uri.substring(1);
- }
- }
-
- // Handles the uri
- try {
- URL context = application.getURL();
- if (window == application.getMainWindow()) {
- DownloadStream stream = null;
- /*
- * Application.handleURI run first. Handles possible
- * ApplicationResources.
- */
- stream = application.handleURI(context, uri);
- if (stream == null) {
- stream = window.handleURI(context, uri);
- }
- return stream;
- } else {
- // Resolve the prefix end index
- final int index = uri.indexOf('/');
- if (index > 0) {
- String prefix = uri.substring(0, index);
- URL windowContext;
- windowContext = new URL(context, prefix + "/");
- final String windowUri = (uri.length() > prefix.length() + 1) ? uri
- .substring(prefix.length() + 1) : "";
- return window.handleURI(windowContext, windowUri);
- } else {
- return null;
- }
- }
-
- } catch (final Throwable t) {
- application.getErrorHandler().terminalError(
- new URIHandlerErrorImpl(application, t));
- return null;
- }
- }
-
- private final HashMap<Class<? extends Paintable>, Integer> typeToKey = new HashMap<Class<? extends Paintable>, Integer>();
+ private final HashMap<Class<? extends ClientConnector>, Integer> typeToKey = new HashMap<Class<? extends ClientConnector>, Integer>();
private int nextTypeKey = 0;
- String getTagForType(Class<? extends Paintable> class1) {
- Integer object = typeToKey.get(class1);
- if (object == null) {
- object = nextTypeKey++;
- typeToKey.put(class1, object);
+ private BootstrapHandler bootstrapHandler;
+
+ String getTagForType(Class<? extends ClientConnector> class1) {
+ Integer id = typeToKey.get(class1);
+ if (id == null) {
+ id = nextTypeKey++;
+ typeToKey.put(class1, id);
+ logger.log(Level.FINE, "Mapping " + class1.getName() + " to " + id);
}
- return object.toString();
+ return id.toString();
}
/**
@@ -2311,7 +2052,7 @@ public abstract class AbstractCommunicationManager implements
*
* TODO make customlayout templates (from theme) to be cached here.
*/
- class OpenWindowCache implements Serializable {
+ class ClientCache implements Serializable {
private final Set<Object> res = new HashSet<Object>();
@@ -2330,10 +2071,116 @@ public abstract class AbstractCommunicationManager implements
}
- abstract String getStreamVariableTargetUrl(VariableOwner owner,
- String name, StreamVariable value);
+ abstract String getStreamVariableTargetUrl(Connector owner, String name,
+ StreamVariable value);
- abstract protected void cleanStreamVariable(VariableOwner owner, String name);
+ abstract protected void cleanStreamVariable(Connector owner, String name);
+
+ /**
+ * Gets the bootstrap handler that should be used for generating the pages
+ * bootstrapping applications for this communication manager.
+ *
+ * @return the bootstrap handler to use
+ */
+ private BootstrapHandler getBootstrapHandler() {
+ if (bootstrapHandler == null) {
+ bootstrapHandler = createBootstrapHandler();
+ }
+
+ return bootstrapHandler;
+ }
+
+ protected abstract BootstrapHandler createBootstrapHandler();
+
+ protected boolean handleApplicationRequest(WrappedRequest request,
+ WrappedResponse response) throws IOException {
+ return application.handleRequest(request, response);
+ }
+
+ public void handleBrowserDetailsRequest(WrappedRequest request,
+ WrappedResponse response, Application application)
+ throws IOException {
+
+ // if we do not yet have a currentRoot, it should be initialized
+ // shortly, and we should send the initial UIDL
+ boolean sendUIDL = Root.getCurrentRoot() == null;
+
+ try {
+ CombinedRequest combinedRequest = new CombinedRequest(request);
+
+ Root root = application.getRootForRequest(combinedRequest);
+ response.setContentType("application/json; charset=UTF-8");
+
+ // Use the same logic as for determined roots
+ BootstrapHandler bootstrapHandler = getBootstrapHandler();
+ BootstrapContext context = bootstrapHandler.createContext(
+ combinedRequest, response, application, root.getRootId());
+
+ String widgetset = context.getWidgetsetName();
+ String theme = context.getThemeName();
+ String themeUri = bootstrapHandler.getThemeUri(context, theme);
+
+ // TODO These are not required if it was only the init of the root
+ // that was delayed
+ JSONObject params = new JSONObject();
+ params.put("widgetset", widgetset);
+ params.put("themeUri", themeUri);
+ // Root id might have changed based on e.g. window.name
+ params.put(ApplicationConnection.ROOT_ID_PARAMETER,
+ root.getRootId());
+ if (sendUIDL) {
+ String initialUIDL = getInitialUIDL(combinedRequest, root);
+ params.put("uidl", initialUIDL);
+ }
+
+ // NOTE! GateIn requires, for some weird reason, getOutputStream
+ // to be used instead of getWriter() (it seems to interpret
+ // application/json as a binary content type)
+ final OutputStream out = response.getOutputStream();
+ final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
+ new OutputStreamWriter(out, "UTF-8")));
+
+ outWriter.write(params.toString());
+ // NOTE GateIn requires the buffers to be flushed to work
+ outWriter.flush();
+ out.flush();
+ } catch (RootRequiresMoreInformationException e) {
+ // Requiring more information at this point is not allowed
+ // TODO handle in a better way
+ throw new RuntimeException(e);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Generates the initial UIDL message that can e.g. be included in a html
+ * page to avoid a separate round trip just for getting the UIDL.
+ *
+ * @param request
+ * the request that caused the initialization
+ * @param root
+ * the root for which the UIDL should be generated
+ * @return a string with the initial UIDL message
+ * @throws PaintException
+ * if an exception occurs while painting
+ */
+ protected String getInitialUIDL(WrappedRequest request, Root root)
+ throws PaintException {
+ // TODO maybe unify writeUidlResponse()?
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+ pWriter.print("{");
+ if (isXSRFEnabled(root.getApplication())) {
+ pWriter.print(getSecurityKeyUIDL(request));
+ }
+ writeUidlResponse(request, true, pWriter, root, false);
+ pWriter.print("}");
+ String initialUIDL = sWriter.toString();
+ logger.log(Level.FINE, "Initial UIDL:" + initialUIDL);
+ return initialUIDL;
+ }
/**
* Stream that extracts content from another stream until the boundary
@@ -2484,4 +2331,5 @@ public abstract class AbstractCommunicationManager implements
return b;
}
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/ApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/ApplicationPortlet.java
deleted file mode 100644
index f971bcbc90..0000000000
--- a/src/com/vaadin/terminal/gwt/server/ApplicationPortlet.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.server;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.portlet.ActionRequest;
-import javax.portlet.ActionResponse;
-import javax.portlet.PortalContext;
-import javax.portlet.Portlet;
-import javax.portlet.PortletConfig;
-import javax.portlet.PortletException;
-import javax.portlet.PortletRequestDispatcher;
-import javax.portlet.PortletSession;
-import javax.portlet.RenderRequest;
-import javax.portlet.RenderResponse;
-
-import com.liferay.portal.kernel.util.PropsUtil;
-import com.vaadin.Application;
-
-/**
- * Portlet main class for Portlet 1.0 (JSR-168) portlets which consist of a
- * portlet and a servlet. For Portlet 2.0 (JSR-286, no servlet required), use
- * {@link ApplicationPortlet2} instead.
- */
-@SuppressWarnings("serial")
-public class ApplicationPortlet implements Portlet, Serializable {
- // portlet configuration parameters
- private static final String PORTLET_PARAMETER_APPLICATION = "application";
- private static final String PORTLET_PARAMETER_STYLE = "style";
- private static final String PORTLET_PARAMETER_WIDGETSET = "widgetset";
-
- // The application to show
- protected String app = null;
- // some applications might require forced height (and, more seldom, width)
- protected String style = null; // e.g "height:500px;"
- // force the portlet to use this widgetset - portlet level setting
- protected String portletWidgetset = null;
-
- public void destroy() {
-
- }
-
- public void init(PortletConfig config) throws PortletException {
- app = config.getInitParameter(PORTLET_PARAMETER_APPLICATION);
- if (app == null) {
- throw new PortletException(
- "No porlet application url defined in portlet.xml. Define the '"
- + PORTLET_PARAMETER_APPLICATION
- + "' init parameter to be the servlet deployment path.");
- }
- style = config.getInitParameter(PORTLET_PARAMETER_STYLE);
- // enable forcing the selection of the widgetset in portlet
- // configuration for a single portlet (backwards compatibility)
- portletWidgetset = config.getInitParameter(PORTLET_PARAMETER_WIDGETSET);
- }
-
- public void processAction(ActionRequest request, ActionResponse response)
- throws PortletException, IOException {
- PortletApplicationContext.dispatchRequest(this, request, response);
- }
-
- public void render(RenderRequest request, RenderResponse response)
- throws PortletException, IOException {
-
- // display the Vaadin application
- writeAjaxWindow(request, response);
- }
-
- protected void writeAjaxWindow(RenderRequest request,
- RenderResponse response) throws IOException {
-
- response.setContentType("text/html");
- if (app != null) {
- PortletSession sess = request.getPortletSession();
- PortletApplicationContext ctx = PortletApplicationContext
- .getApplicationContext(sess);
-
- PortletRequestDispatcher dispatcher = sess.getPortletContext()
- .getRequestDispatcher("/" + app);
-
- try {
- // portal-wide settings
- PortalContext portalCtx = request.getPortalContext();
-
- boolean isLifeRay = portalCtx.getPortalInfo().toLowerCase()
- .contains("liferay");
-
- request.setAttribute(ApplicationServlet.REQUEST_FRAGMENT,
- "true");
-
- // fixed base theme to use - all portal pages with Vaadin
- // applications will load this exactly once
- String portalTheme = getPortalProperty(
- Constants.PORTAL_PARAMETER_VAADIN_THEME, portalCtx);
-
- String portalWidgetset = getPortalProperty(
- Constants.PORTAL_PARAMETER_VAADIN_WIDGETSET, portalCtx);
-
- // location of the widgetset(s) and default theme (to which
- // /VAADIN/widgetsets/...
- // is appended)
- String portalResourcePath = getPortalProperty(
- Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH,
- portalCtx);
-
- if (portalResourcePath != null) {
- // if portalResourcePath is defined, set it as a request
- // parameter which will override the default location in
- // servlet
- request.setAttribute(
- ApplicationServlet.REQUEST_VAADIN_STATIC_FILE_PATH,
- portalResourcePath);
- }
-
- // - if the user has specified a widgetset for this portlet, use
- // it from the portlet (not fully supported)
- // - otherwise, if specified, use the portal-wide widgetset
- // and widgetset path settings (recommended)
- // - finally, default to use the default widgetset if nothing
- // else is found
- if (portletWidgetset != null) {
- request.setAttribute(ApplicationServlet.REQUEST_WIDGETSET,
- portletWidgetset);
- }
- if (portalWidgetset != null) {
- request.setAttribute(
- ApplicationServlet.REQUEST_SHARED_WIDGETSET,
- portalWidgetset);
- }
-
- if (style != null) {
- request.setAttribute(ApplicationServlet.REQUEST_APPSTYLE,
- style);
- }
-
- // portalTheme is only used if the shared portal resource
- // directory is defined
- if (portalTheme != null && portalResourcePath != null) {
- request.setAttribute(
- ApplicationServlet.REQUEST_DEFAULT_THEME,
- portalTheme);
-
- String defaultThemeUri = null;
- defaultThemeUri = portalResourcePath + "/"
- + AbstractApplicationServlet.THEME_DIRECTORY_PATH
- + portalTheme;
- /*
- * Make sure portal default Vaadin theme is included in DOM.
- * Vaadin portlet themes do not "inherit" base theme, so we
- * need to force loading of the common base theme.
- */
- OutputStream out = response.getPortletOutputStream();
-
- // Using portal-wide theme
- String loadDefaultTheme = ("<script type=\"text/javascript\">\n"
- + "if(!vaadin) { var vaadin = {} } \n"
- + "if(!vaadin.themesLoaded) { vaadin.themesLoaded = {} } \n"
- + "if(!vaadin.themesLoaded['"
- + portalTheme
- + "']) {\n"
- + "var stylesheet = document.createElement('link');\n"
- + "stylesheet.setAttribute('rel', 'stylesheet');\n"
- + "stylesheet.setAttribute('type', 'text/css');\n"
- + "stylesheet.setAttribute('href', '"
- + defaultThemeUri
- + "/styles.css');\n"
- + "document.getElementsByTagName('head')[0].appendChild(stylesheet);\n"
- + "vaadin.themesLoaded['"
- + portalTheme
- + "'] = true;\n}\n" + "</script>\n");
- out.write(loadDefaultTheme.getBytes());
- }
-
- dispatcher.include(request, response);
-
- if (isLifeRay) {
- /*
- * Temporary support to heartbeat Liferay session when using
- * Vaadin based portlet. We hit an extra xhr to liferay
- * servlet to extend the session lifetime after each Vaadin
- * request. This hack can be removed when supporting portlet
- * 2.0 and resourceRequests.
- *
- * TODO make this configurable, this is not necessary with
- * some custom session configurations.
- */
- OutputStream out = response.getPortletOutputStream();
-
- String lifeRaySessionHearbeatHack = ("<script type=\"text/javascript\">"
- + "if(!vaadin.postRequestHooks) {"
- + " vaadin.postRequestHooks = {};"
- + "}"
- + "vaadin.postRequestHooks.liferaySessionHeartBeat = function() {"
- + " if (Liferay && Liferay.Session && Liferay.Session.setCookie) {"
- + " Liferay.Session.setCookie();"
- + " }"
- + "};" + "</script>");
- out.write(lifeRaySessionHearbeatHack.getBytes());
- }
-
- } catch (PortletException e) {
- PrintWriter out = response.getWriter();
- out.print("<h1>Servlet include failed!</h1>");
- Logger.getLogger(AbstractApplicationPortlet.class.getName())
- .log(Level.WARNING, "Servlet include failed", e);
- ctx.setPortletApplication(this, null);
- return;
- }
-
- Application app = (Application) request
- .getAttribute(Application.class.getName());
- ctx.setPortletApplication(this, app);
- ctx.firePortletRenderRequest(this, request, response);
-
- }
- }
-
- private String getPortalProperty(String name, PortalContext context) {
- boolean isLifeRay = context.getPortalInfo().toLowerCase()
- .contains("liferay");
-
- // TODO test on non-LifeRay platforms
-
- String value;
- if (isLifeRay) {
- value = getLifeRayPortalProperty(name);
- } else {
- value = context.getProperty(name);
- }
-
- return value;
- }
-
- private String getLifeRayPortalProperty(String name) {
- String value;
- try {
- value = PropsUtil.get(name);
- } catch (Exception e) {
- value = null;
- }
- return value;
- }
-}
diff --git a/src/com/vaadin/terminal/gwt/server/ApplicationPortlet2.java b/src/com/vaadin/terminal/gwt/server/ApplicationPortlet2.java
index 3e15c1cf8f..7a46a07e6c 100644
--- a/src/com/vaadin/terminal/gwt/server/ApplicationPortlet2.java
+++ b/src/com/vaadin/terminal/gwt/server/ApplicationPortlet2.java
@@ -8,6 +8,7 @@ import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import com.vaadin.Application;
+import com.vaadin.terminal.gwt.server.ServletPortletHelper.ApplicationClassException;
/**
* TODO Write documentation, fix JavaDoc tags.
@@ -18,23 +19,16 @@ public class ApplicationPortlet2 extends AbstractApplicationPortlet {
private Class<? extends Application> applicationClass;
- @SuppressWarnings("unchecked")
@Override
public void init(PortletConfig config) throws PortletException {
super.init(config);
- final String applicationClassName = config
- .getInitParameter("application");
- if (applicationClassName == null) {
- throw new PortletException(
- "Application not specified in portlet parameters");
- }
-
try {
- applicationClass = (Class<? extends Application>) getClassLoader()
- .loadClass(applicationClassName);
- } catch (final ClassNotFoundException e) {
- throw new PortletException("Failed to load application class: "
- + applicationClassName);
+ applicationClass = ServletPortletHelper.getApplicationClass(
+ config.getInitParameter("application"),
+ config.getInitParameter(Application.ROOT_PARAMETER),
+ getClassLoader());
+ } catch (ApplicationClassException e) {
+ throw new PortletException(e);
}
}
diff --git a/src/com/vaadin/terminal/gwt/server/ApplicationResourceHandler.java b/src/com/vaadin/terminal/gwt/server/ApplicationResourceHandler.java
new file mode 100644
index 0000000000..7cf66d5fcf
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ApplicationResourceHandler.java
@@ -0,0 +1,54 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletResponse;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.ApplicationResource;
+import com.vaadin.terminal.DownloadStream;
+import com.vaadin.terminal.RequestHandler;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+
+public class ApplicationResourceHandler implements RequestHandler {
+ private static final Pattern APP_RESOURCE_PATTERN = Pattern
+ .compile("^/?APP/(\\d+)/.*");
+
+ public boolean handleRequest(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException {
+ // Check for application resources
+ String requestPath = request.getRequestPathInfo();
+ if (requestPath == null) {
+ return false;
+ }
+ Matcher resourceMatcher = APP_RESOURCE_PATTERN.matcher(requestPath);
+
+ if (resourceMatcher.matches()) {
+ ApplicationResource resource = application
+ .getResource(resourceMatcher.group(1));
+ if (resource != null) {
+ DownloadStream stream = resource.getStream();
+ if (stream != null) {
+ stream.setCacheTime(resource.getCacheTime());
+ stream.writeTo(response);
+ return true;
+ }
+ }
+ // We get here if the url looks like an application resource but no
+ // resource can be served
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ request.getRequestPathInfo() + " can not be found");
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ApplicationRunnerServlet.java b/src/com/vaadin/terminal/gwt/server/ApplicationRunnerServlet.java
deleted file mode 100644
index a9eaa1bb82..0000000000
--- a/src/com/vaadin/terminal/gwt/server/ApplicationRunnerServlet.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.terminal.gwt.server;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.vaadin.Application;
-
-@SuppressWarnings("serial")
-public class ApplicationRunnerServlet extends AbstractApplicationServlet {
-
- private static final Logger logger = Logger
- .getLogger(ApplicationRunnerServlet.class.getName());
-
- /**
- * The name of the application class currently used. Only valid within one
- * request.
- */
- private String[] defaultPackages;
- private final ThreadLocal<HttpServletRequest> request = new ThreadLocal<HttpServletRequest>();
-
- @Override
- public void init(ServletConfig servletConfig) throws ServletException {
- super.init(servletConfig);
- String initParameter = servletConfig
- .getInitParameter("defaultPackages");
- if (initParameter != null) {
- defaultPackages = initParameter.split(",");
- }
- }
-
- @Override
- protected void service(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- this.request.set(request);
- super.service(request, response);
- this.request.set(null);
- }
-
- @Override
- protected URL getApplicationUrl(HttpServletRequest request)
- throws MalformedURLException {
- URL url = super.getApplicationUrl(request);
-
- String path = url.toString();
- path += getApplicationRunnerApplicationClassName(request);
- path += "/";
-
- return new URL(path);
- }
-
- @Override
- protected Application getNewApplication(HttpServletRequest request)
- throws ServletException {
-
- // Creates a new application instance
- try {
- final Application application = getApplicationClass().newInstance();
- return application;
- } catch (final IllegalAccessException e) {
- throw new ServletException(e);
- } catch (final InstantiationException e) {
- throw new ServletException(e);
- } catch (final ClassNotFoundException e) {
- throw new ServletException(
- new InstantiationException(
- "Failed to load application class: "
- + getApplicationRunnerApplicationClassName(request)));
- }
-
- }
-
- private String getApplicationRunnerApplicationClassName(
- HttpServletRequest request) {
- return getApplicationRunnerURIs(request).applicationClassname;
- }
-
- private static class URIS {
- String staticFilesPath;
- // String applicationURI;
- // String context;
- // String runner;
- String applicationClassname;
-
- }
-
- /**
- * Parses application runner URIs.
- *
- * If request URL is e.g.
- * http://localhost:8080/vaadin/run/com.vaadin.demo.Calc then
- * <ul>
- * <li>context=vaadin</li>
- * <li>Runner servlet=run</li>
- * <li>Vaadin application=com.vaadin.demo.Calc</li>
- * </ul>
- *
- * @param request
- * @return string array containing widgetset URI, application URI and
- * context, runner, application classname
- */
- private static URIS getApplicationRunnerURIs(HttpServletRequest request) {
- final String[] urlParts = request.getRequestURI().toString()
- .split("\\/");
- String context = null;
- // String runner = null;
- URIS uris = new URIS();
- String applicationClassname = null;
- String contextPath = request.getContextPath();
- if (urlParts[1].equals(contextPath.replaceAll("\\/", ""))) {
- // class name comes after web context and runner application
- context = urlParts[1];
- // runner = urlParts[2];
- if (urlParts.length == 3) {
- throw new IllegalArgumentException("No application specified");
- }
- applicationClassname = urlParts[3];
-
- uris.staticFilesPath = "/" + context;
- // uris.applicationURI = "/" + context + "/" + runner + "/"
- // + applicationClassname;
- // uris.context = context;
- // uris.runner = runner;
- uris.applicationClassname = applicationClassname;
- } else {
- // no context
- context = "";
- // runner = urlParts[1];
- if (urlParts.length == 2) {
- throw new IllegalArgumentException("No application specified");
- }
- applicationClassname = urlParts[2];
-
- uris.staticFilesPath = "/";
- // uris.applicationURI = "/" + runner + "/" + applicationClassname;
- // uris.context = context;
- // uris.runner = runner;
- uris.applicationClassname = applicationClassname;
- }
- return uris;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- protected Class<? extends Application> getApplicationClass()
- throws ClassNotFoundException {
- // TODO use getClassLoader() ?
-
- Class<? extends Application> appClass = null;
-
- String baseName = getApplicationRunnerApplicationClassName(request
- .get());
- try {
- appClass = (Class<? extends Application>) getClass()
- .getClassLoader().loadClass(baseName);
- return appClass;
- } catch (Exception e) {
- //
- if (defaultPackages != null) {
- for (int i = 0; i < defaultPackages.length; i++) {
- try {
- appClass = (Class<? extends Application>) getClass()
- .getClassLoader().loadClass(
- defaultPackages[i] + "." + baseName);
- } catch (ClassNotFoundException ee) {
- // Ignore as this is expected for many packages
- } catch (Exception e2) {
- // TODO: handle exception
- logger.log(
- Level.FINE,
- "Failed to find application class in the default package.",
- e2);
- }
- if (appClass != null) {
- return appClass;
- }
- }
- }
-
- }
-
- throw new ClassNotFoundException();
- }
-
- @Override
- protected String getRequestPathInfo(HttpServletRequest request) {
- String path = request.getPathInfo();
- if (path == null) {
- return null;
- }
-
- path = path.substring(1 + getApplicationRunnerApplicationClassName(
- request).length());
- return path;
- }
-
- @Override
- protected String getStaticFilesLocation(HttpServletRequest request) {
- URIS uris = getApplicationRunnerURIs(request);
- String staticFilesPath = uris.staticFilesPath;
- if (staticFilesPath.equals("/")) {
- staticFilesPath = "";
- }
-
- return staticFilesPath;
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/server/ApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/ApplicationServlet.java
index 8ad763f5d1..2c4d38ef24 100644
--- a/src/com/vaadin/terminal/gwt/server/ApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/ApplicationServlet.java
@@ -8,6 +8,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import com.vaadin.Application;
+import com.vaadin.terminal.gwt.server.ServletPortletHelper.ApplicationClassException;
/**
* This servlet connects a Vaadin Application to Web.
@@ -35,7 +36,6 @@ public class ApplicationServlet extends AbstractApplicationServlet {
* if an exception has occurred that interferes with the
* servlet's normal operation.
*/
- @SuppressWarnings("unchecked")
@Override
public void init(javax.servlet.ServletConfig servletConfig)
throws javax.servlet.ServletException {
@@ -44,20 +44,13 @@ public class ApplicationServlet extends AbstractApplicationServlet {
// Loads the application class using the same class loader
// as the servlet itself
- // Gets the application class name
- final String applicationClassName = servletConfig
- .getInitParameter("application");
- if (applicationClassName == null) {
- throw new ServletException(
- "Application not specified in servlet parameters");
- }
-
try {
- applicationClass = (Class<? extends Application>) getClassLoader()
- .loadClass(applicationClassName);
- } catch (final ClassNotFoundException e) {
- throw new ServletException("Failed to load application class: "
- + applicationClassName);
+ applicationClass = ServletPortletHelper.getApplicationClass(
+ servletConfig.getInitParameter("application"),
+ servletConfig.getInitParameter(Application.ROOT_PARAMETER),
+ getClassLoader());
+ } catch (ApplicationClassException e) {
+ throw new ServletException(e);
}
}
@@ -84,4 +77,4 @@ public class ApplicationServlet extends AbstractApplicationServlet {
throws ClassNotFoundException {
return applicationClass;
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
new file mode 100644
index 0000000000..84f87124d3
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
@@ -0,0 +1,602 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import com.vaadin.Application;
+import com.vaadin.RootRequiresMoreInformationException;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.RequestHandler;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.ui.Root;
+
+public abstract class BootstrapHandler implements RequestHandler {
+
+ protected class BootstrapContext implements Serializable {
+
+ private final WrappedResponse response;
+ private final WrappedRequest request;
+ private final Application application;
+ private final Integer rootId;
+
+ private Writer writer;
+ private Root root;
+ private String widgetsetName;
+ private String themeName;
+ private String appId;
+
+ private boolean rootFetched = false;
+
+ public BootstrapContext(WrappedResponse response,
+ WrappedRequest request, Application application, Integer rootId) {
+ this.response = response;
+ this.request = request;
+ this.application = application;
+ this.rootId = rootId;
+ }
+
+ public WrappedResponse getResponse() {
+ return response;
+ }
+
+ public WrappedRequest getRequest() {
+ return request;
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public Writer getWriter() throws IOException {
+ if (writer == null) {
+ response.setContentType("text/html");
+ writer = new BufferedWriter(new OutputStreamWriter(
+ response.getOutputStream(), "UTF-8"));
+ }
+ return writer;
+ }
+
+ public Integer getRootId() {
+ return rootId;
+ }
+
+ public Root getRoot() {
+ if (!rootFetched) {
+ root = Root.getCurrentRoot();
+ rootFetched = true;
+ }
+ return root;
+ }
+
+ public String getWidgetsetName() {
+ if (widgetsetName == null) {
+ Root root = getRoot();
+ if (root != null) {
+ widgetsetName = getWidgetsetForRoot(this);
+ }
+ }
+ return widgetsetName;
+ }
+
+ public String getThemeName() {
+ if (themeName == null) {
+ Root root = getRoot();
+ if (root != null) {
+ themeName = findAndEscapeThemeName(this);
+ }
+ }
+ return themeName;
+ }
+
+ public String getAppId() {
+ if (appId == null) {
+ appId = getApplicationId(this);
+ }
+ return appId;
+ }
+
+ }
+
+ public boolean handleRequest(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException {
+
+ // TODO Should all urls be handled here?
+ Integer rootId = null;
+ try {
+ Root root = application.getRootForRequest(request);
+ if (root == null) {
+ writeError(response, new Throwable("No Root found"));
+ return true;
+ }
+
+ rootId = Integer.valueOf(root.getRootId());
+ } catch (RootRequiresMoreInformationException e) {
+ // Just keep going without rootId
+ }
+
+ try {
+ writeBootstrapPage(request, response, application, rootId);
+ } catch (JSONException e) {
+ writeError(response, e);
+ }
+
+ return true;
+ }
+
+ protected final void writeBootstrapPage(WrappedRequest request,
+ WrappedResponse response, Application application, Integer rootId)
+ throws IOException, JSONException {
+
+ BootstrapContext context = createContext(request, response,
+ application, rootId);
+
+ DeploymentConfiguration deploymentConfiguration = request
+ .getDeploymentConfiguration();
+
+ boolean standalone = deploymentConfiguration.isStandalone(request);
+ if (standalone) {
+ setBootstrapPageHeaders(context);
+ writeBootstrapPageHtmlHeadStart(context);
+ writeBootstrapPageHtmlHeader(context);
+ writeBootstrapPageHtmlBodyStart(context);
+ }
+
+ // TODO include initial UIDL in the scripts?
+ writeBootstrapPageHtmlVaadinScripts(context);
+
+ writeBootstrapPageHtmlMainDiv(context);
+
+ Writer page = context.getWriter();
+ if (standalone) {
+ page.write("</body>\n</html>\n");
+ }
+
+ page.close();
+ }
+
+ public BootstrapContext createContext(WrappedRequest request,
+ WrappedResponse response, Application application, Integer rootId) {
+ BootstrapContext context = new BootstrapContext(response, request,
+ application, rootId);
+ return context;
+ }
+
+ protected String getMainDivStyle(BootstrapContext context) {
+ return null;
+ }
+
+ /**
+ * Creates and returns a unique ID for the DIV where the application is to
+ * be rendered.
+ *
+ * @param context
+ *
+ * @return the id to use in the DOM
+ */
+ protected abstract String getApplicationId(BootstrapContext context);
+
+ public String getWidgetsetForRoot(BootstrapContext context) {
+ Root root = context.getRoot();
+ WrappedRequest request = context.getRequest();
+
+ String widgetset = root.getApplication().getWidgetsetForRoot(root);
+ if (widgetset == null) {
+ widgetset = request.getDeploymentConfiguration()
+ .getConfiguredWidgetset(request);
+ }
+
+ widgetset = AbstractApplicationServlet.stripSpecialChars(widgetset);
+ return widgetset;
+ }
+
+ /**
+ * Method to write the div element into which that actual Vaadin application
+ * is rendered.
+ * <p>
+ * Override this method if you want to add some custom html around around
+ * the div element into which the actual Vaadin application will be
+ * rendered.
+ *
+ * @param context
+ *
+ * @throws IOException
+ */
+ protected void writeBootstrapPageHtmlMainDiv(BootstrapContext context)
+ throws IOException {
+ Writer page = context.getWriter();
+ String style = getMainDivStyle(context);
+
+ /*- Add classnames;
+ * .v-app
+ * .v-app-loading
+ * .v-app-<simpleName for app class>
+ *- Additionally added from javascript:
+ * .v-theme-<themeName, remove non-alphanum>
+ */
+
+ String appClass = "v-app-"
+ + getApplicationCSSClassName(context.getApplication());
+
+ String classNames = "v-app " + appClass;
+
+ if (style != null && style.length() != 0) {
+ style = " style=\"" + style + "\"";
+ }
+ page.write("<div id=\"" + context.getAppId() + "\" class=\""
+ + classNames + "\"" + style + ">");
+ page.write("<div class=\"v-app-loading\"></div>");
+ page.write("</div>\n");
+ page.write("<noscript>" + getNoScriptMessage() + "</noscript>");
+ }
+
+ /**
+ * Returns a message printed for browsers without scripting support or if
+ * browsers scripting support is disabled.
+ */
+ protected String getNoScriptMessage() {
+ return "You have to enable javascript in your browser to use an application built with Vaadin.";
+ }
+
+ /**
+ * Returns the application class identifier for use in the application CSS
+ * class name in the root DIV. The application CSS class name is of form
+ * "v-app-"+getApplicationCSSClassName().
+ *
+ * This method should normally not be overridden.
+ *
+ * @return The CSS class name to use in combination with "v-app-".
+ */
+ protected String getApplicationCSSClassName(Application application) {
+ return application.getClass().getSimpleName();
+ }
+
+ /**
+ *
+ * Method to open the body tag of the html kickstart page.
+ * <p>
+ * This method is responsible for closing the head tag and opening the body
+ * tag.
+ * <p>
+ * Override this method if you want to add some custom html to the page.
+ *
+ * @throws IOException
+ */
+ protected void writeBootstrapPageHtmlBodyStart(BootstrapContext context)
+ throws IOException {
+ Writer page = context.getWriter();
+ page.write("\n</head>\n<body scroll=\"auto\" class=\""
+ + ApplicationConnection.GENERATED_BODY_CLASSNAME + "\">\n");
+ }
+
+ /**
+ * Method to write the script part of the page which loads needed Vaadin
+ * scripts and themes.
+ * <p>
+ * Override this method if you want to add some custom html around scripts.
+ *
+ * @param context
+ *
+ * @throws IOException
+ * @throws JSONException
+ */
+ protected void writeBootstrapPageHtmlVaadinScripts(BootstrapContext context)
+ throws IOException, JSONException {
+ WrappedRequest request = context.getRequest();
+ Writer page = context.getWriter();
+
+ DeploymentConfiguration deploymentConfiguration = request
+ .getDeploymentConfiguration();
+ String staticFileLocation = deploymentConfiguration
+ .getStaticFileLocation(request);
+
+ page.write("<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" "
+ + "style=\"position:absolute;width:0;height:0;border:0;overflow:"
+ + "hidden;\" src=\"javascript:false\"></iframe>");
+
+ String bootstrapLocation = staticFileLocation
+ + "/VAADIN/vaadinBootstrap.js";
+ page.write("<script type=\"text/javascript\" src=\"");
+ page.write(bootstrapLocation);
+ page.write("\"></script>\n");
+
+ page.write("<script type=\"text/javascript\">\n");
+ page.write("//<![CDATA[\n");
+ page.write("if (!window.vaadin) alert("
+ + JSONObject.quote("Failed to load the bootstrap javascript: "
+ + bootstrapLocation) + ");\n");
+
+ writeMainScriptTagContents(context);
+ page.write("//]]>\n</script>\n");
+ }
+
+ protected void writeMainScriptTagContents(BootstrapContext context)
+ throws JSONException, IOException {
+ JSONObject defaults = getDefaultParameters(context);
+ JSONObject appConfig = getApplicationParameters(context);
+
+ boolean isDebug = !context.getApplication().isProductionMode();
+ Writer page = context.getWriter();
+
+ page.write("vaadin.setDefaults(");
+ printJsonObject(page, defaults, isDebug);
+ page.write(");\n");
+
+ page.write("vaadin.initApplication(\"");
+ page.write(context.getAppId());
+ page.write("\",");
+ printJsonObject(page, appConfig, isDebug);
+ page.write(");\n");
+ }
+
+ private static void printJsonObject(Writer page, JSONObject jsonObject,
+ boolean isDebug) throws IOException, JSONException {
+ if (isDebug) {
+ page.write(jsonObject.toString(4));
+ } else {
+ page.write(jsonObject.toString());
+ }
+ }
+
+ protected JSONObject getApplicationParameters(BootstrapContext context)
+ throws JSONException, PaintException {
+ Application application = context.getApplication();
+ Integer rootId = context.getRootId();
+
+ JSONObject appConfig = new JSONObject();
+
+ if (rootId != null) {
+ appConfig.put(ApplicationConnection.ROOT_ID_PARAMETER, rootId);
+ }
+
+ if (context.getThemeName() != null) {
+ appConfig.put("themeUri",
+ getThemeUri(context, context.getThemeName()));
+ }
+
+ JSONObject versionInfo = new JSONObject();
+ versionInfo.put("vaadinVersion", AbstractApplicationServlet.VERSION);
+ versionInfo.put("applicationVersion", application.getVersion());
+ appConfig.put("versionInfo", versionInfo);
+
+ appConfig.put("widgetset", context.getWidgetsetName());
+
+ if (rootId == null || application.isRootInitPending(rootId.intValue())) {
+ appConfig.put("initialPath", context.getRequest()
+ .getRequestPathInfo());
+
+ Map<String, String[]> parameterMap = context.getRequest()
+ .getParameterMap();
+ appConfig.put("initialParams", parameterMap);
+ } else {
+ // write the initial UIDL into the config
+ appConfig.put("uidl",
+ getInitialUIDL(context.getRequest(), context.getRoot()));
+ }
+
+ return appConfig;
+ }
+
+ protected JSONObject getDefaultParameters(BootstrapContext context)
+ throws JSONException {
+ JSONObject defaults = new JSONObject();
+
+ WrappedRequest request = context.getRequest();
+ Application application = context.getApplication();
+
+ // Get system messages
+ Application.SystemMessages systemMessages = AbstractApplicationServlet
+ .getSystemMessages(application.getClass());
+ if (systemMessages != null) {
+ // Write the CommunicationError -message to client
+ JSONObject comErrMsg = new JSONObject();
+ comErrMsg.put("caption",
+ systemMessages.getCommunicationErrorCaption());
+ comErrMsg.put("message",
+ systemMessages.getCommunicationErrorMessage());
+ comErrMsg.put("url", systemMessages.getCommunicationErrorURL());
+
+ defaults.put("comErrMsg", comErrMsg);
+
+ JSONObject authErrMsg = new JSONObject();
+ authErrMsg.put("caption",
+ systemMessages.getAuthenticationErrorCaption());
+ authErrMsg.put("message",
+ systemMessages.getAuthenticationErrorMessage());
+ authErrMsg.put("url", systemMessages.getAuthenticationErrorURL());
+
+ defaults.put("authErrMsg", authErrMsg);
+ }
+
+ DeploymentConfiguration deploymentConfiguration = request
+ .getDeploymentConfiguration();
+ String staticFileLocation = deploymentConfiguration
+ .getStaticFileLocation(request);
+ String widgetsetBase = staticFileLocation + "/"
+ + AbstractApplicationServlet.WIDGETSET_DIRECTORY_PATH;
+ defaults.put("widgetsetBase", widgetsetBase);
+
+ if (!application.isProductionMode()) {
+ defaults.put("debug", true);
+ }
+
+ if (deploymentConfiguration.isStandalone(request)) {
+ defaults.put("standalone", true);
+ }
+
+ defaults.put("appUri", getAppUri(context));
+
+ return defaults;
+ }
+
+ protected abstract String getAppUri(BootstrapContext context);
+
+ /**
+ * Method to write the contents of head element in html kickstart page.
+ * <p>
+ * Override this method if you want to add some custom html to the header of
+ * the page.
+ *
+ * @throws IOException
+ */
+ protected void writeBootstrapPageHtmlHeader(BootstrapContext context)
+ throws IOException {
+ Writer page = context.getWriter();
+ String themeName = context.getThemeName();
+
+ page.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n");
+
+ // Chrome frame in all versions of IE (only if Chrome frame is
+ // installed)
+ page.write("<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\"/>\n");
+
+ page.write("<style type=\"text/css\">"
+ + "html, body {height:100%;margin:0;}</style>");
+
+ // Add favicon links
+ if (themeName != null) {
+ String themeUri = getThemeUri(context, themeName);
+ page.write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\""
+ + themeUri + "/favicon.ico\" />");
+ page.write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\""
+ + themeUri + "/favicon.ico\" />");
+ }
+
+ Root root = context.getRoot();
+ String title = ((root == null || root.getCaption() == null) ? "Vaadin "
+ + AbstractApplicationServlet.VERSION_MAJOR : root.getCaption());
+
+ page.write("<title>"
+ + AbstractApplicationServlet.safeEscapeForHtml(title)
+ + "</title>");
+ }
+
+ /**
+ * Method to set http request headers for the Vaadin kickstart page.
+ * <p>
+ * Override this method if you need to customize http headers of the page.
+ *
+ * @param context
+ */
+ protected void setBootstrapPageHeaders(BootstrapContext context) {
+ WrappedResponse response = context.getResponse();
+
+ // Window renders are not cacheable
+ response.setHeader("Cache-Control", "no-cache");
+ response.setHeader("Pragma", "no-cache");
+ response.setDateHeader("Expires", 0);
+ response.setContentType("text/html; charset=UTF-8");
+ }
+
+ /**
+ * Method to write the beginning of the html page.
+ * <p>
+ * This method is responsible for writing appropriate doc type declarations
+ * and to open html and head tags.
+ * <p>
+ * Override this method if you want to add some custom html to the very
+ * beginning of the page.
+ *
+ * @param context
+ * @throws IOException
+ */
+ protected void writeBootstrapPageHtmlHeadStart(BootstrapContext context)
+ throws IOException {
+ Writer page = context.getWriter();
+
+ // write html header
+ page.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
+ + "XHTML 1.0 Transitional//EN\" "
+ + "\"http://www.w3.org/TR/xhtml1/"
+ + "DTD/xhtml1-transitional.dtd\">\n");
+
+ page.write("<html xmlns=\"http://www.w3.org/1999/xhtml\""
+ + ">\n<head>\n");
+ }
+
+ /**
+ * Get the URI for the application theme.
+ *
+ * A portal-wide default theme is fetched from the portal shared resource
+ * directory (if any), other themes from the portlet.
+ *
+ * @param context
+ * @param themeName
+ *
+ * @return
+ */
+ public String getThemeUri(BootstrapContext context, String themeName) {
+ WrappedRequest request = context.getRequest();
+ final String staticFilePath = request.getDeploymentConfiguration()
+ .getStaticFileLocation(request);
+ return staticFilePath + "/"
+ + AbstractApplicationServlet.THEME_DIRECTORY_PATH + themeName;
+ }
+
+ /**
+ * Override if required
+ *
+ * @param context
+ * @return
+ */
+ public String getThemeName(BootstrapContext context) {
+ return context.getApplication().getThemeForRoot(context.getRoot());
+ }
+
+ /**
+ * Don not override.
+ *
+ * @param context
+ * @return
+ */
+ public String findAndEscapeThemeName(BootstrapContext context) {
+ String themeName = getThemeName(context);
+ if (themeName == null) {
+ WrappedRequest request = context.getRequest();
+ themeName = request.getDeploymentConfiguration()
+ .getConfiguredTheme(request);
+ }
+
+ // XSS preventation, theme names shouldn't contain special chars anyway.
+ // The servlet denies them via url parameter.
+ themeName = AbstractApplicationServlet.stripSpecialChars(themeName);
+
+ return themeName;
+ }
+
+ protected void writeError(WrappedResponse response, Throwable e)
+ throws IOException {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ e.getLocalizedMessage());
+ }
+
+ /**
+ * Gets the initial UIDL message to send to the client.
+ *
+ * @param request
+ * the originating request
+ * @param root
+ * the root for which the UIDL should be generated
+ * @return a string with the initial UIDL message
+ * @throws PaintException
+ * if an exception occurs while painting the components
+ */
+ protected abstract String getInitialUIDL(WrappedRequest request, Root root)
+ throws PaintException;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
new file mode 100644
index 0000000000..7a1f0fad68
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ClientConnector.java
@@ -0,0 +1,38 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.server;
+
+import java.util.List;
+
+import com.vaadin.terminal.gwt.client.Connector;
+
+/**
+ * Interface implemented by all connectors that are capable of communicating
+ * with the client side
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public interface ClientConnector extends Connector, RpcTarget {
+ /**
+ * Returns the list of pending server to client RPC calls and clears the
+ * list.
+ *
+ * @return an unmodifiable ordered list of pending server to client method
+ * calls (not null)
+ *
+ * @since 7.0
+ */
+ public List<ClientMethodInvocation> retrievePendingRpcCalls();
+
+ /**
+ * Checks if the communicator is enabled. An enabled communicator is allowed
+ * to receive messages from its counter-part.
+ *
+ * @return true if the connector can receive messages, false otherwise
+ */
+ public boolean isConnectorEnabled();
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
new file mode 100644
index 0000000000..99633a13d6
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
@@ -0,0 +1,69 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+/**
+ * Internal class for keeping track of pending server to client method
+ * invocations for a Connector.
+ *
+ * @since 7.0
+ */
+public class ClientMethodInvocation implements Serializable,
+ Comparable<ClientMethodInvocation> {
+ private final ClientConnector connector;
+ private final String interfaceName;
+ private final String methodName;
+ private final Object[] parameters;
+ private Class<?>[] parameterTypes;
+
+ // used for sorting calls between different connectors in the same Root
+ private final long sequenceNumber;
+ // TODO may cause problems when clustering etc.
+ private static long counter = 0;
+
+ public ClientMethodInvocation(ClientConnector connector,
+ String interfaceName, Method method, Object[] parameters) {
+ this.connector = connector;
+ this.interfaceName = interfaceName;
+ methodName = method.getName();
+ parameterTypes = method.getParameterTypes();
+ this.parameters = (null != parameters) ? parameters : new Object[0];
+ sequenceNumber = ++counter;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public ClientConnector getConnector() {
+ return connector;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ protected long getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ public int compareTo(ClientMethodInvocation o) {
+ if (null == o) {
+ return 0;
+ }
+ return Long.signum(getSequenceNumber() - o.getSequenceNumber());
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
index 9c67a03bb3..3dd2eb97fd 100644
--- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java
@@ -6,24 +6,21 @@ package com.vaadin.terminal.gwt.server;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
+import java.net.URL;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import javax.servlet.ServletContext;
import com.vaadin.Application;
-import com.vaadin.terminal.ApplicationResource;
-import com.vaadin.terminal.DownloadStream;
-import com.vaadin.terminal.Paintable;
+import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.StreamVariable;
-import com.vaadin.terminal.VariableOwner;
-import com.vaadin.ui.Component;
-import com.vaadin.ui.Window;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.ui.Root;
/**
* Application manager processes changes and paints for single application
@@ -42,150 +39,6 @@ import com.vaadin.ui.Window;
public class CommunicationManager extends AbstractCommunicationManager {
/**
- * Concrete wrapper class for {@link HttpServletRequest}.
- *
- * @see Request
- */
- private static class HttpServletRequestWrapper implements Request {
-
- private final HttpServletRequest request;
-
- public HttpServletRequestWrapper(HttpServletRequest request) {
- this.request = request;
- }
-
- public Object getAttribute(String name) {
- return request.getAttribute(name);
- }
-
- public int getContentLength() {
- return request.getContentLength();
- }
-
- public InputStream getInputStream() throws IOException {
- return request.getInputStream();
- }
-
- public String getParameter(String name) {
- return request.getParameter(name);
- }
-
- public String getRequestID() {
- return "RequestURL:" + request.getRequestURI();
- }
-
- public Session getSession() {
- return new HttpSessionWrapper(request.getSession());
- }
-
- public Object getWrappedRequest() {
- return request;
- }
-
- public boolean isRunningInPortlet() {
- return false;
- }
-
- public void setAttribute(String name, Object o) {
- request.setAttribute(name, o);
- }
- }
-
- /**
- * Concrete wrapper class for {@link HttpServletResponse}.
- *
- * @see Response
- */
- private static class HttpServletResponseWrapper implements Response {
-
- private final HttpServletResponse response;
-
- public HttpServletResponseWrapper(HttpServletResponse response) {
- this.response = response;
- }
-
- public OutputStream getOutputStream() throws IOException {
- return response.getOutputStream();
- }
-
- public Object getWrappedResponse() {
- return response;
- }
-
- public void setContentType(String type) {
- response.setContentType(type);
- }
-
- }
-
- /**
- * Concrete wrapper class for {@link HttpSession}.
- *
- * @see Session
- */
- private static class HttpSessionWrapper implements Session {
-
- private final HttpSession session;
-
- public HttpSessionWrapper(HttpSession session) {
- this.session = session;
- }
-
- public Object getAttribute(String name) {
- return session.getAttribute(name);
- }
-
- public int getMaxInactiveInterval() {
- return session.getMaxInactiveInterval();
- }
-
- public Object getWrappedSession() {
- return session;
- }
-
- public boolean isNew() {
- return session.isNew();
- }
-
- public void setAttribute(String name, Object o) {
- session.setAttribute(name, o);
- }
-
- }
-
- private static class AbstractApplicationServletWrapper implements Callback {
-
- private final AbstractApplicationServlet servlet;
-
- public AbstractApplicationServletWrapper(
- AbstractApplicationServlet servlet) {
- this.servlet = servlet;
- }
-
- public void criticalNotification(Request request, Response response,
- String cap, String msg, String details, String outOfSyncURL)
- throws IOException {
- servlet.criticalNotification(
- (HttpServletRequest) request.getWrappedRequest(),
- (HttpServletResponse) response.getWrappedResponse(), cap,
- msg, details, outOfSyncURL);
- }
-
- public String getRequestPathInfo(Request request) {
- return servlet.getRequestPathInfo((HttpServletRequest) request
- .getWrappedRequest());
- }
-
- public InputStream getThemeResourceAsStream(String themeName,
- String resource) throws IOException {
- return servlet.getServletContext().getResourceAsStream(
- "/" + AbstractApplicationServlet.THEME_DIRECTORY_PATH
- + themeName + "/" + resource);
- }
-
- }
-
- /**
* @deprecated use {@link #CommunicationManager(Application)} instead
* @param application
* @param applicationServlet
@@ -208,6 +61,8 @@ public class CommunicationManager extends AbstractCommunicationManager {
/**
* Handles file upload request submitted via Upload component.
*
+ * @param application
+ *
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
*
* @param request
@@ -215,15 +70,15 @@ public class CommunicationManager extends AbstractCommunicationManager {
* @throws IOException
* @throws InvalidUIDLSecurityKeyException
*/
- public void handleFileUpload(HttpServletRequest request,
- HttpServletResponse response) throws IOException,
- InvalidUIDLSecurityKeyException {
+ public void handleFileUpload(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException, InvalidUIDLSecurityKeyException {
/*
* URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl
*/
- String pathInfo = request.getPathInfo();
+ String pathInfo = request.getRequestPathInfo();
// strip away part until the data we are interested starts
int startOfData = pathInfo
.indexOf(AbstractApplicationServlet.UPLOAD_URL_PREFIX)
@@ -238,22 +93,18 @@ public class CommunicationManager extends AbstractCommunicationManager {
String secKey = streamVariableToSeckey.get(streamVariable);
if (secKey.equals(parts[2])) {
- VariableOwner source = getVariableOwner(paintableId);
+ Connector source = getConnector(application, paintableId);
String contentType = request.getContentType();
- if (request.getContentType().contains("boundary")) {
+ if (contentType.contains("boundary")) {
// Multipart requests contain boundary string
- doHandleSimpleMultipartFileUpload(
- new HttpServletRequestWrapper(request),
- new HttpServletResponseWrapper(response),
+ doHandleSimpleMultipartFileUpload(request, response,
streamVariable, variableName, source,
contentType.split("boundary=")[1]);
} else {
// if boundary string does not exist, the posted file is from
// XHR2.post(File)
- doHandleXhrFilePost(new HttpServletRequestWrapper(request),
- new HttpServletResponseWrapper(response),
- streamVariable, variableName, source,
- request.getContentLength());
+ doHandleXhrFilePost(request, response, streamVariable,
+ variableName, source, request.getContentLength());
}
} else {
throw new InvalidUIDLSecurityKeyException(
@@ -262,95 +113,27 @@ public class CommunicationManager extends AbstractCommunicationManager {
}
- /**
- * Handles UIDL request
- *
- * TODO document
- *
- * @param request
- * @param response
- * @param applicationServlet
- * @param window
- * target window of the UIDL request, can be null if window not
- * found
- * @throws IOException
- * @throws ServletException
- */
- public void handleUidlRequest(HttpServletRequest request,
- HttpServletResponse response,
- AbstractApplicationServlet applicationServlet, Window window)
- throws IOException, ServletException,
- InvalidUIDLSecurityKeyException {
- doHandleUidlRequest(new HttpServletRequestWrapper(request),
- new HttpServletResponseWrapper(response),
- new AbstractApplicationServletWrapper(applicationServlet),
- window);
- }
-
- /**
- * Gets the existing application or creates a new one. Get a window within
- * an application based on the requested URI.
- *
- * @param request
- * the HTTP Request.
- * @param application
- * the Application to query for window.
- * @param assumedWindow
- * if the window has been already resolved once, this parameter
- * must contain the window.
- * @return Window matching the given URI or null if not found.
- * @throws ServletException
- * if an exception has occurred that interferes with the
- * servlet's normal operation.
- */
- Window getApplicationWindow(HttpServletRequest request,
- AbstractApplicationServlet applicationServlet,
- Application application, Window assumedWindow)
- throws ServletException {
- return doGetApplicationWindow(new HttpServletRequestWrapper(request),
- new AbstractApplicationServletWrapper(applicationServlet),
- application, assumedWindow);
- }
-
- /**
- * Calls the Window URI handler for a request and returns the
- * {@link DownloadStream} returned by the handler.
- *
- * If the window is the main window of an application, the deprecated
- * {@link Application#handleURI(java.net.URL, String)} is called first to
- * handle {@link ApplicationResource}s and the window handler is only called
- * if it returns null.
- *
- * @see AbstractCommunicationManager#handleURI(Window, Request, Response,
- * Callback)
- *
- * @param window
- * @param request
- * @param response
- * @param applicationServlet
- * @return
- */
- DownloadStream handleURI(Window window, HttpServletRequest request,
- HttpServletResponse response,
- AbstractApplicationServlet applicationServlet) {
- return handleURI(window, new HttpServletRequestWrapper(request),
- new HttpServletResponseWrapper(response),
- new AbstractApplicationServletWrapper(applicationServlet));
- }
-
@Override
- protected void unregisterPaintable(Component p) {
- /* Cleanup possible receivers */
+ protected void postPaint(Root root) {
+ super.postPaint(root);
+
+ Application application = root.getApplication();
if (pidToNameToStreamVariable != null) {
- Map<String, StreamVariable> removed = pidToNameToStreamVariable
- .remove(getPaintableId(p));
- if (removed != null) {
- for (String key : removed.keySet()) {
- streamVariableToSeckey.remove(removed.get(key));
+ Iterator<String> iterator = pidToNameToStreamVariable.keySet()
+ .iterator();
+ while (iterator.hasNext()) {
+ String connectorId = iterator.next();
+ if (application.getConnector(connectorId) == null) {
+ // Owner is no longer attached to the application
+ Map<String, StreamVariable> removed = pidToNameToStreamVariable
+ .get(connectorId);
+ for (String key : removed.keySet()) {
+ streamVariableToSeckey.remove(removed.get(key));
+ }
+ iterator.remove();
}
}
}
- super.unregisterPaintable(p);
}
@@ -359,7 +142,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
private Map<StreamVariable, String> streamVariableToSeckey;
@Override
- String getStreamVariableTargetUrl(VariableOwner owner, String name,
+ String getStreamVariableTargetUrl(Connector owner, String name,
StreamVariable value) {
/*
* We will use the same APP/* URI space as ApplicationResources but
@@ -373,7 +156,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
* NAME and PID from URI forms a key to fetch StreamVariable when
* handling post
*/
- String paintableId = getPaintableId((Paintable) owner);
+ String paintableId = owner.getConnectorId();
String key = paintableId + "/" + name;
if (pidToNameToStreamVariable == null) {
@@ -402,13 +185,81 @@ public class CommunicationManager extends AbstractCommunicationManager {
}
@Override
- protected void cleanStreamVariable(VariableOwner owner, String name) {
+ protected void cleanStreamVariable(Connector owner, String name) {
Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable
- .get(getPaintableId((Paintable) owner));
+ .get(owner.getConnectorId());
nameToStreamVar.remove("name");
if (nameToStreamVar.isEmpty()) {
- pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner));
+ pidToNameToStreamVariable.remove(owner.getConnectorId());
}
}
+ @Override
+ protected BootstrapHandler createBootstrapHandler() {
+ return new BootstrapHandler() {
+ @Override
+ protected String getApplicationId(BootstrapContext context) {
+ String appUrl = getAppUri(context);
+
+ String appId = appUrl;
+ if ("".equals(appUrl)) {
+ appId = "ROOT";
+ }
+ appId = appId.replaceAll("[^a-zA-Z0-9]", "");
+ // Add hashCode to the end, so that it is still (sort of)
+ // predictable, but indicates that it should not be used in CSS
+ // and
+ // such:
+ int hashCode = appId.hashCode();
+ if (hashCode < 0) {
+ hashCode = -hashCode;
+ }
+ appId = appId + "-" + hashCode;
+ return appId;
+ }
+
+ @Override
+ protected String getAppUri(BootstrapContext context) {
+ /* Fetch relative url to application */
+ // don't use server and port in uri. It may cause problems with
+ // some
+ // virtual server configurations which lose the server name
+ Application application = context.getApplication();
+ URL url = application.getURL();
+ String appUrl = url.getPath();
+ if (appUrl.endsWith("/")) {
+ appUrl = appUrl.substring(0, appUrl.length() - 1);
+ }
+ return appUrl;
+ }
+
+ @Override
+ public String getThemeName(BootstrapContext context) {
+ String themeName = context.getRequest().getParameter(
+ AbstractApplicationServlet.URL_PARAMETER_THEME);
+ if (themeName == null) {
+ themeName = super.getThemeName(context);
+ }
+ return themeName;
+ }
+
+ @Override
+ protected String getInitialUIDL(WrappedRequest request, Root root)
+ throws PaintException {
+ return CommunicationManager.this.getInitialUIDL(request, root);
+ }
+ };
+ }
+
+ @Override
+ protected InputStream getThemeResourceAsStream(Root root, String themeName,
+ String resource) {
+ WebApplicationContext context = (WebApplicationContext) root
+ .getApplication().getContext();
+ ServletContext servletContext = context.getHttpSession()
+ .getServletContext();
+ return servletContext.getResourceAsStream("/"
+ + AbstractApplicationServlet.THEME_DIRECTORY_PATH + themeName
+ + "/" + resource);
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java b/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
index 7889968e63..335067ca7a 100644
--- a/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
+++ b/src/com/vaadin/terminal/gwt/server/ComponentSizeValidator.java
@@ -16,8 +16,9 @@ import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.vaadin.terminal.Sizeable;
+import com.vaadin.terminal.Sizeable.Unit;
import com.vaadin.ui.AbstractOrderedLayout;
+import com.vaadin.ui.AbstractSplitPanel;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.CustomComponent;
@@ -25,9 +26,7 @@ import com.vaadin.ui.Form;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.GridLayout.Area;
import com.vaadin.ui.Layout;
-import com.vaadin.ui.OrderedLayout;
import com.vaadin.ui.Panel;
-import com.vaadin.ui.SplitPanel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
@@ -186,7 +185,7 @@ public class ComponentSizeValidator implements Serializable {
clientJSON.write("{");
Component parent = component.getParent();
- String paintableId = communicationManager.getPaintableId(component);
+ String paintableId = component.getConnectorId();
clientJSON.print("id:\"" + paintableId + "\"");
@@ -198,11 +197,7 @@ public class ComponentSizeValidator implements Serializable {
AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
boolean vertical = false;
- if (ol instanceof OrderedLayout) {
- if (((OrderedLayout) ol).getOrientation() == OrderedLayout.ORIENTATION_VERTICAL) {
- vertical = true;
- }
- } else if (ol instanceof VerticalLayout) {
+ if (ol instanceof VerticalLayout) {
vertical = true;
}
@@ -231,11 +226,7 @@ public class ComponentSizeValidator implements Serializable {
AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
boolean horizontal = true;
- if (ol instanceof OrderedLayout) {
- if (((OrderedLayout) ol).getOrientation() == OrderedLayout.ORIENTATION_VERTICAL) {
- horizontal = false;
- }
- } else if (ol instanceof VerticalLayout) {
+ if (ol instanceof VerticalLayout) {
horizontal = false;
}
@@ -323,7 +314,7 @@ public class ComponentSizeValidator implements Serializable {
width += "MAIN WINDOW";
} else if (component.getWidth() >= 0) {
width += "ABSOLUTE, " + component.getWidth() + " "
- + Sizeable.UNIT_SYMBOLS[component.getWidthUnits()];
+ + component.getWidthUnits().getSymbol();
} else {
width += "UNDEFINED";
}
@@ -339,7 +330,7 @@ public class ComponentSizeValidator implements Serializable {
height += "MAIN WINDOW";
} else if (component.getHeight() > 0) {
height += "ABSOLUTE, " + component.getHeight() + " "
- + Sizeable.UNIT_SYMBOLS[component.getHeightUnits()];
+ + component.getHeightUnits().getSymbol();
} else {
height += "UNDEFINED";
}
@@ -417,18 +408,13 @@ public class ComponentSizeValidator implements Serializable {
if (parent.getHeight() < 0) {
// Undefined height
if (parent instanceof Window) {
- Window w = (Window) parent;
- if (w.getParent() == null) {
- // main window is considered to have size
- return true;
- }
+ // Sub window with undefined size has a min-height
+ return true;
}
if (parent instanceof AbstractOrderedLayout) {
boolean horizontal = true;
- if (parent instanceof OrderedLayout) {
- horizontal = ((OrderedLayout) parent).getOrientation() == OrderedLayout.ORIENTATION_HORIZONTAL;
- } else if (parent instanceof VerticalLayout) {
+ if (parent instanceof VerticalLayout) {
horizontal = false;
}
if (horizontal
@@ -460,7 +446,7 @@ public class ComponentSizeValidator implements Serializable {
}
}
- if (parent instanceof Panel || parent instanceof SplitPanel
+ if (parent instanceof Panel || parent instanceof AbstractSplitPanel
|| parent instanceof TabSheet
|| parent instanceof CustomComponent) {
// height undefined, we know how how component works and no
@@ -488,7 +474,7 @@ public class ComponentSizeValidator implements Serializable {
}
private static boolean hasRelativeHeight(Component component) {
- return (component.getHeightUnits() == Sizeable.UNITS_PERCENTAGE && component
+ return (component.getHeightUnits() == Unit.PERCENTAGE && component
.getHeight() > 0);
}
@@ -504,7 +490,7 @@ public class ComponentSizeValidator implements Serializable {
private static boolean hasRelativeWidth(Component paintable) {
return paintable.getWidth() > 0
- && paintable.getWidthUnits() == Sizeable.UNITS_PERCENTAGE;
+ && paintable.getWidthUnits() == Unit.PERCENTAGE;
}
public static boolean parentCanDefineWidth(Component component) {
@@ -514,12 +500,8 @@ public class ComponentSizeValidator implements Serializable {
return true;
}
if (parent instanceof Window) {
- Window w = (Window) parent;
- if (w.getParent() == null) {
- // main window is considered to have size
- return true;
- }
-
+ // Sub window with undefined size has a min-width
+ return true;
}
if (parent.getWidth() < 0) {
@@ -528,11 +510,7 @@ public class ComponentSizeValidator implements Serializable {
if (parent instanceof AbstractOrderedLayout) {
AbstractOrderedLayout ol = (AbstractOrderedLayout) parent;
boolean horizontal = true;
- if (ol instanceof OrderedLayout) {
- if (((OrderedLayout) ol).getOrientation() == OrderedLayout.ORIENTATION_VERTICAL) {
- horizontal = false;
- }
- } else if (ol instanceof VerticalLayout) {
+ if (ol instanceof VerticalLayout) {
horizontal = false;
}
@@ -567,7 +545,7 @@ public class ComponentSizeValidator implements Serializable {
* the component width
*/
return hasNonRelativeWidthComponent((Form) parent);
- } else if (parent instanceof SplitPanel
+ } else if (parent instanceof AbstractSplitPanel
|| parent instanceof TabSheet
|| parent instanceof CustomComponent) {
// FIXME Could we use com.vaadin package name here and
diff --git a/src/com/vaadin/terminal/gwt/server/Constants.java b/src/com/vaadin/terminal/gwt/server/Constants.java
index e243bd7dae..7c467aa7f4 100644
--- a/src/com/vaadin/terminal/gwt/server/Constants.java
+++ b/src/com/vaadin/terminal/gwt/server/Constants.java
@@ -43,7 +43,6 @@ public interface Constants {
static final String URL_PARAMETER_REPAINT_ALL = "repaintAll";
static final String URL_PARAMETER_THEME = "theme";
- static final String SERVLET_PARAMETER_DEBUG = "Debug";
static final String SERVLET_PARAMETER_PRODUCTION_MODE = "productionMode";
static final String SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION = "disable-xsrf-protection";
static final String SERVLET_PARAMETER_RESOURCE_CACHE_TIME = "resourceCacheTime";
diff --git a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
index 3a923c1840..d3fe5a890b 100644
--- a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
+++ b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java
@@ -4,6 +4,7 @@
package com.vaadin.terminal.gwt.server;
import java.io.PrintWriter;
+import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@@ -18,10 +19,12 @@ import com.vaadin.event.dd.TargetDetailsImpl;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager.DragEventType;
import com.vaadin.ui.Component;
-public class DragAndDropService implements VariableOwner {
+public class DragAndDropService implements VariableOwner, ClientConnector {
private static final Logger logger = Logger
.getLogger(DragAndDropService.class.getName());
@@ -177,7 +180,7 @@ public class DragAndDropService implements VariableOwner {
}
public boolean isEnabled() {
- return true;
+ return isConnectorEnabled();
}
public boolean isImmediate() {
@@ -212,4 +215,27 @@ public class DragAndDropService implements VariableOwner {
}
return false;
}
+
+ public SharedState getState() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getConnectorId() {
+ return VDragAndDropManager.DD_SERVICE;
+ }
+
+ public boolean isConnectorEnabled() {
+ // Drag'n'drop can't be disabled
+ return true;
+ }
+
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return null;
+ }
+
+ public RpcManager getRpcManager(Class<?> rpcInterface) {
+ // TODO Use rpc for drag'n'drop
+ return null;
+ }
}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
new file mode 100644
index 0000000000..375cce4161
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -0,0 +1,587 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.vaadin.Application;
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
+import com.vaadin.ui.Component;
+
+/**
+ * Decoder for converting RPC parameters and other values from JSON in transfer
+ * between the client and the server and vice versa.
+ *
+ * @since 7.0
+ */
+public class JsonCodec implements Serializable {
+
+ private static Map<Class<?>, String> typeToTransportType = new HashMap<Class<?>, String>();
+
+ /**
+ * Note! This does not contain primitives.
+ * <p>
+ */
+ private static Map<String, Class<?>> transportTypeToType = new HashMap<String, Class<?>>();
+
+ static {
+ registerType(String.class, JsonEncoder.VTYPE_STRING);
+ registerType(Connector.class, JsonEncoder.VTYPE_CONNECTOR);
+ registerType(Boolean.class, JsonEncoder.VTYPE_BOOLEAN);
+ registerType(boolean.class, JsonEncoder.VTYPE_BOOLEAN);
+ registerType(Integer.class, JsonEncoder.VTYPE_INTEGER);
+ registerType(int.class, JsonEncoder.VTYPE_INTEGER);
+ registerType(Float.class, JsonEncoder.VTYPE_FLOAT);
+ registerType(float.class, JsonEncoder.VTYPE_FLOAT);
+ registerType(Double.class, JsonEncoder.VTYPE_DOUBLE);
+ registerType(double.class, JsonEncoder.VTYPE_DOUBLE);
+ registerType(Long.class, JsonEncoder.VTYPE_LONG);
+ registerType(long.class, JsonEncoder.VTYPE_LONG);
+ registerType(String[].class, JsonEncoder.VTYPE_STRINGARRAY);
+ registerType(Object[].class, JsonEncoder.VTYPE_ARRAY);
+ registerType(Map.class, JsonEncoder.VTYPE_MAP);
+ registerType(List.class, JsonEncoder.VTYPE_LIST);
+ registerType(Set.class, JsonEncoder.VTYPE_SET);
+ }
+
+ private static void registerType(Class<?> type, String transportType) {
+ typeToTransportType.put(type, transportType);
+ if (!type.isPrimitive()) {
+ transportTypeToType.put(transportType, type);
+ }
+ }
+
+ public static boolean isInternalTransportType(String transportType) {
+ return transportTypeToType.containsKey(transportType);
+ }
+
+ public static boolean isInternalType(Type type) {
+ if (type instanceof Class && ((Class<?>) type).isPrimitive()) {
+ // All primitive types are handled internally
+ return true;
+ }
+ return typeToTransportType.containsKey(getClassForType(type));
+ }
+
+ private static Class<?> getClassForType(Type type) {
+ if (type instanceof ParameterizedType) {
+ return (Class<?>) (((ParameterizedType) type).getRawType());
+ } else {
+ return (Class<?>) type;
+ }
+ }
+
+ public static String getTransportType(JSONArray encodedValue)
+ throws JSONException {
+ return encodedValue.getString(0);
+ }
+
+ private static Class<?> getType(String transportType) {
+ return transportTypeToType.get(transportType);
+ }
+
+ /**
+ * Decodes the given value and type, restricted to using only internal
+ * types.
+ *
+ * @param valueAndType
+ * @param application
+ * @throws JSONException
+ */
+ @Deprecated
+ public static Object decodeInternalType(JSONArray valueAndType,
+ Application application) throws JSONException {
+ String transportType = getTransportType(valueAndType);
+ return decodeInternalType(getType(transportType), true, valueAndType,
+ application);
+ }
+
+ public static Object decodeInternalOrCustomType(Type targetType,
+ JSONArray valueAndType, Application application)
+ throws JSONException {
+ if (isInternalType(targetType)) {
+ return decodeInternalType(targetType, false, valueAndType,
+ application);
+ } else {
+ return decodeCustomType(targetType, valueAndType, application);
+ }
+ }
+
+ public static Object decodeCustomType(Type targetType,
+ JSONArray valueAndType, Application application)
+ throws JSONException {
+ if (isInternalType(targetType)) {
+ throw new JSONException("decodeCustomType cannot be used for "
+ + targetType + ", which is an internal type");
+ }
+ String transportType = getCustomTransportType(getClassForType(targetType));
+ String encodedTransportType = valueAndType.getString(0);
+ if (!transportTypesCompatible(encodedTransportType, transportType)) {
+ throw new JSONException("Expected a value of type " + transportType
+ + ", received " + encodedTransportType);
+ }
+
+ // Try to decode object using fields
+ return decodeObject(targetType, (JSONObject) valueAndType.get(1),
+ application);
+ }
+
+ /**
+ * Decodes a value that is of an internal type.
+ * <p>
+ * Ensures the encoded value is of the same type as target type.
+ * </p>
+ * <p>
+ * Allows restricting collections so that they must be declared using
+ * generics. If this is used then all objects in the collection are encoded
+ * using the declared type. Otherwise only internal types are allowed in
+ * collections.
+ * </p>
+ *
+ * @param targetType
+ * The type that should be returned by this method
+ * @param valueAndType
+ * The encoded value and type array
+ * @param application
+ * A reference to the application
+ * @param enforceGenericsInCollections
+ * true if generics should be enforce, false to only allow
+ * internal types in collections
+ * @return
+ * @throws JSONException
+ */
+ public static Object decodeInternalType(Type targetType,
+ boolean restrictToInternalTypes, JSONArray valueAndType,
+ Application application) throws JSONException {
+ String encodedTransportType = valueAndType.getString(0);
+ if (!isInternalType(targetType)) {
+ throw new JSONException("Type " + targetType
+ + " is not a supported internal type.");
+ }
+ String transportType = getInternalTransportType(targetType);
+ if (!transportTypesCompatible(encodedTransportType, transportType)) {
+ throw new JSONException("Expected a value of type " + targetType
+ + ", received " + getType(encodedTransportType));
+ }
+
+ Object encodedJsonValue = valueAndType.get(1);
+
+ if (JsonEncoder.VTYPE_NULL.equals(encodedTransportType)) {
+ return null;
+ }
+ // Collections
+ if (JsonEncoder.VTYPE_LIST.equals(transportType)) {
+ return decodeList(targetType, restrictToInternalTypes,
+ (JSONArray) encodedJsonValue, application);
+ } else if (JsonEncoder.VTYPE_SET.equals(transportType)) {
+ return decodeSet(targetType, restrictToInternalTypes,
+ (JSONArray) encodedJsonValue, application);
+ } else if (JsonEncoder.VTYPE_MAP_CONNECTOR.equals(transportType)) {
+ return decodeConnectorToObjectMap(targetType,
+ restrictToInternalTypes, (JSONObject) encodedJsonValue,
+ application);
+ } else if (JsonEncoder.VTYPE_MAP.equals(transportType)) {
+ return decodeStringToObjectMap(targetType, restrictToInternalTypes,
+ (JSONObject) encodedJsonValue, application);
+ }
+
+ // Arrays
+ if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) {
+
+ return decodeObjectArray(targetType, (JSONArray) encodedJsonValue,
+ application);
+
+ } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) {
+ return decodeStringArray((JSONArray) encodedJsonValue);
+ }
+
+ // Special Vaadin types
+
+ String stringValue = String.valueOf(encodedJsonValue);
+
+ if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
+ return application.getConnector(stringValue);
+ }
+
+ // Standard Java types
+
+ if (JsonEncoder.VTYPE_STRING.equals(transportType)) {
+ return stringValue;
+ } else if (JsonEncoder.VTYPE_INTEGER.equals(transportType)) {
+ return Integer.valueOf(stringValue);
+ } else if (JsonEncoder.VTYPE_LONG.equals(transportType)) {
+ return Long.valueOf(stringValue);
+ } else if (JsonEncoder.VTYPE_FLOAT.equals(transportType)) {
+ return Float.valueOf(stringValue);
+ } else if (JsonEncoder.VTYPE_DOUBLE.equals(transportType)) {
+ return Double.valueOf(stringValue);
+ } else if (JsonEncoder.VTYPE_BOOLEAN.equals(transportType)) {
+ return Boolean.valueOf(stringValue);
+ }
+
+ throw new JSONException("Unknown type " + transportType);
+ }
+
+ private static boolean transportTypesCompatible(
+ String encodedTransportType, String transportType) {
+ if (encodedTransportType == null) {
+ return false;
+ }
+ if (encodedTransportType.equals(transportType)) {
+ return true;
+ }
+ if (encodedTransportType.equals(JsonEncoder.VTYPE_NULL)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Deprecated
+ private static Map<String, Object> decodeStringToObjectMap(Type targetType,
+ boolean restrictToInternalTypes, JSONObject jsonMap,
+ Application application) throws JSONException {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ Iterator<String> it = jsonMap.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONArray encodedValueAndType = jsonMap.getJSONArray(key);
+ Object decodedChild = decodeChild(targetType,
+ restrictToInternalTypes, 1, encodedValueAndType,
+ application);
+ map.put(key, decodedChild);
+ }
+ return map;
+ }
+
+ @Deprecated
+ private static Map<Connector, Object> decodeConnectorToObjectMap(
+ Type targetType, boolean restrictToInternalTypes,
+ JSONObject jsonMap, Application application) throws JSONException {
+ HashMap<Connector, Object> map = new HashMap<Connector, Object>();
+ Iterator<String> it = jsonMap.keys();
+ while (it.hasNext()) {
+ String connectorId = it.next();
+ Connector connector = application.getConnector(connectorId);
+ JSONArray encodedValueAndType = jsonMap.getJSONArray(connectorId);
+ Object decodedChild = decodeChild(targetType,
+ restrictToInternalTypes, 1, encodedValueAndType,
+ application);
+ map.put(connector, decodedChild);
+ }
+ return map;
+ }
+
+ /**
+ * @param targetType
+ * @param restrictToInternalTypes
+ * @param typeIndex
+ * The index of a generic type to use to define the child type
+ * that should be decoded
+ * @param encodedValueAndType
+ * @param application
+ * @return
+ * @throws JSONException
+ */
+ private static Object decodeChild(Type targetType,
+ boolean restrictToInternalTypes, int typeIndex,
+ JSONArray encodedValueAndType, Application application)
+ throws JSONException {
+ if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
+ Type childType = ((ParameterizedType) targetType)
+ .getActualTypeArguments()[typeIndex];
+ // Only decode the given type
+ return decodeInternalOrCustomType(childType, encodedValueAndType,
+ application);
+ } else {
+ // Only internal types when not enforcing a given type to avoid
+ // security issues
+ return decodeInternalType(encodedValueAndType, application);
+ }
+ }
+
+ private static String[] decodeStringArray(JSONArray jsonArray)
+ throws JSONException {
+ int length = jsonArray.length();
+ List<String> tokens = new ArrayList<String>(length);
+ for (int i = 0; i < length; ++i) {
+ tokens.add(jsonArray.getString(i));
+ }
+ return tokens.toArray(new String[tokens.size()]);
+ }
+
+ private static Object[] decodeObjectArray(Type targetType,
+ JSONArray jsonArray, Application application) throws JSONException {
+ List list = decodeList(List.class, true, jsonArray, application);
+ return list.toArray(new Object[list.size()]);
+ }
+
+ private static List<Object> decodeList(Type targetType,
+ boolean restrictToInternalTypes, JSONArray jsonArray,
+ Application application) throws JSONException {
+ List<Object> list = new ArrayList<Object>();
+ for (int i = 0; i < jsonArray.length(); ++i) {
+ // each entry always has two elements: type and value
+ JSONArray encodedValueAndType = jsonArray.getJSONArray(i);
+ Object decodedChild = decodeChild(targetType,
+ restrictToInternalTypes, 0, encodedValueAndType,
+ application);
+ list.add(decodedChild);
+ }
+ return list;
+ }
+
+ private static Set<Object> decodeSet(Type targetType,
+ boolean restrictToInternalTypes, JSONArray jsonArray,
+ Application application) throws JSONException {
+ HashSet<Object> set = new HashSet<Object>();
+ set.addAll(decodeList(List.class, restrictToInternalTypes, jsonArray,
+ application));
+ return set;
+ }
+
+ /**
+ * Returns the name that should be used as field name in the JSON. We strip
+ * "set" from the setter, keeping the result - this is easy to do on both
+ * server and client, avoiding some issues with cASE. E.g setZIndex()
+ * becomes "ZIndex". Also ensures that both getter and setter are present,
+ * returning null otherwise.
+ *
+ * @param pd
+ * @return the name to be used or null if both getter and setter are not
+ * found.
+ */
+ private static String getTransportFieldName(PropertyDescriptor pd) {
+ if (pd.getReadMethod() == null || pd.getWriteMethod() == null) {
+ return null;
+ }
+ return pd.getWriteMethod().getName().substring(3);
+ }
+
+ private static Object decodeObject(Type targetType,
+ JSONObject serializedObject, Application application)
+ throws JSONException {
+
+ Class<?> targetClass = getClassForType(targetType);
+ try {
+ Object decodedObject = targetClass.newInstance();
+ for (PropertyDescriptor pd : Introspector.getBeanInfo(targetClass)
+ .getPropertyDescriptors()) {
+
+ String fieldName = getTransportFieldName(pd);
+ if (fieldName == null) {
+ continue;
+ }
+ JSONArray encodedFieldValue = serializedObject
+ .getJSONArray(fieldName);
+ Type fieldType = pd.getReadMethod().getGenericReturnType();
+ Object decodedFieldValue = decodeInternalOrCustomType(
+ fieldType, encodedFieldValue, application);
+
+ pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
+ }
+
+ return decodedObject;
+ } catch (IllegalArgumentException e) {
+ throw new JSONException(e);
+ } catch (IllegalAccessException e) {
+ throw new JSONException(e);
+ } catch (InvocationTargetException e) {
+ throw new JSONException(e);
+ } catch (InstantiationException e) {
+ throw new JSONException(e);
+ } catch (IntrospectionException e) {
+ throw new JSONException(e);
+ }
+ }
+
+ @Deprecated
+ private static JSONArray encode(Object value, Application application)
+ throws JSONException {
+ return encode(value, null, application);
+ }
+
+ public static JSONArray encode(Object value, Class<?> valueType,
+ Application application) throws JSONException {
+
+ if (null == value) {
+ return encodeNull();
+ }
+
+ if (valueType == null) {
+ valueType = value.getClass();
+ }
+
+ String internalTransportType = getInternalTransportType(valueType);
+ if (value instanceof String[]) {
+ String[] array = (String[]) value;
+ JSONArray jsonArray = new JSONArray();
+ for (int i = 0; i < array.length; ++i) {
+ jsonArray.put(array[i]);
+ }
+ return combineTypeAndValue(JsonEncoder.VTYPE_STRINGARRAY, jsonArray);
+ } else if (value instanceof String) {
+ return combineTypeAndValue(JsonEncoder.VTYPE_STRING, value);
+ } else if (value instanceof Boolean) {
+ return combineTypeAndValue(JsonEncoder.VTYPE_BOOLEAN, value);
+ } else if (value instanceof Number) {
+ return combineTypeAndValue(internalTransportType, value);
+ } else if (value instanceof Collection) {
+ if (internalTransportType == null) {
+ throw new RuntimeException(
+ "Unable to serialize unsupported type: " + valueType);
+ }
+ Collection<?> collection = (Collection<?>) value;
+ JSONArray jsonArray = encodeCollection(collection, application);
+
+ return combineTypeAndValue(internalTransportType, jsonArray);
+ } else if (value instanceof Object[]) {
+ Object[] array = (Object[]) value;
+ JSONArray jsonArray = encodeArrayContents(array, application);
+ return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray);
+ } else if (value instanceof Map) {
+ Map<Object, Object> map = (Map<Object, Object>) value;
+ JSONObject jsonMap = encodeMapContents(map, application);
+ // Hack to support Connector as map key. Should be fixed by #
+ if (!map.isEmpty()
+ && map.keySet().iterator().next() instanceof Connector) {
+ return combineTypeAndValue(JsonEncoder.VTYPE_MAP_CONNECTOR,
+ jsonMap);
+ } else {
+ return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap);
+ }
+ } else if (value instanceof Connector) {
+ Connector connector = (Connector) value;
+ if (value instanceof Component
+ && !(AbstractCommunicationManager
+ .isVisible((Component) value))) {
+ return encodeNull();
+ }
+ return combineTypeAndValue(JsonEncoder.VTYPE_CONNECTOR,
+ connector.getConnectorId());
+ } else if (internalTransportType != null) {
+ return combineTypeAndValue(internalTransportType,
+ String.valueOf(value));
+ } else {
+ // Any object that we do not know how to encode we encode by looping
+ // through fields
+ return combineTypeAndValue(getCustomTransportType(valueType),
+ encodeObject(value, application));
+ }
+ }
+
+ private static JSONArray encodeNull() {
+ return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL);
+ }
+
+ private static Object encodeObject(Object value, Application application)
+ throws JSONException {
+ JSONObject jsonMap = new JSONObject();
+
+ try {
+ for (PropertyDescriptor pd : Introspector.getBeanInfo(
+ value.getClass()).getPropertyDescriptors()) {
+ Class<?> fieldType = pd.getPropertyType();
+ String fieldName = getTransportFieldName(pd);
+ if (fieldName == null) {
+ continue;
+ }
+ Method getterMethod = pd.getReadMethod();
+ Object fieldValue = getterMethod.invoke(value, (Object[]) null);
+ jsonMap.put(fieldName,
+ encode(fieldValue, fieldType, application));
+ }
+ } catch (Exception e) {
+ // TODO: Should exceptions be handled in a different way?
+ throw new JSONException(e);
+ }
+ return jsonMap;
+ }
+
+ private static JSONArray encodeArrayContents(Object[] array,
+ Application application) throws JSONException {
+ JSONArray jsonArray = new JSONArray();
+ for (Object o : array) {
+ jsonArray.put(encode(o, null, application));
+ }
+ return jsonArray;
+ }
+
+ private static JSONArray encodeCollection(Collection collection,
+ Application application) throws JSONException {
+ JSONArray jsonArray = new JSONArray();
+ for (Object o : collection) {
+ jsonArray.put(encode(o, application));
+ }
+ return jsonArray;
+ }
+
+ private static JSONObject encodeMapContents(Map<Object, Object> map,
+ Application application) throws JSONException {
+ JSONObject jsonMap = new JSONObject();
+ for (Object mapKey : map.keySet()) {
+ Object mapValue = map.get(mapKey);
+
+ if (mapKey instanceof ClientConnector) {
+ mapKey = ((ClientConnector) mapKey).getConnectorId();
+ }
+ if (!(mapKey instanceof String)) {
+ throw new JSONException(
+ "Only maps with String/Connector keys are currently supported (#8602)");
+ }
+
+ jsonMap.put((String) mapKey, encode(mapValue, null, application));
+ }
+ return jsonMap;
+ }
+
+ private static JSONArray combineTypeAndValue(String type, Object value) {
+ if (type == null) {
+ throw new RuntimeException("Type for value " + value
+ + " cannot be null!");
+ }
+ JSONArray outerArray = new JSONArray();
+ outerArray.put(type);
+ outerArray.put(value);
+ return outerArray;
+ }
+
+ /**
+ * Gets the transport type for the given class. Returns null if no transport
+ * type can be found.
+ *
+ * @param valueType
+ * The type that should be transported
+ * @return
+ * @throws JSONException
+ */
+ private static String getInternalTransportType(Type valueType) {
+ return typeToTransportType.get(getClassForType(valueType));
+ }
+
+ private static String getCustomTransportType(Class<?> targetType) {
+ return targetType.getName();
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
index d6c53a2da6..0140c0f799 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
@@ -4,23 +4,15 @@
package com.vaadin.terminal.gwt.server;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Serializable;
-import java.io.StringWriter;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
-import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.Application;
@@ -28,14 +20,12 @@ import com.vaadin.terminal.ApplicationResource;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.StreamVariable;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.ui.Alignment;
-import com.vaadin.ui.ClientWidget;
-import com.vaadin.ui.Component;
import com.vaadin.ui.CustomLayout;
/**
@@ -63,6 +53,10 @@ public class JsonPaintTarget implements PaintTarget {
private final Stack<JsonTag> openJsonTags;
+ // these match each other element-wise
+ private final Stack<ClientConnector> openPaintables;
+ private final Stack<String> openPaintableTags;
+
private final PrintWriter uidlBuffer;
private boolean closed = false;
@@ -77,20 +71,13 @@ public class JsonPaintTarget implements PaintTarget {
private JsonTag tag;
- private int errorsOpen;
-
private boolean cacheEnabled = false;
- private final Collection<Paintable> paintedComponents = new HashSet<Paintable>();
-
- private Collection<Paintable> identifiersCreatedDueRefPaint;
-
- private final Collection<Class<? extends Paintable>> usedPaintableTypes = new LinkedList<Class<? extends Paintable>>();
+ private final Set<Class<? extends ClientConnector>> usedClientConnectors = new HashSet<Class<? extends ClientConnector>>();
/**
- * Creates a new XMLPrintWriter, without automatic line flushing.
+ * Creates a new JsonPaintTarget.
*
- * @param variableMap
* @param manager
* @param outWriter
* A character-output stream.
@@ -112,6 +99,10 @@ public class JsonPaintTarget implements PaintTarget {
// Initialize tag-writing
mOpenTags = new Stack<String>();
openJsonTags = new Stack<JsonTag>();
+
+ openPaintables = new Stack<ClientConnector>();
+ openPaintableTags = new Stack<String>();
+
cacheEnabled = cachingRequired;
}
@@ -155,10 +146,6 @@ public class JsonPaintTarget implements PaintTarget {
tag = new JsonTag(tagName);
- if ("error".equals(tagName)) {
- errorsOpen++;
- }
-
customLayoutArgumentsOpen = false;
}
@@ -197,19 +184,7 @@ public class JsonPaintTarget implements PaintTarget {
+ tagName + "' expected: '" + lastTag + "'.");
}
- // simple hack which writes error uidl structure into attribute
- if ("error".equals(lastTag)) {
- if (errorsOpen == 1) {
- parent.addAttribute("\"error\":[\"error\",{}"
- + tag.getData() + "]");
- } else {
- // sub error
- parent.addData(tag.getJSON());
- }
- errorsOpen--;
- } else {
- parent.addData(tag.getJSON());
- }
+ parent.addData(tag.getJSON());
tag = parent;
} else {
@@ -424,9 +399,9 @@ public class JsonPaintTarget implements PaintTarget {
}
- public void addAttribute(String name, Paintable value)
+ public void addAttribute(String name, ClientConnector value)
throws PaintException {
- final String id = getPaintIdentifier(value);
+ final String id = value.getConnectorId();
addAttribute(name, id);
}
@@ -442,9 +417,8 @@ public class JsonPaintTarget implements PaintTarget {
Object key = it.next();
Object mapValue = value.get(key);
sb.append("\"");
- if (key instanceof Paintable) {
- Paintable paintable = (Paintable) key;
- sb.append(getPaintIdentifier(paintable));
+ if (key instanceof ClientConnector) {
+ sb.append(((ClientConnector) key).getConnectorId());
} else {
sb.append(escapeJSON(key.toString()));
}
@@ -493,10 +467,9 @@ public class JsonPaintTarget implements PaintTarget {
tag.addVariable(new StringVariable(owner, name, escapeJSON(value)));
}
- public void addVariable(VariableOwner owner, String name, Paintable value)
- throws PaintException {
- tag.addVariable(new StringVariable(owner, name,
- getPaintIdentifier(value)));
+ public void addVariable(VariableOwner owner, String name,
+ ClientConnector value) throws PaintException {
+ tag.addVariable(new StringVariable(owner, name, value.getConnectorId()));
}
public void addVariable(VariableOwner owner, String name, int value)
@@ -669,41 +642,48 @@ public class JsonPaintTarget implements PaintTarget {
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.PaintTarget#startTag(com.vaadin.terminal
+ * @see com.vaadin.terminal.PaintTarget#startPaintable(com.vaadin.terminal
* .Paintable, java.lang.String)
*/
- public boolean startTag(Paintable paintable, String tagName)
+ public PaintStatus startPaintable(ClientConnector connector, String tagName)
throws PaintException {
+ boolean topLevelPaintable = openPaintables.isEmpty();
+
+ logger.fine("startPaintable for " + connector.getClass().getName()
+ + "@" + Integer.toHexString(connector.hashCode()));
startTag(tagName, true);
- final boolean isPreviouslyPainted = manager.hasPaintableId(paintable)
- && (identifiersCreatedDueRefPaint == null || !identifiersCreatedDueRefPaint
- .contains(paintable));
- final String id = manager.getPaintableId(paintable);
- paintable.addListener(manager);
- addAttribute("id", id);
- paintedComponents.add(paintable);
-
- if (paintable instanceof CustomLayout) {
- customLayoutArgumentsOpen = true;
+
+ openPaintables.push(connector);
+ openPaintableTags.push(tagName);
+
+ addAttribute("id", connector.getConnectorId());
+
+ // Only paint top level paintables. All sub paintables are marked as
+ // queued and painted separately later.
+ if (!topLevelPaintable) {
+ return PaintStatus.CACHED;
}
- return cacheEnabled && isPreviouslyPainted;
+ if (connector instanceof CustomLayout) {
+ customLayoutArgumentsOpen = true;
+ }
+ return PaintStatus.PAINTING;
}
- @Deprecated
- public void paintReference(Paintable paintable, String referenceName)
- throws PaintException {
- addAttribute(referenceName, paintable);
- }
+ public void endPaintable(ClientConnector paintable) throws PaintException {
+ logger.fine("endPaintable for " + paintable.getClass().getName() + "@"
+ + Integer.toHexString(paintable.hashCode()));
- public String getPaintIdentifier(Paintable paintable) throws PaintException {
- if (!manager.hasPaintableId(paintable)) {
- if (identifiersCreatedDueRefPaint == null) {
- identifiersCreatedDueRefPaint = new HashSet<Paintable>();
- }
- identifiersCreatedDueRefPaint.add(paintable);
+ ClientConnector openPaintable = openPaintables.peek();
+ if (paintable != openPaintable) {
+ throw new PaintException("Invalid UIDL: closing wrong paintable: '"
+ + paintable.getConnectorId() + "' expected: '"
+ + openPaintable.getConnectorId() + "'.");
}
- return manager.getPaintableId(paintable);
+ // remove paintable from the stack
+ openPaintables.pop();
+ String openTag = openPaintableTags.pop();
+ endTag(openTag);
}
/*
@@ -980,178 +960,32 @@ public class JsonPaintTarget implements PaintTarget {
return usedResources;
}
- /**
- * Method to check if paintable is already painted into this target.
- *
- * @param p
- * @return true if is not yet painted into this target and is connected to
- * app
- */
- public boolean needsToBePainted(Paintable p) {
- if (paintedComponents.contains(p)) {
- return false;
- } else if (((Component) p).getApplication() == null) {
- return false;
- } else {
- return true;
- }
- }
-
- private static final Map<Class<? extends Paintable>, Class<? extends Paintable>> widgetMappingCache = new HashMap<Class<? extends Paintable>, Class<? extends Paintable>>();
-
@SuppressWarnings("unchecked")
- public String getTag(Paintable paintable) {
- Class<? extends Paintable> class1;
- synchronized (widgetMappingCache) {
- class1 = widgetMappingCache.get(paintable.getClass());
+ public String getTag(ClientConnector clientConnector) {
+ Class<? extends ClientConnector> clientConnectorClass = clientConnector
+ .getClass();
+ while (clientConnectorClass.isAnonymousClass()) {
+ clientConnectorClass = (Class<? extends ClientConnector>) clientConnectorClass
+ .getSuperclass();
}
- if (class1 == null) {
- /*
- * Client widget annotation is searched from component hierarchy to
- * detect the component that presumably has client side
- * implementation. The server side name is used in the
- * transportation, but encoded into integer strings to optimized
- * transferred data.
- */
- class1 = paintable.getClass();
- while (!hasClientWidgetMapping(class1)) {
- Class<?> superclass = class1.getSuperclass();
- if (superclass != null
- && Paintable.class.isAssignableFrom(superclass)) {
- class1 = (Class<? extends Paintable>) superclass;
- } else {
- logger.warning("No superclass of "
- + paintable.getClass().getName()
- + " has a @ClientWidget"
- + " annotation. Component will not be mapped correctly on client side.");
- break;
- }
- }
- synchronized (widgetMappingCache) {
- widgetMappingCache.put(paintable.getClass(), class1);
- }
- }
-
- usedPaintableTypes.add(class1);
- return manager.getTagForType(class1);
-
- }
-
- private boolean hasClientWidgetMapping(Class<? extends Paintable> class1) {
- try {
- return class1.isAnnotationPresent(ClientWidget.class);
- } catch (NoClassDefFoundError e) {
- String stacktrace = getStacktraceString(e);
- if (stacktrace
- .contains("com.ibm.oti.reflect.AnnotationParser.parseClass")) {
- // #7479 IBM JVM apparently tries to eagerly load the classes
- // referred to by annotations. Checking the annotation from byte
- // code to be sure that we are dealing the this case and not
- // some other class loading issue.
- if (bytecodeContainsClientWidgetAnnotation(class1)) {
- return true;
- }
- } else {
- // throw exception forward
- throw e;
- }
- } catch (LinkageError e) {
- String stacktrace = getStacktraceString(e);
- if (stacktrace
- .contains("org.jboss.modules.ModuleClassLoader.defineClass")) {
- // #7822 JBoss AS 7 apparently tries to eagerly load the classes
- // referred to by annotations. Checking the annotation from byte
- // code to be sure that we are dealing the this case and not
- // some other class loading issue.
- if (bytecodeContainsClientWidgetAnnotation(class1)) {
- // Seems that JBoss still prints a stacktrace to the logs
- // even though the LinkageError has been caught
- return true;
- }
- } else {
- // throw exception forward
- throw e;
- }
- } catch (RuntimeException e) {
- if (e.getStackTrace()[0].getClassName().equals(
- "org.glassfish.web.loader.WebappClassLoader")) {
-
- // See #3920
- // Glassfish 3 is darn eager to load the value class, even
- // though we just want to check if the annotation exists.
-
- // In some situations (depending on class loading order) it
- // would be enough to return true here, but it is safer to check
- // the annotation from byte code
-
- if (bytecodeContainsClientWidgetAnnotation(class1)) {
- return true;
- }
- } else {
- // throw exception forward
- throw e;
- }
- }
- return false;
- }
-
- private static String getStacktraceString(Throwable e) {
- StringWriter writer = new StringWriter();
- e.printStackTrace(new PrintWriter(writer));
- String stacktrace = writer.toString();
- return stacktrace;
- }
-
- private boolean bytecodeContainsClientWidgetAnnotation(
- Class<? extends Paintable> class1) {
-
- try {
- String name = class1.getName().replace('.', '/') + ".class";
-
- InputStream stream = class1.getClassLoader().getResourceAsStream(
- name);
- BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(stream));
- try {
- String line;
- boolean atSourcefile = false;
- while ((line = bufferedReader.readLine()) != null) {
- if (line.startsWith("SourceFile")) {
- atSourcefile = true;
- }
- if (atSourcefile) {
- if (line.contains("ClientWidget")) {
- return true;
- }
- }
- // TODO could optimize to quit at the end attribute
- }
- } catch (IOException e1) {
- logger.log(Level.SEVERE,
- "An error occurred while finding widget mapping.", e1);
- } finally {
- try {
- bufferedReader.close();
- } catch (IOException e1) {
- logger.log(Level.SEVERE, "Could not close reader.", e1);
-
- }
- }
- } catch (Throwable t) {
- logger.log(Level.SEVERE,
- "An error occurred while finding widget mapping.", t);
+ Class<?> clazz = clientConnectorClass;
+ while (!usedClientConnectors.contains(clazz)
+ && clazz.getSuperclass() != null
+ && ClientConnector.class.isAssignableFrom(clazz)) {
+ usedClientConnectors.add((Class<? extends ClientConnector>) clazz);
+ clazz = clazz.getSuperclass();
}
-
- return false;
+ return manager.getTagForType(clientConnectorClass);
}
- Collection<Class<? extends Paintable>> getUsedPaintableTypes() {
- return usedPaintableTypes;
+ Collection<Class<? extends ClientConnector>> getUsedClientConnectors() {
+ return usedClientConnectors;
}
public void addVariable(VariableOwner owner, String name,
StreamVariable value) throws PaintException {
- String url = manager.getStreamVariableTargetUrl(owner, name, value);
+ String url = manager.getStreamVariableTargetUrl((Connector) owner,
+ name, value);
if (url != null) {
addVariable(owner, name, url);
} // else { //NOP this was just a cleanup by component }
diff --git a/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java
new file mode 100644
index 0000000000..42fa3ab5a5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java
@@ -0,0 +1,38 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+
+public class LegacyChangeVariablesInvocation extends MethodInvocation {
+ private Map<String, Object> variableChanges = new HashMap<String, Object>();
+
+ public LegacyChangeVariablesInvocation(String connectorId,
+ String variableName, Object value) {
+ super(connectorId, ApplicationConnection.UPDATE_VARIABLE_INTERFACE,
+ ApplicationConnection.UPDATE_VARIABLE_METHOD);
+ setVariableChange(variableName, value);
+ }
+
+ public static boolean isLegacyVariableChange(String interfaceName,
+ String methodName) {
+ return ApplicationConnection.UPDATE_VARIABLE_METHOD
+ .equals(interfaceName)
+ && ApplicationConnection.UPDATE_VARIABLE_METHOD
+ .equals(methodName);
+ }
+
+ public void setVariableChange(String name, Object value) {
+ variableChanges.put(name, value);
+ }
+
+ public Map<String, Object> getVariableChanges() {
+ return variableChanges;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext.java
deleted file mode 100644
index 362fee1cc7..0000000000
--- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-/**
- *
- */
-package com.vaadin.terminal.gwt.server;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-import javax.portlet.ActionRequest;
-import javax.portlet.ActionResponse;
-import javax.portlet.Portlet;
-import javax.portlet.PortletSession;
-import javax.portlet.RenderRequest;
-import javax.portlet.RenderResponse;
-import javax.servlet.http.HttpSession;
-
-import com.vaadin.Application;
-
-/**
- * @author marc
- */
-public class PortletApplicationContext extends WebApplicationContext implements
- Serializable {
-
- protected transient PortletSession portletSession;
-
- protected Map<Application, Set<PortletListener>> portletListeners = new HashMap<Application, Set<PortletListener>>();
-
- protected Map<Portlet, Application> portletToApplication = new HashMap<Portlet, Application>();
-
- PortletApplicationContext() {
-
- }
-
- static public PortletApplicationContext getApplicationContext(
- PortletSession session) {
- WebApplicationContext cx = (WebApplicationContext) session
- .getAttribute(WebApplicationContext.class.getName(),
- PortletSession.APPLICATION_SCOPE);
- if (cx == null) {
- cx = new PortletApplicationContext();
- }
- if (!(cx instanceof PortletApplicationContext)) {
- // TODO Should we even try this? And should we leave original as-is?
- PortletApplicationContext pcx = new PortletApplicationContext();
- pcx.applications.addAll(cx.applications);
- cx.applications.clear();
- pcx.browser = cx.browser;
- cx.browser = null;
- pcx.listeners = cx.listeners;
- cx.listeners = null;
- pcx.session = cx.session;
- cx = pcx;
- }
- if (((PortletApplicationContext) cx).portletSession == null) {
- ((PortletApplicationContext) cx).portletSession = session;
- }
- session.setAttribute(WebApplicationContext.class.getName(), cx,
- PortletSession.APPLICATION_SCOPE);
- return (PortletApplicationContext) cx;
- }
-
- static public WebApplicationContext getApplicationContext(
- HttpSession session) {
- WebApplicationContext cx = (WebApplicationContext) session
- .getAttribute(WebApplicationContext.class.getName());
- if (cx == null) {
- cx = new PortletApplicationContext();
- }
- if (cx.session == null) {
- cx.session = session;
- }
- session.setAttribute(WebApplicationContext.class.getName(), cx);
- return cx;
- }
-
- @Override
- protected void removeApplication(Application application) {
- portletListeners.remove(application);
- for (Iterator<Application> it = portletToApplication.values()
- .iterator(); it.hasNext();) {
- Application value = it.next();
- if (value == application) {
- // values().iterator() is backed by the map
- it.remove();
- }
- }
- super.removeApplication(application);
- }
-
- /**
- * Reinitializing the session is not supported from portlets.
- *
- * @see com.vaadin.terminal.gwt.server.WebApplicationContext#reinitializeSession()
- */
- @Override
- public void reinitializeSession() {
- throw new UnsupportedOperationException(
- "Reinitializing the session is not supported from portlets");
- }
-
- public void setPortletApplication(Portlet portlet, Application app) {
- portletToApplication.put(portlet, app);
- }
-
- public Application getPortletApplication(Portlet portlet) {
- return portletToApplication.get(portlet);
- }
-
- public PortletSession getPortletSession() {
- return portletSession;
- }
-
- public void addPortletListener(Application app, PortletListener listener) {
- Set<PortletListener> l = portletListeners.get(app);
- if (l == null) {
- l = new LinkedHashSet<PortletListener>();
- portletListeners.put(app, l);
- }
- l.add(listener);
- }
-
- public void removePortletListener(Application app, PortletListener listener) {
- Set<PortletListener> l = portletListeners.get(app);
- if (l != null) {
- l.remove(listener);
- }
- }
-
- public static void dispatchRequest(Portlet portlet, RenderRequest request,
- RenderResponse response) {
- PortletApplicationContext ctx = getApplicationContext(request
- .getPortletSession());
- if (ctx != null) {
- ctx.firePortletRenderRequest(portlet, request, response);
- }
- }
-
- public static void dispatchRequest(Portlet portlet, ActionRequest request,
- ActionResponse response) {
- PortletApplicationContext ctx = getApplicationContext(request
- .getPortletSession());
- if (ctx != null) {
- ctx.firePortletActionRequest(portlet, request, response);
- }
- }
-
- public void firePortletRenderRequest(Portlet portlet,
- RenderRequest request, RenderResponse response) {
- Application app = getPortletApplication(portlet);
- Set<PortletListener> listeners = portletListeners.get(app);
- if (listeners != null) {
- for (PortletListener l : listeners) {
- l.handleRenderRequest(request, new RestrictedRenderResponse(
- response));
- }
- }
- }
-
- public void firePortletActionRequest(Portlet portlet,
- ActionRequest request, ActionResponse response) {
- Application app = getPortletApplication(portlet);
- Set<PortletListener> listeners = portletListeners.get(app);
- if (listeners != null) {
- for (PortletListener l : listeners) {
- l.handleActionRequest(request, response);
- }
- }
- }
-
- public interface PortletListener extends Serializable {
- public void handleRenderRequest(RenderRequest request,
- RenderResponse response);
-
- public void handleActionRequest(ActionRequest request,
- ActionResponse response);
- }
-
-}
diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
index dce1a1a78c..661da57af6 100644
--- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
+++ b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java
@@ -35,8 +35,7 @@ import javax.xml.namespace.QName;
import com.vaadin.Application;
import com.vaadin.terminal.ApplicationResource;
-import com.vaadin.terminal.ExternalResource;
-import com.vaadin.ui.Window;
+import com.vaadin.ui.Root;
/**
* TODO Write documentation, fix JavaDoc tags.
@@ -172,18 +171,18 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
}
}
- public void firePortletRenderRequest(Application app, Window window,
+ public void firePortletRenderRequest(Application app, Root root,
RenderRequest request, RenderResponse response) {
Set<PortletListener> listeners = portletListeners.get(app);
if (listeners != null) {
for (PortletListener l : listeners) {
l.handleRenderRequest(request, new RestrictedRenderResponse(
- response), window);
+ response), root);
}
}
}
- public void firePortletActionRequest(Application app, Window window,
+ public void firePortletActionRequest(Application app, Root root,
ActionRequest request, ActionResponse response) {
String key = request.getParameter(ActionRequest.ACTION_NAME);
if (eventActionDestinationMap.containsKey(key)) {
@@ -205,28 +204,28 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
Set<PortletListener> listeners = portletListeners.get(app);
if (listeners != null) {
for (PortletListener l : listeners) {
- l.handleActionRequest(request, response, window);
+ l.handleActionRequest(request, response, root);
}
}
}
}
- public void firePortletEventRequest(Application app, Window window,
+ public void firePortletEventRequest(Application app, Root root,
EventRequest request, EventResponse response) {
Set<PortletListener> listeners = portletListeners.get(app);
if (listeners != null) {
for (PortletListener l : listeners) {
- l.handleEventRequest(request, response, window);
+ l.handleEventRequest(request, response, root);
}
}
}
- public void firePortletResourceRequest(Application app, Window window,
+ public void firePortletResourceRequest(Application app, Root root,
ResourceRequest request, ResourceResponse response) {
Set<PortletListener> listeners = portletListeners.get(app);
if (listeners != null) {
for (PortletListener l : listeners) {
- l.handleResourceRequest(request, response, window);
+ l.handleResourceRequest(request, response, root);
}
}
}
@@ -234,16 +233,16 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
public interface PortletListener extends Serializable {
public void handleRenderRequest(RenderRequest request,
- RenderResponse response, Window window);
+ RenderResponse response, Root root);
public void handleActionRequest(ActionRequest request,
- ActionResponse response, Window window);
+ ActionResponse response, Root root);
public void handleEventRequest(EventRequest request,
- EventResponse response, Window window);
+ EventResponse response, Root root);
public void handleResourceRequest(ResourceRequest request,
- ResourceResponse response, Window window);
+ ResourceResponse response, Root root);
}
/**
@@ -308,7 +307,7 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
* Event names for events sent and received by a portlet need to be declared
* in portlet.xml .
*
- * @param window
+ * @param root
* a window in which a temporary action URL can be opened if
* necessary
* @param name
@@ -317,7 +316,7 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
* event value object that is Serializable and, if appropriate,
* has a valid JAXB annotation
*/
- public void sendPortletEvent(Window window, QName name, Serializable value)
+ public void sendPortletEvent(Root root, QName name, Serializable value)
throws IllegalStateException {
if (response instanceof MimeResponse) {
String actionKey = "" + System.currentTimeMillis();
@@ -328,7 +327,9 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
if (actionUrl != null) {
eventActionDestinationMap.put(actionKey, name);
eventActionValueMap.put(actionKey, value);
- window.open(new ExternalResource(actionUrl.toString()));
+ throw new RuntimeException(
+ "Root.open has not yet been implemented");
+ // root.open(new ExternalResource(actionUrl.toString()));
} else {
// this should never happen as we already know the response is a
// MimeResponse
@@ -355,7 +356,7 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
* Shared parameters set or read by a portlet need to be declared in
* portlet.xml .
*
- * @param window
+ * @param root
* a window in which a temporary action URL can be opened if
* necessary
* @param name
@@ -363,8 +364,8 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
* @param value
* parameter value
*/
- public void setSharedRenderParameter(Window window, String name,
- String value) throws IllegalStateException {
+ public void setSharedRenderParameter(Root root, String name, String value)
+ throws IllegalStateException {
if (response instanceof MimeResponse) {
String actionKey = "" + System.currentTimeMillis();
while (sharedParameterActionNameMap.containsKey(actionKey)) {
@@ -374,7 +375,9 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
if (actionUrl != null) {
sharedParameterActionNameMap.put(actionKey, name);
sharedParameterActionValueMap.put(actionKey, value);
- window.open(new ExternalResource(actionUrl.toString()));
+ throw new RuntimeException(
+ "Root.open has not yet been implemented");
+ // root.open(new ExternalResource(actionUrl.toString()));
} else {
// this should never happen as we already know the response is a
// MimeResponse
@@ -394,7 +397,7 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
*
* Portlet modes used by a portlet need to be declared in portlet.xml .
*
- * @param window
+ * @param root
* a window in which the render URL can be opened if necessary
* @param portletMode
* the portlet mode to switch to
@@ -402,12 +405,13 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext {
* if the portlet mode is not allowed for some reason
* (configuration, permissions etc.)
*/
- public void setPortletMode(Window window, PortletMode portletMode)
+ public void setPortletMode(Root root, PortletMode portletMode)
throws IllegalStateException, PortletModeException {
if (response instanceof MimeResponse) {
PortletURL url = ((MimeResponse) response).createRenderURL();
url.setPortletMode(portletMode);
- window.open(new ExternalResource(url.toString()));
+ throw new RuntimeException("Root.open has not yet been implemented");
+ // root.open(new ExternalResource(url.toString()));
} else if (response instanceof StateAwareResponse) {
((StateAwareResponse) response).setPortletMode(portletMode);
} else {
diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
index ffa8ad4054..b3ec33a9e0 100644
--- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
@@ -5,29 +5,29 @@ package com.vaadin.terminal.gwt.server;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
-import javax.portlet.ClientDataRequest;
import javax.portlet.MimeResponse;
+import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
-import javax.portlet.PortletSession;
-import javax.portlet.ResourceRequest;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
import javax.portlet.ResourceResponse;
import javax.portlet.ResourceURL;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequestWrapper;
import com.vaadin.Application;
-import com.vaadin.terminal.DownloadStream;
-import com.vaadin.terminal.Paintable;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.StreamVariable;
-import com.vaadin.terminal.VariableOwner;
-import com.vaadin.ui.Component;
-import com.vaadin.ui.Window;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.ui.Root;
/**
* TODO document me!
@@ -40,241 +40,65 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
private transient ResourceResponse currentUidlResponse;
- private static class PortletRequestWrapper implements Request {
-
- private final PortletRequest request;
-
- public PortletRequestWrapper(PortletRequest request) {
- this.request = request;
- }
-
- public Object getAttribute(String name) {
- return request.getAttribute(name);
- }
-
- public int getContentLength() {
- return ((ClientDataRequest) request).getContentLength();
- }
-
- public InputStream getInputStream() throws IOException {
- return ((ClientDataRequest) request).getPortletInputStream();
- }
-
- public String getParameter(String name) {
- String value = request.getParameter(name);
- if (value == null) {
- // for GateIn portlet container simple-portal
- try {
- Method getRealReq = request.getClass().getMethod(
- "getRealRequest");
- HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq
- .invoke(request);
- value = origRequest.getParameter(name);
- } catch (Exception e) {
- // do nothing - not on GateIn simple-portal
- }
- }
- return value;
- }
-
- public String getRequestID() {
- return "WindowID:" + request.getWindowID();
- }
-
- public Session getSession() {
- return new PortletSessionWrapper(request.getPortletSession());
- }
-
- public Object getWrappedRequest() {
- return request;
- }
-
- public boolean isRunningInPortlet() {
- return true;
- }
-
- public void setAttribute(String name, Object o) {
- request.setAttribute(name, o);
- }
-
- }
-
- private static class PortletResponseWrapper implements Response {
-
- private final PortletResponse response;
-
- public PortletResponseWrapper(PortletResponse response) {
- this.response = response;
- }
-
- public OutputStream getOutputStream() throws IOException {
- return ((MimeResponse) response).getPortletOutputStream();
- }
-
- public Object getWrappedResponse() {
- return response;
- }
-
- public void setContentType(String type) {
- ((MimeResponse) response).setContentType(type);
- }
- }
-
- private static class PortletSessionWrapper implements Session {
-
- private final PortletSession session;
-
- public PortletSessionWrapper(PortletSession session) {
- this.session = session;
- }
-
- public Object getAttribute(String name) {
- return session.getAttribute(name);
- }
-
- public int getMaxInactiveInterval() {
- return session.getMaxInactiveInterval();
- }
-
- public Object getWrappedSession() {
- return session;
- }
-
- public boolean isNew() {
- return session.isNew();
- }
-
- public void setAttribute(String name, Object o) {
- session.setAttribute(name, o);
- }
-
- }
-
- private static class AbstractApplicationPortletWrapper implements Callback {
-
- private final AbstractApplicationPortlet portlet;
-
- public AbstractApplicationPortletWrapper(
- AbstractApplicationPortlet portlet) {
- this.portlet = portlet;
- }
-
- public void criticalNotification(Request request, Response response,
- String cap, String msg, String details, String outOfSyncURL)
- throws IOException {
- portlet.criticalNotification(
- (PortletRequest) request.getWrappedRequest(),
- (MimeResponse) response.getWrappedResponse(), cap, msg,
- details, outOfSyncURL);
- }
-
- public String getRequestPathInfo(Request request) {
- if (request.getWrappedRequest() instanceof ResourceRequest) {
- return ((ResourceRequest) request.getWrappedRequest())
- .getResourceID();
- } else {
- // We do not use paths in portlet mode
- throw new UnsupportedOperationException(
- "PathInfo only available when using ResourceRequests");
- }
- }
-
- public InputStream getThemeResourceAsStream(String themeName,
- String resource) throws IOException {
- return portlet.getPortletContext().getResourceAsStream(
- "/" + AbstractApplicationPortlet.THEME_DIRECTORY_PATH
- + themeName + "/" + resource);
- }
-
- }
-
public PortletCommunicationManager(Application application) {
super(application);
}
- public void handleFileUpload(ResourceRequest request,
- ResourceResponse response) throws IOException {
+ public void handleFileUpload(WrappedRequest request,
+ WrappedResponse response) throws IOException {
String contentType = request.getContentType();
String name = request.getParameter("name");
String ownerId = request.getParameter("rec-owner");
- VariableOwner variableOwner = getVariableOwner(ownerId);
- StreamVariable streamVariable = ownerToNameToStreamVariable.get(
- variableOwner).get(name);
+ Connector owner = getConnector(getApplication(), ownerId);
+ StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner)
+ .get(name);
if (contentType.contains("boundary")) {
- doHandleSimpleMultipartFileUpload(
- new PortletRequestWrapper(request),
- new PortletResponseWrapper(response), streamVariable, name,
- variableOwner, contentType.split("boundary=")[1]);
+ doHandleSimpleMultipartFileUpload(request, response,
+ streamVariable, name, owner,
+ contentType.split("boundary=")[1]);
} else {
- doHandleXhrFilePost(new PortletRequestWrapper(request),
- new PortletResponseWrapper(response), streamVariable, name,
- variableOwner, request.getContentLength());
+ doHandleXhrFilePost(request, response, streamVariable, name, owner,
+ request.getContentLength());
}
}
@Override
- protected void unregisterPaintable(Component p) {
- super.unregisterPaintable(p);
+ protected void postPaint(Root root) {
+ super.postPaint(root);
+
+ Application application = root.getApplication();
if (ownerToNameToStreamVariable != null) {
- ownerToNameToStreamVariable.remove(p);
+ Iterator<Connector> iterator = ownerToNameToStreamVariable.keySet()
+ .iterator();
+ while (iterator.hasNext()) {
+ Connector owner = iterator.next();
+ if (application.getConnector(owner.getConnectorId()) == null) {
+ // Owner is no longer attached to the application
+ iterator.remove();
+ }
+ }
}
}
- public void handleUidlRequest(ResourceRequest request,
- ResourceResponse response,
- AbstractApplicationPortlet applicationPortlet, Window window)
- throws InvalidUIDLSecurityKeyException, IOException {
- currentUidlResponse = response;
- doHandleUidlRequest(new PortletRequestWrapper(request),
- new PortletResponseWrapper(response),
- new AbstractApplicationPortletWrapper(applicationPortlet),
- window);
+ @Override
+ public void handleUidlRequest(WrappedRequest request,
+ WrappedResponse response, Callback callback, Root root)
+ throws IOException, InvalidUIDLSecurityKeyException {
+ currentUidlResponse = (ResourceResponse) ((WrappedPortletResponse) response)
+ .getPortletResponse();
+ super.handleUidlRequest(request, response, callback, root);
currentUidlResponse = null;
}
- DownloadStream handleURI(Window window, ResourceRequest request,
- ResourceResponse response,
- AbstractApplicationPortlet applicationPortlet) {
- return handleURI(window, new PortletRequestWrapper(request),
- new PortletResponseWrapper(response),
- new AbstractApplicationPortletWrapper(applicationPortlet));
- }
-
- /**
- * Gets the existing application or creates a new one. Get a window within
- * an application based on the requested URI.
- *
- * @param request
- * the portlet Request.
- * @param applicationPortlet
- * @param application
- * the Application to query for window.
- * @param assumedWindow
- * if the window has been already resolved once, this parameter
- * must contain the window.
- * @return Window matching the given URI or null if not found.
- * @throws ServletException
- * if an exception has occurred that interferes with the
- * servlet's normal operation.
- */
- Window getApplicationWindow(PortletRequest request,
- AbstractApplicationPortlet applicationPortlet,
- Application application, Window assumedWindow) {
-
- return doGetApplicationWindow(new PortletRequestWrapper(request),
- new AbstractApplicationPortletWrapper(applicationPortlet),
- application, assumedWindow);
- }
-
- private Map<VariableOwner, Map<String, StreamVariable>> ownerToNameToStreamVariable;
+ private Map<Connector, Map<String, StreamVariable>> ownerToNameToStreamVariable;
@Override
- String getStreamVariableTargetUrl(VariableOwner owner, String name,
+ String getStreamVariableTargetUrl(Connector owner, String name,
StreamVariable value) {
if (ownerToNameToStreamVariable == null) {
- ownerToNameToStreamVariable = new HashMap<VariableOwner, Map<String, StreamVariable>>();
+ ownerToNameToStreamVariable = new HashMap<Connector, Map<String, StreamVariable>>();
}
Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable
.get(owner);
@@ -286,14 +110,14 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
ResourceURL resurl = currentUidlResponse.createResourceURL();
resurl.setResourceID("UPLOAD");
resurl.setParameter("name", name);
- resurl.setParameter("rec-owner", getPaintableId((Paintable) owner));
+ resurl.setParameter("rec-owner", owner.getConnectorId());
resurl.setProperty("name", name);
- resurl.setProperty("rec-owner", getPaintableId((Paintable) owner));
+ resurl.setProperty("rec-owner", owner.getConnectorId());
return resurl.toString();
}
@Override
- protected void cleanStreamVariable(VariableOwner owner, String name) {
+ protected void cleanStreamVariable(Connector owner, String name) {
Map<String, StreamVariable> map = ownerToNameToStreamVariable
.get(owner);
map.remove(name);
@@ -302,4 +126,131 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
}
}
+ @Override
+ protected BootstrapHandler createBootstrapHandler() {
+ return new BootstrapHandler() {
+ @Override
+ public boolean handleRequest(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException {
+ PortletRequest portletRequest = WrappedPortletRequest.cast(
+ request).getPortletRequest();
+ if (portletRequest instanceof RenderRequest) {
+ return super.handleRequest(application, request, response);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected String getApplicationId(BootstrapContext context) {
+ PortletRequest portletRequest = WrappedPortletRequest.cast(
+ context.getRequest()).getPortletRequest();
+ /*
+ * We need to generate a unique ID because some portals already
+ * create a DIV with the portlet's Window ID as the DOM ID.
+ */
+ return "v-" + portletRequest.getWindowID();
+ }
+
+ @Override
+ protected String getAppUri(BootstrapContext context) {
+ return getRenderResponse(context).createActionURL().toString();
+ }
+
+ private RenderResponse getRenderResponse(BootstrapContext context) {
+ PortletResponse response = ((WrappedPortletResponse) context
+ .getResponse()).getPortletResponse();
+
+ RenderResponse renderResponse = (RenderResponse) response;
+ return renderResponse;
+ }
+
+ @Override
+ protected JSONObject getDefaultParameters(BootstrapContext context)
+ throws JSONException {
+ /*
+ * We need this in order to get uploads to work. TODO this is
+ * not needed for uploads anymore, check if this is needed for
+ * some other things
+ */
+ JSONObject defaults = super.getDefaultParameters(context);
+ defaults.put("usePortletURLs", true);
+
+ ResourceURL uidlUrlBase = getRenderResponse(context)
+ .createResourceURL();
+ uidlUrlBase.setResourceID("UIDL");
+ defaults.put("portletUidlURLBase", uidlUrlBase.toString());
+ defaults.put("pathInfo", "");
+
+ return defaults;
+ }
+
+ @Override
+ protected void writeMainScriptTagContents(BootstrapContext context)
+ throws JSONException, IOException {
+ // fixed base theme to use - all portal pages with Vaadin
+ // applications will load this exactly once
+ String portalTheme = WrappedPortletRequest
+ .cast(context.getRequest())
+ .getPortalProperty(
+ AbstractApplicationPortlet.PORTAL_PARAMETER_VAADIN_THEME);
+ if (portalTheme != null
+ && !portalTheme.equals(context.getThemeName())) {
+ String portalThemeUri = getThemeUri(context, portalTheme);
+ // XSS safe - originates from portal properties
+ context.getWriter().write(
+ "vaadin.loadTheme('" + portalThemeUri + "');");
+ }
+
+ super.writeMainScriptTagContents(context);
+ }
+
+ @Override
+ protected String getMainDivStyle(BootstrapContext context) {
+ DeploymentConfiguration deploymentConfiguration = context
+ .getRequest().getDeploymentConfiguration();
+ return deploymentConfiguration.getApplicationOrSystemProperty(
+ AbstractApplicationPortlet.PORTLET_PARAMETER_STYLE,
+ null);
+ }
+
+ @Override
+ protected String getInitialUIDL(WrappedRequest request, Root root)
+ throws PaintException {
+ return PortletCommunicationManager.this.getInitialUIDL(request,
+ root);
+ }
+
+ @Override
+ protected JSONObject getApplicationParameters(
+ BootstrapContext context) throws JSONException,
+ PaintException {
+ JSONObject parameters = super.getApplicationParameters(context);
+ WrappedPortletResponse wrappedPortletResponse = (WrappedPortletResponse) context
+ .getResponse();
+ MimeResponse portletResponse = (MimeResponse) wrappedPortletResponse
+ .getPortletResponse();
+ ResourceURL resourceURL = portletResponse.createResourceURL();
+ resourceURL.setResourceID("browserDetails");
+ parameters.put("browserDetailsUrl", resourceURL.toString());
+ return parameters;
+ }
+
+ };
+
+ }
+
+ @Override
+ protected InputStream getThemeResourceAsStream(Root root, String themeName,
+ String resource) {
+ PortletApplicationContext2 context = (PortletApplicationContext2) root
+ .getApplication().getContext();
+ PortletContext portletContext = context.getPortletSession()
+ .getPortletContext();
+ return portletContext.getResourceAsStream("/"
+ + AbstractApplicationPortlet.THEME_DIRECTORY_PATH + themeName
+ + "/" + resource);
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/server/RequestTimer.java b/src/com/vaadin/terminal/gwt/server/RequestTimer.java
index 5ed89c2d29..d47f444bef 100644
--- a/src/com/vaadin/terminal/gwt/server/RequestTimer.java
+++ b/src/com/vaadin/terminal/gwt/server/RequestTimer.java
@@ -4,10 +4,7 @@
package com.vaadin.terminal.gwt.server;
-import javax.portlet.PortletRequest;
-import javax.portlet.PortletSession;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import com.vaadin.terminal.WrappedRequest;
/**
* Times the handling of requests and stores the information as an attribute in
@@ -25,72 +22,13 @@ public class RequestTimer {
private long lastRequestTime = -1;
/**
- * This class acts as a proxy for setting and getting session and request
- * attributes on HttpServletRequests and PortletRequests. Using this class
- * we don't need to duplicate code everywhere.
- */
- static class RequestWrapper {
- private final HttpServletRequest servletRequest;
- private final PortletRequest portletRequest;
-
- public RequestWrapper(HttpServletRequest servletRequest) {
- this.servletRequest = servletRequest;
- portletRequest = null;
- }
-
- public RequestWrapper(PortletRequest portletRequest) {
- this.portletRequest = portletRequest;
- servletRequest = null;
- }
-
- public void setAttribute(String name, Object value) {
- if (servletRequest != null) {
- servletRequest.setAttribute(name, value);
- } else {
- portletRequest.setAttribute(name, value);
- }
- }
-
- public void setSessionAttribute(String name, Object value) {
- if (servletRequest != null) {
- HttpSession session = servletRequest.getSession();
- if (session != null) {
- session.setAttribute(name, value);
- }
- } else {
- PortletSession portletSession = portletRequest
- .getPortletSession();
- if (portletSession != null) {
- portletSession.setAttribute(name, value);
- }
- }
- }
-
- public Object getSessionAttribute(String name) {
- if (servletRequest != null) {
- HttpSession session = servletRequest.getSession();
- if (session != null) {
- return session.getAttribute(name);
- }
- } else {
- PortletSession portletSession = portletRequest
- .getPortletSession();
- if (portletSession != null) {
- return portletSession.getAttribute(name);
- }
- }
- return null;
- }
- }
-
- /**
* Starts the timing of a request. This should be called before any
* processing of the request.
*
* @param request
* the request.
*/
- public void start(RequestWrapper request) {
+ public void start(WrappedRequest request) {
requestStartTime = System.nanoTime();
request.setAttribute("TOTAL", totalSessionTime);
request.setAttribute("LASTREQUEST", lastRequestTime);
@@ -116,7 +54,7 @@ public class RequestTimer {
* the request for which to get a valid timer.
* @return a valid timer.
*/
- public static RequestTimer get(RequestWrapper request) {
+ public static RequestTimer get(WrappedRequest request) {
RequestTimer timer = (RequestTimer) request
.getSessionAttribute(SESSION_ATTR_ID);
if (timer == null) {
@@ -136,7 +74,7 @@ public class RequestTimer {
* @param requestTimer
* the timer.
*/
- public static void set(RequestWrapper request, RequestTimer requestTimer) {
+ public static void set(WrappedRequest request, RequestTimer requestTimer) {
request.setSessionAttribute(RequestTimer.SESSION_ATTR_ID, requestTimer);
}
}
diff --git a/src/com/vaadin/terminal/gwt/server/ResourceReference.java b/src/com/vaadin/terminal/gwt/server/ResourceReference.java
new file mode 100644
index 0000000000..56f2bed896
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ResourceReference.java
@@ -0,0 +1,51 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.server;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.ApplicationResource;
+import com.vaadin.terminal.ExternalResource;
+import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.terminal.gwt.client.communication.URLReference;
+
+public class ResourceReference extends URLReference {
+
+ private Resource resource;
+
+ public ResourceReference(Resource resource) {
+ this.resource = resource;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ @Override
+ public String getURL() {
+ if (resource instanceof ExternalResource) {
+ return ((ExternalResource) resource).getURL();
+ } else if (resource instanceof ApplicationResource) {
+ final ApplicationResource r = (ApplicationResource) resource;
+ final Application a = r.getApplication();
+ if (a == null) {
+ throw new RuntimeException(
+ "An ApplicationResource ("
+ + r.getClass().getName()
+ + " must be attached to an application when it is sent to the client.");
+ }
+ final String uri = a.getRelativeLocation(r);
+ return uri;
+ } else if (resource instanceof ThemeResource) {
+ final String uri = "theme://"
+ + ((ThemeResource) resource).getResourceId();
+ return uri;
+ } else {
+ throw new RuntimeException(getClass().getSimpleName()
+ + " does not support resources of type: "
+ + resource.getClass().getName());
+ }
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/RpcManager.java b/src/com/vaadin/terminal/gwt/server/RpcManager.java
new file mode 100644
index 0000000000..d240ab8467
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/RpcManager.java
@@ -0,0 +1,17 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.Serializable;
+
+/**
+ * Server side RPC manager that can invoke methods based on RPC calls received
+ * from the client.
+ *
+ * @since 7.0
+ */
+public interface RpcManager extends Serializable {
+ public void applyInvocation(ServerRpcMethodInvocation invocation);
+}
diff --git a/src/com/vaadin/terminal/gwt/server/RpcTarget.java b/src/com/vaadin/terminal/gwt/server/RpcTarget.java
new file mode 100644
index 0000000000..b280f5c6b5
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/RpcTarget.java
@@ -0,0 +1,28 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.VariableOwner;
+
+/**
+ * Marker interface for server side classes that can receive RPC calls.
+ *
+ * This plays a role similar to that of {@link VariableOwner}.
+ *
+ * @since 7.0
+ */
+public interface RpcTarget extends Serializable {
+ /**
+ * Returns the RPC manager instance to use when receiving calls for an RPC
+ * interface.
+ *
+ * @param rpcInterface
+ * interface for which the call was made
+ * @return RpcManager or null if none found for the interface
+ */
+ public RpcManager getRpcManager(Class<?> rpcInterface);
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java
new file mode 100644
index 0000000000..07f83864c2
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java
@@ -0,0 +1,138 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.terminal.gwt.client.Connector;
+
+/**
+ * Server side RPC manager that handles RPC calls coming from the client.
+ *
+ * Each {@link RpcTarget} (typically a {@link ClientConnector}) should have its
+ * own instance of {@link ServerRpcManager} if it wants to receive RPC calls
+ * from the client.
+ *
+ * @since 7.0
+ */
+public class ServerRpcManager<T> implements RpcManager {
+
+ private final T implementation;
+ private final Class<T> rpcInterface;
+
+ private static final Map<Class<?>, Class<?>> boxedTypes = new HashMap<Class<?>, Class<?>>();
+ static {
+ try {
+ Class<?>[] boxClasses = new Class<?>[] { Boolean.class, Byte.class,
+ Short.class, Character.class, Integer.class, Long.class,
+ Float.class, Double.class };
+ for (Class<?> boxClass : boxClasses) {
+ Field typeField = boxClass.getField("TYPE");
+ Class<?> primitiveType = (Class<?>) typeField.get(boxClass);
+ boxedTypes.put(primitiveType, boxClass);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Create a RPC manager for an RPC target.
+ *
+ * @param target
+ * RPC call target (normally a {@link Connector})
+ * @param implementation
+ * RPC interface implementation for the target
+ * @param rpcInterface
+ * RPC interface type
+ */
+ public ServerRpcManager(T implementation, Class<T> rpcInterface) {
+ this.implementation = implementation;
+ this.rpcInterface = rpcInterface;
+ }
+
+ /**
+ * Invoke a method in a server side RPC target class. This method is to be
+ * used by the RPC framework and unit testing tools only.
+ *
+ * @param target
+ * non-null target of the RPC call
+ * @param invocation
+ * method invocation to perform
+ */
+ public static void applyInvocation(RpcTarget target,
+ ServerRpcMethodInvocation invocation) {
+ RpcManager manager = target.getRpcManager(invocation
+ .getInterfaceClass());
+ if (manager != null) {
+ manager.applyInvocation(invocation);
+ } else {
+ getLogger()
+ .log(Level.WARNING,
+ "RPC call received for RpcTarget "
+ + target.getClass().getName()
+ + " ("
+ + invocation.getConnectorId()
+ + ") but the target has not registered any RPC interfaces");
+ }
+ }
+
+ /**
+ * Returns the RPC interface implementation for the RPC target.
+ *
+ * @return RPC interface implementation
+ */
+ protected T getImplementation() {
+ return implementation;
+ }
+
+ /**
+ * Returns the RPC interface type managed by this RPC manager instance.
+ *
+ * @return RPC interface type
+ */
+ protected Class<T> getRpcInterface() {
+ return rpcInterface;
+ }
+
+ /**
+ * Invoke a method in a server side RPC target class. This method is to be
+ * used by the RPC framework and unit testing tools only.
+ *
+ * @param invocation
+ * method invocation to perform
+ */
+ public void applyInvocation(ServerRpcMethodInvocation invocation) {
+ Method method = invocation.getMethod();
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ Object[] args = new Object[parameterTypes.length];
+ Object[] arguments = invocation.getParameters();
+ for (int i = 0; i < args.length; i++) {
+ // no conversion needed for basic cases
+ // Class<?> type = parameterTypes[i];
+ // if (type.isPrimitive()) {
+ // type = boxedTypes.get(type);
+ // }
+ args[i] = arguments[i];
+ }
+ try {
+ method.invoke(implementation, args);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to invoke method "
+ + invocation.getMethodName() + " in "
+ + invocation.getInterfaceName(), e);
+ }
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(ServerRpcManager.class.getName());
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
new file mode 100644
index 0000000000..6f278f7797
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java
@@ -0,0 +1,107 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.server;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public class ServerRpcMethodInvocation extends MethodInvocation {
+
+ private static final Map<String, Method> invocationMethodCache = new ConcurrentHashMap<String, Method>(
+ 128, 0.75f, 1);
+
+ private final Method method;
+
+ private Class<? extends ServerRpc> interfaceClass;
+
+ public ServerRpcMethodInvocation(String connectorId, String interfaceName,
+ String methodName, int parameterCount) {
+ super(connectorId, interfaceName, methodName);
+
+ interfaceClass = findClass();
+ method = findInvocationMethod(interfaceClass, methodName,
+ parameterCount);
+ }
+
+ private Class<? extends ServerRpc> findClass() {
+ try {
+ Class<?> rpcInterface = Class.forName(getInterfaceName());
+ if (!ServerRpc.class.isAssignableFrom(rpcInterface)) {
+ throw new IllegalArgumentException("The interface "
+ + getInterfaceName() + "is not a server RPC interface.");
+ }
+ return (Class<? extends ServerRpc>) rpcInterface;
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("The server RPC interface "
+ + getInterfaceName() + " could not be found", e);
+ } finally {
+
+ }
+ }
+
+ public Class<? extends ServerRpc> getInterfaceClass() {
+ return interfaceClass;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ /**
+ * Tries to find the method from the cache or alternatively by invoking
+ * {@link #doFindInvocationMethod(Class, String, int)} and updating the
+ * cache.
+ *
+ * @param targetType
+ * @param methodName
+ * @param parameterCount
+ * @return
+ */
+ private Method findInvocationMethod(Class<?> targetType, String methodName,
+ int parameterCount) {
+ // TODO currently only using method name and number of parameters as the
+ // signature
+ String signature = targetType.getName() + "." + methodName + "("
+ + parameterCount;
+ Method invocationMethod = invocationMethodCache.get(signature);
+
+ if (invocationMethod == null) {
+ invocationMethod = doFindInvocationMethod(targetType, methodName,
+ parameterCount);
+
+ if (invocationMethod != null) {
+ invocationMethodCache.put(signature, invocationMethod);
+ }
+ }
+
+ return invocationMethod;
+ }
+
+ /**
+ * Tries to find the method from the class by looping through available
+ * methods.
+ *
+ * @param targetType
+ * @param methodName
+ * @param parameterCount
+ * @return
+ */
+ private Method doFindInvocationMethod(Class<?> targetType,
+ String methodName, int parameterCount) {
+ Method[] methods = targetType.getMethods();
+ for (Method method : methods) {
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (method.getName().equals(methodName)
+ && parameterTypes.length == parameterCount) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java b/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java
new file mode 100644
index 0000000000..9b1e60e621
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java
@@ -0,0 +1,73 @@
+package com.vaadin.terminal.gwt.server;
+
+import java.io.Serializable;
+
+import com.vaadin.Application;
+import com.vaadin.ui.Root;
+
+/*
+ @VaadinApache2LicenseForJavaFiles@
+ */
+
+class ServletPortletHelper implements Serializable {
+ public static class ApplicationClassException extends Exception {
+
+ public ApplicationClassException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ApplicationClassException(String message) {
+ super(message);
+ }
+ }
+
+ static Class<? extends Application> getApplicationClass(
+ String applicationParameter, String rootParameter,
+ ClassLoader classLoader) throws ApplicationClassException {
+ if (applicationParameter == null) {
+
+ // Validate the parameter value
+ verifyRootClass(rootParameter, classLoader);
+
+ // Application can be used if a valid rootLayout is defined
+ return Application.class;
+ }
+
+ try {
+ return (Class<? extends Application>) classLoader
+ .loadClass(applicationParameter);
+ } catch (final ClassNotFoundException e) {
+ throw new ApplicationClassException(
+ "Failed to load application class: " + applicationParameter,
+ e);
+ }
+ }
+
+ private static void verifyRootClass(String className,
+ ClassLoader classLoader) throws ApplicationClassException {
+ if (className == null) {
+ throw new ApplicationClassException(Application.ROOT_PARAMETER
+ + " init parameter not defined");
+ }
+
+ // Check that the root layout class can be found
+ try {
+ Class<?> rootClass = classLoader.loadClass(className);
+ if (!Root.class.isAssignableFrom(rootClass)) {
+ throw new ApplicationClassException(className
+ + " does not implement Root");
+ }
+ // Try finding a default constructor, else throw exception
+ rootClass.getConstructor();
+ } catch (ClassNotFoundException e) {
+ throw new ApplicationClassException(className
+ + " could not be loaded", e);
+ } catch (SecurityException e) {
+ throw new ApplicationClassException("Could not access " + className
+ + " class", e);
+ } catch (NoSuchMethodException e) {
+ throw new ApplicationClassException(className
+ + " doesn't have a public no-args constructor");
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/UnsupportedBrowserHandler.java b/src/com/vaadin/terminal/gwt/server/UnsupportedBrowserHandler.java
new file mode 100644
index 0000000000..334a7acf8d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/UnsupportedBrowserHandler.java
@@ -0,0 +1,88 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.server;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.RequestHandler;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
+
+/**
+ * A {@link RequestHandler} that presents an informative page if the browser in
+ * use is unsupported. Recognizes Chrome Frame and allow it to be used.
+ *
+ * <p>
+ * This handler is usually added to the application by
+ * {@link AbstractCommunicationManager}.
+ * </p>
+ */
+@SuppressWarnings("serial")
+public class UnsupportedBrowserHandler implements RequestHandler {
+
+ /** Cookie used to ignore browser checks */
+ public static final String FORCE_LOAD_COOKIE = "vaadinforceload=1";
+
+ public boolean handleRequest(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException {
+
+ if (request.getBrowserDetails() != null) {
+ // Check if the browser is supported
+ // If Chrome Frame is available we'll assume it's ok
+ WebBrowser b = request.getBrowserDetails().getWebBrowser();
+ if (b.isTooOldToFunctionProperly() && !b.isChromeFrameCapable()) {
+ // bypass if cookie set
+ String c = request.getHeader("Cookie");
+ if (c == null || !c.contains(FORCE_LOAD_COOKIE)) {
+ writeBrowserTooOldPage(request, response);
+ return true; // request handled
+ }
+ }
+ }
+
+ return false; // pass to next handler
+ }
+
+ /**
+ * Writes a page encouraging the user to upgrade to a more current browser.
+ *
+ * @param request
+ * @param response
+ * @throws IOException
+ */
+ protected void writeBrowserTooOldPage(WrappedRequest request,
+ WrappedResponse response) throws IOException {
+ Writer page = response.getWriter();
+ WebBrowser b = request.getBrowserDetails().getWebBrowser();
+
+ page.write("<html><body><h1>I'm sorry, but your browser is not supported</h1>"
+ + "<p>The version ("
+ + b.getBrowserMajorVersion()
+ + "."
+ + b.getBrowserMinorVersion()
+ + ") of the browser you are using "
+ + " is outdated and not supported.</p>"
+ + "<p>You should <b>consider upgrading</b> to a more up-to-date browser.</p> "
+ + "<p>The most popular browsers are <b>"
+ + " <a href=\"https://www.google.com/chrome\">Chrome</a>,"
+ + " <a href=\"http://www.mozilla.com/firefox\">Firefox</a>,"
+ + (b.isWindows() ? " <a href=\"http://windows.microsoft.com/en-US/internet-explorer/downloads/ie\">Internet Explorer</a>,"
+ : "")
+ + " <a href=\"http://www.opera.com/browser\">Opera</a>"
+ + " and <a href=\"http://www.apple.com/safari\">Safari</a>.</b><br/>"
+ + "Upgrading to the latest version of one of these <b>will make the web safer, faster and better looking.</b></p>"
+ + (b.isIE() ? "<script type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js\"></script>"
+ + "<p>If you can not upgrade your browser, please consider trying <a onclick=\"CFInstall.check({mode:'overlay'});return false;\" href=\"http://www.google.com/chromeframe\">Chrome Frame</a>.</p>"
+ : "") //
+ + "<p><sub><a onclick=\"document.cookie='"
+ + FORCE_LOAD_COOKIE
+ + "';window.location.reload();return false;\" href=\"#\">Continue without updating</a> (not recommended)</sub></p>"
+ + "</body>\n" + "</html>");
+
+ page.close();
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/WebBrowser.java b/src/com/vaadin/terminal/gwt/server/WebBrowser.java
index a57a4c65d2..358f6f38fb 100644
--- a/src/com/vaadin/terminal/gwt/server/WebBrowser.java
+++ b/src/com/vaadin/terminal/gwt/server/WebBrowser.java
@@ -8,6 +8,7 @@ import java.util.Date;
import java.util.Locale;
import com.vaadin.terminal.Terminal;
+import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.gwt.client.VBrowserDetails;
/**
@@ -189,6 +190,34 @@ public class WebBrowser implements Terminal {
}
/**
+ * Tests whether the user is using Chrome Frame.
+ *
+ * @return true if the user is using Chrome Frame, false if the user is not
+ * using Chrome or if no information on the browser is present
+ */
+ public boolean isChromeFrame() {
+ if (browserDetails == null) {
+ return false;
+ }
+
+ return browserDetails.isChromeFrame();
+ }
+
+ /**
+ * Tests whether the user's browser is Chrome Frame capable.
+ *
+ * @return true if the user can use Chrome Frame, false if the user can not
+ * or if no information on the browser is present
+ */
+ public boolean isChromeFrameCapable() {
+ if (browserDetails == null) {
+ return false;
+ }
+
+ return browserDetails.isChromeFrameCapable();
+ }
+
+ /**
* Gets the major version of the browser the user is using.
*
* <p>
@@ -417,25 +446,50 @@ public class WebBrowser implements Terminal {
* only. Updates all properties in the class according to the given
* information.
*
- * @param locale
- * The browser primary locale
- * @param address
- * The browser ip address
- * @param secureConnection
- * true if using an https connection
- * @param agent
- * Raw userAgent string from the browser
+ * @param request
+ * the wrapped request to read the information from
*/
- void updateRequestDetails(Locale locale, String address,
- boolean secureConnection, String agent) {
- this.locale = locale;
- this.address = address;
- this.secureConnection = secureConnection;
+ void updateRequestDetails(WrappedRequest request) {
+ locale = request.getLocale();
+ address = request.getRemoteAddr();
+ secureConnection = request.isSecure();
+ String agent = request.getHeader("user-agent");
+
if (agent != null) {
browserApplication = agent;
browserDetails = new VBrowserDetails(agent);
}
+ if (request.getParameter("sw") != null) {
+ updateClientSideDetails(request.getParameter("sw"),
+ request.getParameter("sh"), request.getParameter("cw"),
+ request.getParameter("ch"), request.getParameter("tzo"),
+ request.getParameter("rtzo"), request.getParameter("dstd"),
+ request.getParameter("dston"),
+ request.getParameter("curdate"),
+ request.getParameter("td") != null);
+ }
+ }
+
+ /**
+ * Checks if the browser is so old that it simply won't work with a Vaadin
+ * application. Can be used to redirect to an alternative page, show
+ * alternative content or similar.
+ *
+ * When this method returns true chances are very high that the browser
+ * won't work and it does not make sense to direct the user to the Vaadin
+ * application.
+ *
+ * @return true if the browser won't work, false if not the browser is
+ * supported or might work
+ */
+ public boolean isTooOldToFunctionProperly() {
+ if (browserDetails == null) {
+ // Don't know, so assume it will work
+ return false;
+ }
+
+ return browserDetails.isTooOldToFunctionProperly();
}
}
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java
new file mode 100644
index 0000000000..b6f1a192cb
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletRequest.java
@@ -0,0 +1,109 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.CombinedRequest;
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.WrappedRequest;
+
+/**
+ * Wrapper for {@link HttpServletRequest}.
+ *
+ * @author Vaadin Ltd.
+ * @since 7.0
+ *
+ * @see WrappedRequest
+ * @see WrappedHttpServletResponse
+ */
+public class WrappedHttpServletRequest extends HttpServletRequestWrapper
+ implements WrappedRequest {
+
+ private final DeploymentConfiguration deploymentConfiguration;
+
+ /**
+ * Wraps a http servlet request and associates with a deployment
+ * configuration
+ *
+ * @param request
+ * the http servlet request to wrap
+ * @param deploymentConfiguration
+ * the associated deployment configuration
+ */
+ public WrappedHttpServletRequest(HttpServletRequest request,
+ DeploymentConfiguration deploymentConfiguration) {
+ super(request);
+ this.deploymentConfiguration = deploymentConfiguration;
+ }
+
+ public String getRequestPathInfo() {
+ return getPathInfo();
+ }
+
+ public int getSessionMaxInactiveInterval() {
+ return getSession().getMaxInactiveInterval();
+ }
+
+ public Object getSessionAttribute(String name) {
+ return getSession().getAttribute(name);
+ }
+
+ public void setSessionAttribute(String name, Object attribute) {
+ getSession().setAttribute(name, attribute);
+ }
+
+ /**
+ * Gets the original, unwrapped HTTP servlet request.
+ *
+ * @return the servlet request
+ */
+ public HttpServletRequest getHttpServletRequest() {
+ return this;
+ }
+
+ public DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+
+ public BrowserDetails getBrowserDetails() {
+ return new BrowserDetails() {
+ public String getUriFragment() {
+ return null;
+ }
+
+ public String getWindowName() {
+ return null;
+ }
+
+ public WebBrowser getWebBrowser() {
+ WebApplicationContext context = (WebApplicationContext) Application
+ .getCurrentApplication().getContext();
+ return context.getBrowser();
+ }
+ };
+ }
+
+ /**
+ * Helper method to get a <code>WrappedHttpServletRequest</code> from a
+ * <code>WrappedRequest</code>. Aside from casting, this method also takes
+ * care of situations where there's another level of wrapping.
+ *
+ * @param request
+ * a wrapped request
+ * @return a wrapped http servlet request
+ * @throws ClassCastException
+ * if the wrapped request doesn't wrap a http servlet request
+ */
+ public static WrappedHttpServletRequest cast(WrappedRequest request) {
+ if (request instanceof CombinedRequest) {
+ CombinedRequest combinedRequest = (CombinedRequest) request;
+ request = combinedRequest.getSecondRequest();
+ }
+ return (WrappedHttpServletRequest) request;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedHttpServletResponse.java b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletResponse.java
new file mode 100644
index 0000000000..14a391b21f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/WrappedHttpServletResponse.java
@@ -0,0 +1,73 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.WrappedResponse;
+
+/**
+ * Wrapper for {@link HttpServletResponse}.
+ *
+ * @author Vaadin Ltd.
+ * @since 7.0
+ *
+ * @see WrappedResponse
+ * @see WrappedHttpServletRequest
+ */
+public class WrappedHttpServletResponse extends HttpServletResponseWrapper
+ implements WrappedResponse {
+
+ private DeploymentConfiguration deploymentConfiguration;
+
+ /**
+ * Wraps a http servlet response and an associated deployment configuration
+ *
+ * @param response
+ * the http servlet response to wrap
+ * @param deploymentConfiguration
+ * the associated deployment configuration
+ */
+ public WrappedHttpServletResponse(HttpServletResponse response,
+ DeploymentConfiguration deploymentConfiguration) {
+ super(response);
+ this.deploymentConfiguration = deploymentConfiguration;
+ }
+
+ /**
+ * Gets the original unwrapped <code>HttpServletResponse</code>
+ *
+ * @return the unwrapped response
+ */
+ public HttpServletResponse getHttpServletResponse() {
+ return this;
+ }
+
+ public void setCacheTime(long milliseconds) {
+ doSetCacheTime(this, milliseconds);
+ }
+
+ // Implementation shared with WrappedPortletResponse
+ static void doSetCacheTime(WrappedResponse response, long milliseconds) {
+ if (milliseconds <= 0) {
+ response.setHeader("Cache-Control", "no-cache");
+ response.setHeader("Pragma", "no-cache");
+ response.setDateHeader("Expires", 0);
+ } else {
+ response.setHeader("Cache-Control", "max-age=" + milliseconds
+ / 1000);
+ response.setDateHeader("Expires", System.currentTimeMillis()
+ + milliseconds);
+ // Required to apply caching in some Tomcats
+ response.setHeader("Pragma", "cache");
+ }
+ }
+
+ public DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java
new file mode 100644
index 0000000000..3838695aa3
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java
@@ -0,0 +1,189 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.ClientDataRequest;
+import javax.portlet.PortletRequest;
+import javax.portlet.ResourceRequest;
+
+import com.vaadin.Application;
+import com.vaadin.terminal.CombinedRequest;
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.WrappedRequest;
+
+/**
+ * Wrapper for {@link PortletRequest} and its subclasses.
+ *
+ * @author Vaadin Ltd.
+ * @since 7.0
+ *
+ * @see WrappedRequest
+ * @see WrappedPortletResponse
+ */
+public class WrappedPortletRequest implements WrappedRequest {
+
+ private final PortletRequest request;
+ private final DeploymentConfiguration deploymentConfiguration;
+
+ /**
+ * Wraps a portlet request and an associated deployment configuration
+ *
+ * @param request
+ * the portlet request to wrap
+ * @param deploymentConfiguration
+ * the associated deployment configuration
+ */
+ public WrappedPortletRequest(PortletRequest request,
+ DeploymentConfiguration deploymentConfiguration) {
+ this.request = request;
+ this.deploymentConfiguration = deploymentConfiguration;
+ }
+
+ public Object getAttribute(String name) {
+ return request.getAttribute(name);
+ }
+
+ public int getContentLength() {
+ try {
+ return ((ClientDataRequest) request).getContentLength();
+ } catch (ClassCastException e) {
+ throw new IllegalStateException(
+ "Content lenght only available for ClientDataRequests");
+ }
+ }
+
+ public InputStream getInputStream() throws IOException {
+ try {
+ return ((ClientDataRequest) request).getPortletInputStream();
+ } catch (ClassCastException e) {
+ throw new IllegalStateException(
+ "Input data only available for ClientDataRequests");
+ }
+ }
+
+ public String getParameter(String name) {
+ return request.getParameter(name);
+ }
+
+ public Map<String, String[]> getParameterMap() {
+ return request.getParameterMap();
+ }
+
+ public void setAttribute(String name, Object o) {
+ request.setAttribute(name, o);
+ }
+
+ public String getRequestPathInfo() {
+ if (request instanceof ResourceRequest) {
+ return ((ResourceRequest) request).getResourceID();
+ } else {
+ return null;
+ }
+ }
+
+ public int getSessionMaxInactiveInterval() {
+ return request.getPortletSession().getMaxInactiveInterval();
+ }
+
+ public Object getSessionAttribute(String name) {
+ return request.getPortletSession().getAttribute(name);
+ }
+
+ public void setSessionAttribute(String name, Object attribute) {
+ request.getPortletSession().setAttribute(name, attribute);
+ }
+
+ /**
+ * Gets the original, unwrapped portlet request.
+ *
+ * @return the unwrapped portlet request
+ */
+ public PortletRequest getPortletRequest() {
+ return request;
+ }
+
+ public String getContentType() {
+ try {
+ return ((ResourceRequest) request).getContentType();
+ } catch (ClassCastException e) {
+ throw new IllegalStateException(
+ "Content type only available for ResourceRequests");
+ }
+ }
+
+ public BrowserDetails getBrowserDetails() {
+ return new BrowserDetails() {
+ public String getUriFragment() {
+ return null;
+ }
+
+ public String getWindowName() {
+ return null;
+ }
+
+ public WebBrowser getWebBrowser() {
+ PortletApplicationContext2 context = (PortletApplicationContext2) Application
+ .getCurrentApplication().getContext();
+ return context.getBrowser();
+ }
+ };
+ }
+
+ public Locale getLocale() {
+ return request.getLocale();
+ }
+
+ public String getRemoteAddr() {
+ return null;
+ }
+
+ public boolean isSecure() {
+ return request.isSecure();
+ }
+
+ public String getHeader(String string) {
+ return null;
+ }
+
+ /**
+ * Reads a portal property from the portal context of the wrapped request.
+ *
+ * @param name
+ * a string with the name of the portal property to get
+ * @return a string with the value of the property, or <code>null</code> if
+ * the property is not defined
+ */
+ public String getPortalProperty(String name) {
+ return request.getPortalContext().getProperty(name);
+ }
+
+ public DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+
+ /**
+ * Helper method to get a <code>WrappedPortlettRequest</code> from a
+ * <code>WrappedRequest</code>. Aside from casting, this method also takes
+ * care of situations where there's another level of wrapping.
+ *
+ * @param request
+ * a wrapped request
+ * @return a wrapped portlet request
+ * @throws ClassCastException
+ * if the wrapped request doesn't wrap a portlet request
+ */
+ public static WrappedPortletRequest cast(WrappedRequest request) {
+ if (request instanceof CombinedRequest) {
+ CombinedRequest combinedRequest = (CombinedRequest) request;
+ request = combinedRequest.getSecondRequest();
+ }
+ return (WrappedPortletRequest) request;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/WrappedPortletResponse.java b/src/com/vaadin/terminal/gwt/server/WrappedPortletResponse.java
new file mode 100644
index 0000000000..8824396352
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/WrappedPortletResponse.java
@@ -0,0 +1,102 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletResponse;
+import javax.portlet.ResourceResponse;
+
+import com.vaadin.terminal.DeploymentConfiguration;
+import com.vaadin.terminal.WrappedResponse;
+
+/**
+ * Wrapper for {@link PortletResponse} and its subclasses.
+ *
+ * @author Vaadin Ltd.
+ * @since 7.0
+ *
+ * @see WrappedResponse
+ * @see WrappedPortletRequest
+ */
+public class WrappedPortletResponse implements WrappedResponse {
+ private static final DateFormat HTTP_DATE_FORMAT = new SimpleDateFormat(
+ "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
+ static {
+ HTTP_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ private final PortletResponse response;
+ private DeploymentConfiguration deploymentConfiguration;
+
+ /**
+ * Wraps a portlet response and an associated deployment configuration
+ *
+ * @param response
+ * the portlet response to wrap
+ * @param deploymentConfiguration
+ * the associated deployment configuration
+ */
+ public WrappedPortletResponse(PortletResponse response,
+ DeploymentConfiguration deploymentConfiguration) {
+ this.response = response;
+ this.deploymentConfiguration = deploymentConfiguration;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return ((MimeResponse) response).getPortletOutputStream();
+ }
+
+ /**
+ * Gets the original, unwrapped portlet response.
+ *
+ * @return the unwrapped portlet response
+ */
+ public PortletResponse getPortletResponse() {
+ return response;
+ }
+
+ public void setContentType(String type) {
+ ((MimeResponse) response).setContentType(type);
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ return ((MimeResponse) response).getWriter();
+ }
+
+ public void setStatus(int responseStatus) {
+ response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
+ Integer.toString(responseStatus));
+ }
+
+ public void setHeader(String name, String value) {
+ response.setProperty(name, value);
+ }
+
+ public void setDateHeader(String name, long timestamp) {
+ response.setProperty(name, HTTP_DATE_FORMAT.format(new Date(timestamp)));
+ }
+
+ public void setCacheTime(long milliseconds) {
+ WrappedHttpServletResponse.doSetCacheTime(this, milliseconds);
+ }
+
+ public void sendError(int errorCode, String message) throws IOException {
+ setStatus(errorCode);
+ getWriter().write(message);
+ }
+
+ public DeploymentConfiguration getDeploymentConfiguration() {
+ return deploymentConfiguration;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
index 2e4ce39513..6a0aa0f4c2 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java
@@ -31,8 +31,7 @@ import java.util.logging.Logger;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.ui.ClientWidget;
+import com.vaadin.terminal.gwt.server.ClientConnector;
/**
* Utility class to collect widgetset related information from classpath.
@@ -93,29 +92,27 @@ public class ClassPathExplorer {
}
/**
- * Finds server side widgets with {@link ClientWidget} annotation on the
- * class path (entries that can contain widgets/widgetsets - see
- * {@link #getRawClasspathEntries()}).
+ * Finds server side widgets with ClientWidget annotation on the class path
+ * (entries that can contain widgets/widgetsets - see
+ * getRawClasspathEntries()).
*
* As a side effect, also accept criteria are searched under the same class
* path entries and added into the acceptCriterion collection.
*
- * @return a collection of {@link Paintable} classes
+ * @return a collection of {@link ClientConnector} classes
*/
- public static Collection<Class<? extends Paintable>> getPaintablesHavingWidgetAnnotation() {
- logger.info("Searching for paintables..");
+ public static void findAcceptCriteria() {
+ logger.info("Searching for accept criteria..");
long start = System.currentTimeMillis();
- Collection<Class<? extends Paintable>> paintables = new HashSet<Class<? extends Paintable>>();
Set<String> keySet = classpathLocations.keySet();
for (String url : keySet) {
- logger.fine("Searching for paintables in "
+ logger.fine("Searching for accept criteria in "
+ classpathLocations.get(url));
- searchForPaintables(classpathLocations.get(url), url, paintables);
+ searchForPaintables(classpathLocations.get(url), url);
}
long end = System.currentTimeMillis();
logger.info("Search took " + (end - start) + "ms");
- return paintables;
}
@@ -129,7 +126,7 @@ public class ClassPathExplorer {
if (acceptCriterion.isEmpty()) {
// accept criterion are searched as a side effect, normally after
// paintable detection
- getPaintablesHavingWidgetAnnotation();
+ findAcceptCriteria();
}
return acceptCriterion;
}
@@ -449,7 +446,7 @@ public class ClassPathExplorer {
/**
* Searches for all paintable classes and accept criteria under a location
- * based on {@link ClientWidget} and {@link ClientCriterion} annotations.
+ * based on {@link ClientCriterion} annotations.
*
* Note that client criteria are updated directly to the
* {@link #acceptCriterion} field, whereas paintables are added to the
@@ -457,11 +454,9 @@ public class ClassPathExplorer {
*
* @param location
* @param locationString
- * @param paintables
*/
private final static void searchForPaintables(URL location,
- String locationString,
- Collection<Class<? extends Paintable>> paintables) {
+ String locationString) {
// Get a File object for the package
File directory = new File(location.getFile());
@@ -478,7 +473,7 @@ public class ClassPathExplorer {
String packageName = locationString
.substring(locationString.lastIndexOf("/") + 1);
classname = packageName + "." + classname;
- tryToAdd(classname, paintables);
+ tryToAdd(classname);
}
}
} else {
@@ -510,7 +505,7 @@ public class ClassPathExplorer {
classname = classname.substring(1);
}
classname = classname.replace('/', '.');
- tryToAdd(classname, paintables);
+ tryToAdd(classname);
}
}
}
@@ -542,17 +537,13 @@ public class ClassPathExplorer {
private static Set<Class<? extends AcceptCriterion>> acceptCriterion = new HashSet<Class<? extends AcceptCriterion>>();
/**
- * Checks a class for the {@link ClientWidget} and {@link ClientCriterion}
- * annotations, and adds it to the appropriate collection if it has either.
+ * Checks a class for the {@link ClientCriterion} annotations, and adds it
+ * to the appropriate collection.
*
* @param fullclassName
- * @param paintables
- * the collection to which to add server side classes with
- * {@link ClientWidget} annotation
*/
@SuppressWarnings("unchecked")
- private static void tryToAdd(final String fullclassName,
- Collection<Class<? extends Paintable>> paintables) {
+ private static void tryToAdd(final String fullclassName) {
PrintStream out = System.out;
PrintStream err = System.err;
Throwable errorToShow = null;
@@ -563,10 +554,7 @@ public class ClassPathExplorer {
Class<?> c = Class.forName(fullclassName);
- if (c.getAnnotation(ClientWidget.class) != null) {
- paintables.add((Class<? extends Paintable>) c);
- // System.out.println("Found paintable " + fullclassName);
- } else if (c.getAnnotation(ClientCriterion.class) != null) {
+ if (c.getAnnotation(ClientCriterion.class) != null) {
acceptCriterion.add((Class<? extends AcceptCriterion>) c);
}
} catch (UnsupportedClassVersionError e) {
@@ -667,10 +655,9 @@ public class ClassPathExplorer {
* Test method for helper tool
*/
public static void main(String[] args) {
- Collection<Class<? extends Paintable>> paintables = ClassPathExplorer
- .getPaintablesHavingWidgetAnnotation();
- logger.info("Found annotated paintables:");
- for (Class<? extends Paintable> cls : paintables) {
+ ClassPathExplorer.findAcceptCriteria();
+ logger.info("Found client criteria:");
+ for (Class<? extends AcceptCriterion> cls : acceptCriterion) {
logger.info(cls.getCanonicalName());
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
index 4ea4cbb8fe..f0d6f0453b 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java
@@ -6,57 +6,58 @@ package com.vaadin.terminal.gwt.widgetsetutils;
import java.util.Collection;
import java.util.HashSet;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.ui.ClientWidget;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
/**
* An abstract helper class that can be used to easily build a widgetset with
* customized load styles for each components. In three abstract methods one can
- * override the default values given in {@link ClientWidget} annotations.
+ * override the default values given in {@link Connect} annotations.
*
* @see WidgetMapGenerator
*
*/
public abstract class CustomWidgetMapGenerator extends WidgetMapGenerator {
- private Collection<Class<? extends Paintable>> eagerPaintables = new HashSet<Class<? extends Paintable>>();
- private Collection<Class<? extends Paintable>> lazyPaintables = new HashSet<Class<? extends Paintable>>();
- private Collection<Class<? extends Paintable>> deferredPaintables = new HashSet<Class<? extends Paintable>>();
+ private Collection<Class<? extends ComponentConnector>> eagerPaintables = new HashSet<Class<? extends ComponentConnector>>();
+ private Collection<Class<? extends ComponentConnector>> lazyPaintables = new HashSet<Class<? extends ComponentConnector>>();
+ private Collection<Class<? extends ComponentConnector>> deferredPaintables = new HashSet<Class<? extends ComponentConnector>>();
@Override
- protected LoadStyle getLoadStyle(Class<? extends Paintable> paintableType) {
+ protected LoadStyle getLoadStyle(
+ Class<? extends ComponentConnector> connector) {
if (eagerPaintables == null) {
init();
}
- if (eagerPaintables.contains(paintableType)) {
+ if (eagerPaintables.contains(connector)) {
return LoadStyle.EAGER;
}
- if (lazyPaintables.contains(paintableType)) {
+ if (lazyPaintables.contains(connector)) {
return LoadStyle.LAZY;
}
- if (deferredPaintables.contains(paintableType)) {
+ if (deferredPaintables.contains(connector)) {
return LoadStyle.DEFERRED;
}
- return super.getLoadStyle(paintableType);
+ return super.getLoadStyle(connector);
}
private void init() {
- Class<? extends Paintable>[] eagerComponents = getEagerComponents();
+ Class<? extends ComponentConnector>[] eagerComponents = getEagerComponents();
if (eagerComponents != null) {
- for (Class<? extends Paintable> class1 : eagerComponents) {
+ for (Class<? extends ComponentConnector> class1 : eagerComponents) {
eagerPaintables.add(class1);
}
}
- Class<? extends Paintable>[] lazyComponents = getEagerComponents();
+ Class<? extends ComponentConnector>[] lazyComponents = getEagerComponents();
if (lazyComponents != null) {
- for (Class<? extends Paintable> class1 : lazyComponents) {
+ for (Class<? extends ComponentConnector> class1 : lazyComponents) {
lazyPaintables.add(class1);
}
}
- Class<? extends Paintable>[] deferredComponents = getEagerComponents();
+ Class<? extends ComponentConnector>[] deferredComponents = getEagerComponents();
if (deferredComponents != null) {
- for (Class<? extends Paintable> class1 : deferredComponents) {
+ for (Class<? extends ComponentConnector> class1 : deferredComponents) {
deferredPaintables.add(class1);
}
}
@@ -66,18 +67,18 @@ public abstract class CustomWidgetMapGenerator extends WidgetMapGenerator {
* @return an array of components whose load style should be overridden to
* {@link LoadStyle#EAGER}
*/
- protected abstract Class<? extends Paintable>[] getEagerComponents();
+ protected abstract Class<? extends ComponentConnector>[] getEagerComponents();
/**
* @return an array of components whose load style should be overridden to
* {@link LoadStyle#LAZY}
*/
- protected abstract Class<? extends Paintable>[] getLazyComponents();
+ protected abstract Class<? extends ComponentConnector>[] getLazyComponents();
/**
* @return an array of components whose load style should be overridden to
* {@link LoadStyle#DEFERRED}
*/
- protected abstract Class<? extends Paintable>[] getDeferredComponents();
+ protected abstract Class<? extends ComponentConnector>[] getDeferredComponents();
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
index 6381a3b4cb..8a1dfee3b5 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java
@@ -3,8 +3,8 @@
*/
package com.vaadin.terminal.gwt.widgetsetutils;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
/**
* WidgetMap generator that builds a widgetset that packs all included widgets
@@ -21,8 +21,10 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
*
*/
public class EagerWidgetMapGenerator extends WidgetMapGenerator {
+
@Override
- protected LoadStyle getLoadStyle(Class<? extends Paintable> paintableType) {
+ protected LoadStyle getLoadStyle(
+ Class<? extends ComponentConnector> connector) {
return LoadStyle.EAGER;
}
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
index 7de72f09ce..729a999a21 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java
@@ -3,8 +3,8 @@
*/
package com.vaadin.terminal.gwt.widgetsetutils;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
/**
* WidgetMap generator that builds a widgetset that optimizes the transferred
@@ -16,7 +16,8 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
*/
public class LazyWidgetMapGenerator extends WidgetMapGenerator {
@Override
- protected LoadStyle getLoadStyle(Class<? extends Paintable> paintableType) {
+ protected LoadStyle getLoadStyle(
+ Class<? extends ComponentConnector> connector) {
return LoadStyle.LAZY;
}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java
new file mode 100644
index 0000000000..2899061204
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java
@@ -0,0 +1,197 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.RpcManager;
+
+/**
+ * GWT generator that creates an implementation for {@link RpcManager} on the
+ * client side classes for executing RPC calls received from the the server.
+ *
+ * @since 7.0
+ */
+public class RpcManagerGenerator extends Generator {
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+
+ String packageName = null;
+ String className = null;
+ try {
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ // get classType and save instance variables
+ JClassType classType = typeOracle.getType(typeName);
+ packageName = classType.getPackage().getName();
+ className = classType.getSimpleSourceName() + "Impl";
+ // Generate class source code for SerializerMapImpl
+ generateClass(logger, context, packageName, className);
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR,
+ "SerializerMapGenerator creation failed", e);
+ }
+ // return the fully qualifed name of the class generated
+ return packageName + "." + className;
+ }
+
+ /**
+ * Generate source code for RpcManagerImpl
+ *
+ * @param logger
+ * Logger object
+ * @param context
+ * Generator context
+ * @param packageName
+ * package name for the class to generate
+ * @param className
+ * class name for the class to generate
+ */
+ private void generateClass(TreeLogger logger, GeneratorContext context,
+ String packageName, String className) {
+ // get print writer that receives the source code
+ PrintWriter printWriter = null;
+ printWriter = context.tryCreate(logger, packageName, className);
+ // print writer if null, source code has ALREADY been generated
+ if (printWriter == null) {
+ return;
+ }
+ logger.log(Type.INFO,
+ "Detecting server to client RPC interface types...");
+ Date date = new Date();
+ TypeOracle typeOracle = context.getTypeOracle();
+ JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class
+ .getName());
+ JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes();
+
+ // init composer, set class properties, create source writer
+ ClassSourceFileComposerFactory composer = null;
+ composer = new ClassSourceFileComposerFactory(packageName, className);
+ composer.addImport("com.google.gwt.core.client.GWT");
+ composer.addImplementedInterface(RpcManager.class.getName());
+ SourceWriter sourceWriter = composer.createSourceWriter(context,
+ printWriter);
+ sourceWriter.indent();
+
+ List<JClassType> rpcInterfaces = new ArrayList<JClassType>();
+
+ // iterate over RPC interfaces and create helper methods for each
+ // interface
+ for (JClassType type : rpcInterfaceSubtypes) {
+ if (null == type.isInterface()) {
+ // only interested in interfaces here, not implementations
+ continue;
+ }
+ rpcInterfaces.add(type);
+ // generate method to call methods of an RPC interface
+ sourceWriter.println("private void " + getInvokeMethodName(type)
+ + "(" + MethodInvocation.class.getName() + " invocation, "
+ + ConnectorMap.class.getName() + " connectorMap) {");
+ sourceWriter.indent();
+
+ // loop over the methods of the interface and its superinterfaces
+ // methods
+ for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) {
+ for (JMethod method : currentType.getMethods()) {
+ sourceWriter.println("if (\"" + method.getName()
+ + "\".equals(invocation.getMethodName())) {");
+ sourceWriter.indent();
+ // construct parameter string with appropriate casts
+ String paramString = "";
+ JType[] parameterTypes = method.getParameterTypes();
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ paramString = paramString + "("
+ + parameterTypes[i].getQualifiedSourceName()
+ + ") invocation.getParameters()[" + i + "]";
+ if (i < parameterTypes.length - 1) {
+ paramString = paramString + ", ";
+ }
+ }
+ sourceWriter
+ .println(ServerConnector.class.getName()
+ + " connector = connectorMap.getConnector(invocation.getConnectorId());");
+ sourceWriter
+ .println("for ("
+ + ClientRpc.class.getName()
+ + " rpcImplementation : connector.getRpcImplementations(\""
+ + type.getQualifiedSourceName() + "\")) {");
+ sourceWriter.indent();
+ sourceWriter.println("((" + type.getQualifiedSourceName()
+ + ") rpcImplementation)." + method.getName() + "("
+ + paramString + ");");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ sourceWriter.println("return;");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ }
+ }
+
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ logger.log(Type.DEBUG,
+ "Constructed helper method for server to client RPC for "
+ + type.getName());
+ }
+
+ // generate top-level "switch-case" method to select the correct
+ // previously generated method based on the RPC interface
+ sourceWriter.println("public void applyInvocation("
+ + MethodInvocation.class.getName() + " invocation, "
+ + ConnectorMap.class.getName() + " connectorMap) {");
+ sourceWriter.indent();
+
+ for (JClassType type : rpcInterfaces) {
+ sourceWriter.println("if (\"" + type.getQualifiedSourceName()
+ + "\".equals(invocation.getInterfaceName())) {");
+ sourceWriter.indent();
+ sourceWriter.println(getInvokeMethodName(type)
+ + "(invocation, connectorMap);");
+ sourceWriter.println("return;");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ logger.log(Type.INFO,
+ "Configured server to client RPC for " + type.getName());
+ }
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ // close generated class
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ // commit generated class
+ context.commit(logger, printWriter);
+ logger.log(Type.INFO,
+ "Done. (" + (new Date().getTime() - date.getTime()) / 1000
+ + "seconds)");
+
+ }
+
+ private String getInvokeMethodName(JClassType type) {
+ return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_");
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java
new file mode 100644
index 0000000000..040715fccf
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java
@@ -0,0 +1,126 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy.RpcProxyCreator;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+public class RpcProxyCreatorGenerator extends Generator {
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext ctx,
+ String requestedClassName) throws UnableToCompleteException {
+ logger.log(TreeLogger.DEBUG, "Running RpcProxyCreatorGenerator");
+ TypeOracle typeOracle = ctx.getTypeOracle();
+ assert (typeOracle != null);
+
+ JClassType requestedType = typeOracle.findType(requestedClassName);
+ String packageName = requestedType.getPackage().getName();
+ String className = requestedType.getSimpleSourceName() + "Impl";
+ if (requestedType == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ + requestedClassName + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ createType(logger, ctx, packageName, className);
+ return packageName + "." + className;
+ }
+
+ private void createType(TreeLogger logger, GeneratorContext context,
+ String packageName, String className) {
+ ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
+ packageName, className);
+
+ PrintWriter printWriter = context.tryCreate(logger,
+ composer.getCreatedPackage(),
+ composer.getCreatedClassShortName());
+ if (printWriter == null) {
+ // print writer is null if source code has already been generated
+ return;
+ }
+ Date date = new Date();
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ // init composer, set class properties, create source writer
+ composer.addImport(GWT.class.getCanonicalName());
+ composer.addImport(ServerRpc.class.getCanonicalName());
+ composer.addImport(ServerConnector.class.getCanonicalName());
+ composer.addImport(InitializableServerRpc.class.getCanonicalName());
+ composer.addImport(IllegalArgumentException.class.getCanonicalName());
+ composer.addImplementedInterface(RpcProxyCreator.class
+ .getCanonicalName());
+
+ SourceWriter sourceWriter = composer.createSourceWriter(context,
+ printWriter);
+ sourceWriter.indent();
+
+ sourceWriter
+ .println("public <T extends ServerRpc> T create(Class<T> rpcInterface, ServerConnector connector) {");
+ sourceWriter.indent();
+
+ sourceWriter
+ .println("if (rpcInterface == null || connector == null) {");
+ sourceWriter.indent();
+ sourceWriter
+ .println("throw new IllegalArgumentException(\"RpcInterface and/or connector cannot be null\");");
+ sourceWriter.outdent();
+
+ JClassType initializableInterface = typeOracle.findType(ServerRpc.class
+ .getCanonicalName());
+
+ for (JClassType rpcType : initializableInterface.getSubtypes()) {
+ String rpcClassName = rpcType.getQualifiedSourceName();
+ if (InitializableServerRpc.class.getCanonicalName().equals(
+ rpcClassName)) {
+ // InitializableClientToServerRpc is a special marker interface
+ // that should not get a generated class
+ continue;
+ }
+ sourceWriter.println("} else if (rpcInterface == " + rpcClassName
+ + ".class) {");
+ sourceWriter.indent();
+ sourceWriter.println(rpcClassName + " rpc = GWT.create("
+ + rpcClassName + ".class);");
+ sourceWriter.println("((" + InitializableServerRpc.class.getName()
+ + ") rpc).initRpc(connector);");
+ sourceWriter.println("return (T) rpc;");
+ sourceWriter.outdent();
+ }
+
+ sourceWriter.println("} else {");
+ sourceWriter.indent();
+ sourceWriter
+ .println("throw new IllegalArgumentException(\"No RpcInterface of type \"+ rpcInterface.getName() + \" was found.\");");
+ sourceWriter.outdent();
+ // End of if
+ sourceWriter.println("}");
+ // End of method
+ sourceWriter.println("}");
+
+ // close generated class
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ // commit generated class
+ context.commit(logger, printWriter);
+ logger.log(Type.INFO, composer.getCreatedClassName() + " created in "
+ + (new Date().getTime() - date.getTime()) / 1000 + "seconds");
+
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java
new file mode 100644
index 0000000000..ad4e513049
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java
@@ -0,0 +1,142 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+
+/**
+ * GWT generator that creates client side proxy classes for making RPC calls
+ * from the client to the server.
+ *
+ * GWT.create() calls for interfaces extending {@link ServerRpc} are affected,
+ * and a proxy implementation is created. Note that the init(...) method of the
+ * proxy must be called before the proxy is used.
+ *
+ * @since 7.0
+ */
+public class RpcProxyGenerator extends Generator {
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext ctx,
+ String requestedClassName) throws UnableToCompleteException {
+ logger.log(TreeLogger.DEBUG, "Running RpcProxyGenerator", null);
+
+ TypeOracle typeOracle = ctx.getTypeOracle();
+ assert (typeOracle != null);
+
+ JClassType requestedType = typeOracle.findType(requestedClassName);
+ if (requestedType == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ + requestedClassName + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ String generatedClassName = "ServerRpc_"
+ + requestedType.getName().replaceAll("[$.]", "_");
+
+ JClassType initializableInterface = typeOracle
+ .findType(InitializableServerRpc.class.getCanonicalName());
+
+ ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
+ requestedType.getPackage().getName(), generatedClassName);
+ composer.addImplementedInterface(requestedType.getQualifiedSourceName());
+ composer.addImplementedInterface(initializableInterface
+ .getQualifiedSourceName());
+ composer.addImport(MethodInvocation.class.getCanonicalName());
+
+ PrintWriter printWriter = ctx.tryCreate(logger,
+ composer.getCreatedPackage(),
+ composer.getCreatedClassShortName());
+ if (printWriter != null) {
+ logger.log(Type.INFO, "Generating client proxy for RPC interface '"
+ + requestedType.getQualifiedSourceName() + "'");
+ SourceWriter writer = composer.createSourceWriter(ctx, printWriter);
+
+ // constructor
+ writer.println("public " + generatedClassName + "() {}");
+
+ // initialization etc.
+ writeCommonFieldsAndMethods(logger, writer, typeOracle);
+
+ // actual proxy methods forwarding calls to the server
+ writeRemoteProxyMethods(logger, writer, typeOracle, requestedType,
+ requestedType.isClassOrInterface().getInheritableMethods());
+
+ // End of class
+ writer.outdent();
+ writer.println("}");
+
+ ctx.commit(logger, printWriter);
+ }
+
+ return composer.getCreatedClassName();
+ }
+
+ private void writeCommonFieldsAndMethods(TreeLogger logger,
+ SourceWriter writer, TypeOracle typeOracle) {
+ JClassType applicationConnectionClass = typeOracle
+ .findType(ApplicationConnection.class.getCanonicalName());
+
+ // fields
+ writer.println("private " + ServerConnector.class.getName()
+ + " connector;");
+
+ // init method from the RPC interface
+ writer.println("public void initRpc(" + ServerConnector.class.getName()
+ + " connector) {");
+ writer.indent();
+ writer.println("this.connector = connector;");
+ writer.outdent();
+ writer.println("}");
+ }
+
+ private static void writeRemoteProxyMethods(TreeLogger logger,
+ SourceWriter writer, TypeOracle typeOracle,
+ JClassType requestedType, JMethod[] methods) {
+ for (JMethod m : methods) {
+ writer.print(m.getReadableDeclaration(false, false, false, false,
+ true));
+ writer.println(" {");
+ writer.indent();
+
+ writer.print("connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(connector.getConnectorId(), \""
+ + requestedType.getQualifiedBinaryName() + "\", \"");
+ writer.print(m.getName());
+ writer.print("\", new Object[] {");
+ // new Object[] { ... } for parameters - autoboxing etc. by the
+ // compiler
+ JParameter[] parameters = m.getParameters();
+ boolean first = true;
+ for (JParameter p : parameters) {
+ if (!first) {
+ writer.print(", ");
+ }
+ first = false;
+
+ writer.print(p.getName());
+ }
+ writer.println("}), true);");
+
+ writer.outdent();
+ writer.println("}");
+ }
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
new file mode 100644
index 0000000000..d3ed9fe484
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
@@ -0,0 +1,272 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
+import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
+import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
+import com.vaadin.terminal.gwt.client.communication.SerializerMap;
+
+/**
+ * GWT generator for creating serializer classes for custom classes sent from
+ * server to client.
+ *
+ * Only fields with a correspondingly named setter are deserialized.
+ *
+ * @since 7.0
+ */
+public class SerializerGenerator extends Generator {
+
+ private static final String SUBTYPE_SEPARATOR = "___";
+ private static String beanSerializerPackageName = SerializerMap.class
+ .getPackage().getName();
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String beanTypeName) throws UnableToCompleteException {
+ JClassType beanType = context.getTypeOracle().findType(beanTypeName);
+ String beanSerializerClassName = getSerializerSimpleClassName(beanType);
+ try {
+ // Generate class source code
+ generateClass(logger, context, beanType, beanSerializerPackageName,
+ beanSerializerClassName);
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR, "SerializerGenerator failed for "
+ + beanType.getQualifiedSourceName(), e);
+ throw new UnableToCompleteException();
+ }
+
+ // return the fully qualifed name of the class generated
+ return getFullyQualifiedSerializerClassName(beanType);
+ }
+
+ /**
+ * Generate source code for a VaadinSerializer implementation.
+ *
+ * @param logger
+ * Logger object
+ * @param context
+ * Generator context
+ * @param beanType
+ * @param beanTypeName
+ * bean type for which the serializer is to be generated
+ * @param beanSerializerTypeName
+ * name of the serializer class to generate
+ */
+ private void generateClass(TreeLogger logger, GeneratorContext context,
+ JClassType beanType, String serializerPackageName,
+ String serializerClassName) {
+ // get print writer that receives the source code
+ PrintWriter printWriter = null;
+ printWriter = context.tryCreate(logger, serializerPackageName,
+ serializerClassName);
+
+ // print writer if null, source code has ALREADY been generated
+ if (printWriter == null) {
+ return;
+ }
+ Date date = new Date();
+ TypeOracle typeOracle = context.getTypeOracle();
+ String beanQualifiedSourceName = beanType.getQualifiedSourceName();
+ logger.log(Type.DEBUG, "Processing serializable type "
+ + beanQualifiedSourceName + "...");
+
+ // init composer, set class properties, create source writer
+ ClassSourceFileComposerFactory composer = null;
+ composer = new ClassSourceFileComposerFactory(serializerPackageName,
+ serializerClassName);
+ composer.addImport(GWT.class.getName());
+ composer.addImport(JSONArray.class.getName());
+ // composer.addImport(JSONObject.class.getName());
+ // composer.addImport(VPaintableMap.class.getName());
+ composer.addImport(JsonDecoder.class.getName());
+ // composer.addImport(VaadinSerializer.class.getName());
+
+ composer.addImplementedInterface(JSONSerializer.class.getName());
+
+ SourceWriter sourceWriter = composer.createSourceWriter(context,
+ printWriter);
+ sourceWriter.indent();
+
+ // Serializer
+
+ // public JSONValue serialize(Object value, ConnectorMap idMapper,
+ // ApplicationConnection connection) {
+ sourceWriter.println("public " + JSONObject.class.getName()
+ + " serialize(" + Object.class.getName() + " value, "
+ + ConnectorMap.class.getName() + " idMapper, "
+ + ApplicationConnection.class.getName() + " connection) {");
+ sourceWriter.indent();
+ // MouseEventDetails castedValue = (MouseEventDetails) value;
+ sourceWriter.println(beanQualifiedSourceName + " castedValue = ("
+ + beanQualifiedSourceName + ") value;");
+ // JSONObject json = new JSONObject();
+ sourceWriter.println(JSONObject.class.getName() + " json = new "
+ + JSONObject.class.getName() + "();");
+
+ for (JMethod setterMethod : getSetters(beanType)) {
+ String setterName = setterMethod.getName();
+ String fieldName = setterName.substring(3); // setZindex() -> ZIndex
+ String getterName = findGetter(beanType, setterMethod);
+
+ if (getterName == null) {
+ logger.log(TreeLogger.ERROR, "No getter found for " + fieldName
+ + ". Serialization will likely fail");
+ }
+ // json.put("button",
+ // JsonEncoder.encode(castedValue.getButton(), idMapper,
+ // connection));
+ sourceWriter.println("json.put(\"" + fieldName + "\", "
+ + JsonEncoder.class.getName() + ".encode(castedValue."
+ + getterName + "(), idMapper, connection));");
+ }
+ // return json;
+ sourceWriter.println("return json;");
+ // }
+ sourceWriter.println("}");
+
+ // Deserializer
+ sourceWriter.println("public " + beanQualifiedSourceName
+ + " deserialize(" + JSONObject.class.getName() + " jsonValue, "
+ + ConnectorMap.class.getName() + " idMapper, "
+ + ApplicationConnection.class.getName() + " connection) {");
+ sourceWriter.indent();
+
+ // VButtonState state = GWT.create(VButtonState.class);
+ sourceWriter.println(beanQualifiedSourceName + " state = GWT.create("
+ + beanQualifiedSourceName + ".class);");
+ for (JMethod method : getSetters(beanType)) {
+ String setterName = method.getName();
+ String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
+ JType setterParameterType = method.getParameterTypes()[0];
+
+ logger.log(Type.DEBUG, "* Processing field " + fieldName + " in "
+ + beanQualifiedSourceName + " (" + beanType.getName() + ")");
+
+ String jsonFieldName = "json_" + fieldName;
+ // JSONArray json_Height = (JSONArray) jsonValue.get("height");
+ sourceWriter.println("JSONArray " + jsonFieldName
+ + " = (JSONArray) jsonValue.get(\"" + fieldName + "\");");
+
+ // state.setHeight((String)
+ // JsonDecoder.decodeValue(jsonFieldValue,idMapper, connection));
+
+ String fieldType;
+ JPrimitiveType primitiveType = setterParameterType.isPrimitive();
+ if (primitiveType != null) {
+ // This is a primitive type -> must used the boxed type
+ fieldType = primitiveType.getQualifiedBoxedSourceName();
+ } else {
+ fieldType = setterParameterType.getQualifiedSourceName();
+ }
+
+ sourceWriter.println("state." + setterName + "((" + fieldType
+ + ") " + JsonDecoder.class.getName() + ".decodeValue("
+ + jsonFieldName + ", idMapper, connection));");
+ }
+
+ // return state;
+ sourceWriter.println("return state;");
+ sourceWriter.println("}");
+ sourceWriter.outdent();
+
+ // End of class
+ sourceWriter.println("}");
+ sourceWriter.outdent();
+
+ // commit generated class
+ context.commit(logger, printWriter);
+ logger.log(TreeLogger.INFO, "Generated Serializer class "
+ + getFullyQualifiedSerializerClassName(beanType));
+
+ }
+
+ private String findGetter(JClassType beanType, JMethod setterMethod) {
+ JType setterParameterType = setterMethod.getParameterTypes()[0];
+ String fieldName = setterMethod.getName().substring(3);
+ if (setterParameterType.getQualifiedSourceName().equals(
+ boolean.class.getName())) {
+ return "is" + fieldName;
+ } else {
+ return "get" + fieldName;
+ }
+ }
+
+ /**
+ * Returns a list of all setters found in the beanType or its parent class
+ *
+ * @param beanType
+ * The type to check
+ * @return A list of setter methods from the class and its parents
+ */
+ protected static List<JMethod> getSetters(JClassType beanType) {
+
+ List<JMethod> setterMethods = new ArrayList<JMethod>();
+
+ while (beanType != null
+ && !beanType.getQualifiedSourceName().equals(
+ Object.class.getName())) {
+ for (JMethod method : beanType.getMethods()) {
+ // Process all setters that have corresponding fields
+ if (!method.isPublic() || method.isStatic()
+ || !method.getName().startsWith("set")
+ || method.getParameterTypes().length != 1) {
+ // Not setter, skip to next method
+ continue;
+ }
+ setterMethods.add(method);
+ }
+ beanType = beanType.getSuperclass();
+ }
+
+ return setterMethods;
+ }
+
+ private String decapitalize(String name) {
+ return name.substring(0, 1).toLowerCase() + name.substring(1);
+ }
+
+ private static String getSerializerSimpleClassName(JClassType beanType) {
+ return getSimpleClassName(beanType) + "_Serializer";
+ }
+
+ private static String getSimpleClassName(JClassType type) {
+ if (type.isMemberType()) {
+ // Assumed to be static sub class
+ String baseName = getSimpleClassName(type.getEnclosingType());
+ String name = baseName + SUBTYPE_SEPARATOR
+ + type.getSimpleSourceName();
+ return name;
+ }
+ return type.getSimpleSourceName();
+ }
+
+ public static String getFullyQualifiedSerializerClassName(JClassType type) {
+ return beanSerializerPackageName + "."
+ + getSerializerSimpleClassName(type);
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
new file mode 100644
index 0000000000..013df4710c
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
@@ -0,0 +1,320 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
+import com.vaadin.terminal.gwt.client.communication.SerializerMap;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+/**
+ * GWT generator that creates a {@link SerializerMap} implementation (mapper
+ * from type string to serializer instance) and serializer classes for all
+ * subclasses of {@link SharedState}.
+ *
+ * @since 7.0
+ */
+public class SerializerMapGenerator extends Generator {
+
+ private String packageName;
+ private String className;
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+
+ try {
+ TypeOracle typeOracle = context.getTypeOracle();
+ Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers(
+ typeOracle, logger);
+ warnIfNotJavaSerializable(typesNeedingSerializers, typeOracle,
+ logger);
+ Set<JClassType> typesWithExistingSerializers = findTypesWithExistingSerializers(
+ typeOracle, logger);
+ Set<JClassType> serializerMappings = new HashSet<JClassType>();
+ serializerMappings.addAll(typesNeedingSerializers);
+ serializerMappings.addAll(typesWithExistingSerializers);
+ // get classType and save instance variables
+ JClassType classType = typeOracle.getType(typeName);
+ packageName = classType.getPackage().getName();
+ className = classType.getSimpleSourceName() + "Impl";
+ // Generate class source code for SerializerMapImpl
+ generateSerializerMap(serializerMappings, logger, context);
+
+ SerializerGenerator sg = new SerializerGenerator();
+ for (JClassType type : typesNeedingSerializers) {
+ sg.generate(logger, context, type.getQualifiedSourceName());
+ }
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR,
+ "SerializerMapGenerator creation failed", e);
+ throw new UnableToCompleteException();
+ }
+ // return the fully qualifed name of the class generated
+ return packageName + "." + className;
+ }
+
+ /**
+ * Emits a warning for all classes that are used in communication but do not
+ * implement java.io.Serializable. Implementing java.io.Serializable is not
+ * needed for communication but for the server side Application to be
+ * serializable i.e. work in GAE for instance.
+ *
+ * @param typesNeedingSerializers
+ * @param typeOracle
+ * @param logger
+ */
+ private void warnIfNotJavaSerializable(
+ Set<JClassType> typesNeedingSerializers, TypeOracle typeOracle,
+ TreeLogger logger) {
+ JClassType javaSerializable = typeOracle.findType(Serializable.class
+ .getName());
+ for (JClassType type : typesNeedingSerializers) {
+ boolean serializable = type.isAssignableTo(javaSerializable);
+ if (!serializable) {
+ logger.log(
+ Type.ERROR,
+ type
+ + " is used in RPC or shared state but does not implement "
+ + Serializable.class.getName()
+ + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type.");
+ }
+ }
+ }
+
+ private Set<JClassType> findTypesWithExistingSerializers(
+ TypeOracle typeOracle, TreeLogger logger) {
+ JClassType serializerInterface = typeOracle
+ .findType(JSONSerializer.class.getName());
+ Set<JClassType> types = new HashSet<JClassType>();
+ for (JClassType serializer : serializerInterface.getSubtypes()) {
+ JType[] deserializeParamTypes = new JType[] {
+ typeOracle.findType(JSONObject.class.getName()),
+ typeOracle.findType(ConnectorMap.class.getName()),
+ typeOracle.findType(ApplicationConnection.class.getName()) };
+ JMethod deserializeMethod = serializer.findMethod("deserialize",
+ deserializeParamTypes);
+ if (deserializeMethod == null) {
+ continue;
+ }
+
+ types.add(deserializeMethod.getReturnType().isClass());
+ }
+ return types;
+ }
+
+ /**
+ * Generate source code for SerializerMapImpl
+ *
+ * @param typesNeedingSerializers
+ *
+ * @param logger
+ * Logger object
+ * @param context
+ * Generator context
+ */
+ private void generateSerializerMap(Set<JClassType> typesNeedingSerializers,
+ TreeLogger logger, GeneratorContext context) {
+ // get print writer that receives the source code
+ PrintWriter printWriter = null;
+ printWriter = context.tryCreate(logger, packageName, className);
+ // print writer if null, source code has ALREADY been generated
+ if (printWriter == null) {
+ return;
+ }
+ Date date = new Date();
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ // init composer, set class properties, create source writer
+ ClassSourceFileComposerFactory composer = null;
+ composer = new ClassSourceFileComposerFactory(packageName, className);
+ composer.addImport("com.google.gwt.core.client.GWT");
+ composer.addImplementedInterface(SerializerMap.class.getName());
+ SourceWriter sourceWriter = composer.createSourceWriter(context,
+ printWriter);
+ sourceWriter.indent();
+
+ sourceWriter.println("public " + JSONSerializer.class.getName()
+ + " getSerializer(String type) {");
+ sourceWriter.indent();
+
+ // TODO cache serializer instances in a map
+ for (JClassType type : typesNeedingSerializers) {
+ sourceWriter.println("if (type.equals(\""
+ + type.getQualifiedBinaryName() + "\")) {");
+ sourceWriter.indent();
+ String serializerName = SerializerGenerator
+ .getFullyQualifiedSerializerClassName(type);
+ sourceWriter.println("return GWT.create(" + serializerName
+ + ".class);");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ logger.log(Type.INFO, "Configured serializer (" + serializerName
+ + ") for " + type.getName());
+ }
+ sourceWriter
+ .println("throw new RuntimeException(\"No serializer found for class \"+type);");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ // close generated class
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ // commit generated class
+ context.commit(logger, printWriter);
+ logger.log(Type.INFO,
+ "Done. (" + (new Date().getTime() - date.getTime()) / 1000
+ + "seconds)");
+
+ }
+
+ public Set<JClassType> findTypesNeedingSerializers(TypeOracle typeOracle,
+ TreeLogger logger) {
+ logger.log(Type.DEBUG, "Detecting serializable data types...");
+
+ HashSet<JClassType> types = new HashSet<JClassType>();
+
+ // Generate serializer classes for each subclass of SharedState
+ JClassType serializerType = typeOracle.findType(SharedState.class
+ .getName());
+ JClassType[] serializerSubtypes = serializerType.getSubtypes();
+ for (JClassType type : serializerSubtypes) {
+ types.add(type);
+ }
+
+ // Serializer classes might also be needed for RPC methods
+ for (Class<?> cls : new Class[] { ServerRpc.class, ClientRpc.class }) {
+ JClassType rpcType = typeOracle.findType(cls.getName());
+ JClassType[] serverRpcSubtypes = rpcType.getSubtypes();
+ for (JClassType type : serverRpcSubtypes) {
+ addMethodParameterTypes(type, types, logger);
+ }
+ }
+
+ // Add all types used from/in the types
+ for (Object t : types.toArray()) {
+ findSubTypesNeedingSerializers((JClassType) t, types);
+ }
+ logger.log(Type.DEBUG, "Serializable data types: " + types.toString());
+
+ return types;
+ }
+
+ private void addMethodParameterTypes(JClassType classContainingMethods,
+ Set<JClassType> types, TreeLogger logger) {
+ for (JMethod method : classContainingMethods.getMethods()) {
+ if (method.getName().equals("initRpc")) {
+ continue;
+ }
+ for (JType type : method.getParameterTypes()) {
+ addTypeIfNeeded(types, type);
+ }
+ }
+ }
+
+ public void findSubTypesNeedingSerializers(JClassType type,
+ Set<JClassType> serializableTypes) {
+ // Find all setters and look at their parameter type to determine if a
+ // new serializer is needed
+ for (JMethod setterMethod : SerializerGenerator.getSetters(type)) {
+ // The one and only parameter for the setter
+ JType setterType = setterMethod.getParameterTypes()[0];
+ addTypeIfNeeded(serializableTypes, setterType);
+ }
+ }
+
+ private void addTypeIfNeeded(Set<JClassType> serializableTypes, JType type) {
+ if (serializableTypes.contains(type)) {
+ return;
+ }
+ JParameterizedType parametrized = type.isParameterized();
+ if (parametrized != null) {
+ for (JClassType parameterType : parametrized.getTypeArgs()) {
+ addTypeIfNeeded(serializableTypes, parameterType);
+ }
+ }
+
+ if (serializationHandledByFramework(type)) {
+ return;
+ }
+
+ if (serializableTypes.contains(type)) {
+ return;
+ }
+
+ JClassType typeClass = type.isClass();
+ if (typeClass != null) {
+ // setterTypeClass is null at least for List<String>. It is
+ // possible that we need to handle the cases somehow, for
+ // instance for List<MyObject>.
+ serializableTypes.add(typeClass);
+ findSubTypesNeedingSerializers(typeClass, serializableTypes);
+ }
+ }
+
+ Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>();
+ {
+ frameworkHandledTypes.add(String.class);
+ frameworkHandledTypes.add(Boolean.class);
+ frameworkHandledTypes.add(Integer.class);
+ frameworkHandledTypes.add(Float.class);
+ frameworkHandledTypes.add(Double.class);
+ frameworkHandledTypes.add(Long.class);
+ frameworkHandledTypes.add(Enum.class);
+ frameworkHandledTypes.add(String[].class);
+ frameworkHandledTypes.add(Object[].class);
+ frameworkHandledTypes.add(Map.class);
+ frameworkHandledTypes.add(List.class);
+ frameworkHandledTypes.add(Set.class);
+
+ }
+
+ private boolean serializationHandledByFramework(JType setterType) {
+ // Some types are handled by the framework at the moment. See #8449
+ // This method should be removed at some point.
+ if (setterType.isArray() != null) {
+ return true;
+ }
+ if (setterType.isEnum() != null) {
+ return true;
+ }
+ if (setterType.isPrimitive() != null) {
+ return true;
+ }
+
+ String qualifiedName = setterType.getQualifiedSourceName();
+ for (Class<?> cls : frameworkHandledTypes) {
+ if (qualifiedName.equals(cls.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
index c1f9134e7b..6d4289b173 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java
@@ -22,18 +22,20 @@ import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.terminal.gwt.client.ui.VView;
-import com.vaadin.ui.ClientWidget;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
+import com.vaadin.terminal.gwt.client.ui.root.RootConnector;
+import com.vaadin.terminal.gwt.server.ClientConnector;
/**
* WidgetMapGenerator's are GWT generator to build WidgetMapImpl dynamically
- * based on {@link ClientWidget} annotations available in workspace. By
- * modifying the generator it is possible to do some fine tuning for the
- * generated widgetset (aka client side engine). The components to be included
- * in the client side engine can modified be overriding
- * {@link #getUsedPaintables()}.
+ * based on {@link Connect} annotations available in workspace. By modifying the
+ * generator it is possible to do some fine tuning for the generated widgetset
+ * (aka client side engine). The components to be included in the client side
+ * engine can modified be overriding {@link #getUsedConnectors()}.
* <p>
* The generator also decides how the client side component implementations are
* loaded to the browser. The default generator is
@@ -41,11 +43,11 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* that loads all widget implementation on application initialization. This has
* been the only option until Vaadin 6.4.
* <p>
- * This generator uses the loadStyle hints from the {@link ClientWidget}
- * annotations. Depending on the {@link LoadStyle} used, the widget may be
- * included in the initially loaded JavaScript, loaded when the application has
- * started and there is no communication to server or lazy loaded when the
- * implementation is absolutely needed.
+ * This generator uses the loadStyle hints from the {@link Connect} annotations.
+ * Depending on the {@link LoadStyle} used, the widget may be included in the
+ * initially loaded JavaScript, loaded when the application has started and
+ * there is no communication to server or lazy loaded when the implementation is
+ * absolutely needed.
* <p>
* The GWT module description file of the widgetset (
* <code>...Widgetset.gwt.xml</code>) can be used to define the
@@ -69,6 +71,9 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
*/
public class WidgetMapGenerator extends Generator {
+ private static String componentConnectorClassName = ComponentConnector.class
+ .getName();
+
private String packageName;
private String className;
@@ -123,15 +128,15 @@ public class WidgetMapGenerator extends Generator {
SourceWriter sourceWriter = composer.createSourceWriter(context,
printWriter);
- Collection<Class<? extends Paintable>> paintablesHavingWidgetAnnotation = getUsedPaintables();
+ Collection<Class<? extends ComponentConnector>> connectors = getUsedConnectors(context
+ .getTypeOracle());
- validatePaintables(logger, context, paintablesHavingWidgetAnnotation);
+ validateConnectors(logger, connectors);
+ logConnectors(logger, context, connectors);
// generator constructor source code
- generateImplementationDetector(sourceWriter,
- paintablesHavingWidgetAnnotation);
- generateInstantiatorMethod(sourceWriter,
- paintablesHavingWidgetAnnotation);
+ generateImplementationDetector(sourceWriter, connectors);
+ generateInstantiatorMethod(sourceWriter, connectors);
// close generated class
sourceWriter.outdent();
sourceWriter.println("}");
@@ -143,47 +148,43 @@ public class WidgetMapGenerator extends Generator {
}
- /**
- * Verifies that all client side components are available for client side
- * GWT module.
- *
- * @param logger
- * @param context
- * @param paintablesHavingWidgetAnnotation
- */
- private void validatePaintables(
- TreeLogger logger,
- GeneratorContext context,
- Collection<Class<? extends Paintable>> paintablesHavingWidgetAnnotation) {
- TypeOracle typeOracle = context.getTypeOracle();
-
- for (Iterator<Class<? extends Paintable>> iterator = paintablesHavingWidgetAnnotation
- .iterator(); iterator.hasNext();) {
- Class<? extends Paintable> class1 = iterator.next();
-
- ClientWidget annotation = class1.getAnnotation(ClientWidget.class);
-
- if (typeOracle.findType(annotation.value().getName()) == null) {
- // GWT widget not inherited
- logger.log(Type.WARN, "Widget class "
- + annotation.value().getName()
- + " was not found. The component " + class1.getName()
- + " will not be included in the widgetset.");
- iterator.remove();
+ private void validateConnectors(TreeLogger logger,
+ Collection<Class<? extends ComponentConnector>> connectors) {
+
+ Iterator<Class<? extends ComponentConnector>> iter = connectors
+ .iterator();
+ while (iter.hasNext()) {
+ Class<? extends ComponentConnector> connectorClass = iter.next();
+ Connect annotation = connectorClass.getAnnotation(Connect.class);
+ if (!ClientConnector.class.isAssignableFrom(annotation.value())) {
+ logger.log(
+ Type.WARN,
+ "Connector class "
+ + annotation.value().getName()
+ + " defined in @Connect annotation is not a subclass of "
+ + ClientConnector.class.getName()
+ + ". The component connector "
+ + connectorClass.getName()
+ + " will not be included in the widgetset.");
+ iter.remove();
}
-
}
+
+ }
+
+ private void logConnectors(TreeLogger logger, GeneratorContext context,
+ Collection<Class<? extends ComponentConnector>> connectors) {
logger.log(Type.INFO,
- "Widget set will contain implementations for following components: ");
+ "Widget set will contain implementations for following component connectors: ");
TreeSet<String> classNames = new TreeSet<String>();
HashMap<String, String> loadStyle = new HashMap<String, String>();
- for (Class<? extends Paintable> class1 : paintablesHavingWidgetAnnotation) {
- String className = class1.getCanonicalName();
+ for (Class<? extends ComponentConnector> connectorClass : connectors) {
+ String className = connectorClass.getCanonicalName();
classNames.add(className);
- if (getLoadStyle(class1) == LoadStyle.DEFERRED) {
+ if (getLoadStyle(connectorClass) == LoadStyle.DEFERRED) {
loadStyle.put(className, "DEFERRED");
- } else if (getLoadStyle(class1) == LoadStyle.LAZY) {
+ } else if (getLoadStyle(connectorClass) == LoadStyle.LAZY) {
loadStyle.put(className, "LAZY");
}
@@ -206,57 +207,72 @@ public class WidgetMapGenerator extends Generator {
* @return a collections of Vaadin components that will be added to
* widgetset
*/
- protected Collection<Class<? extends Paintable>> getUsedPaintables() {
- return ClassPathExplorer.getPaintablesHavingWidgetAnnotation();
+ @SuppressWarnings("unchecked")
+ private Collection<Class<? extends ComponentConnector>> getUsedConnectors(
+ TypeOracle typeOracle) {
+ JClassType connectorType = typeOracle.findType(Connector.class
+ .getName());
+ Collection<Class<? extends ComponentConnector>> connectors = new HashSet<Class<? extends ComponentConnector>>();
+ for (JClassType jClassType : connectorType.getSubtypes()) {
+ Connect annotation = jClassType.getAnnotation(Connect.class);
+ if (annotation != null) {
+ try {
+ Class<? extends ComponentConnector> clazz = (Class<? extends ComponentConnector>) Class
+ .forName(jClassType.getQualifiedSourceName());
+ connectors.add(clazz);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return connectors;
}
/**
* Returns true if the widget for given component will be lazy loaded by the
* client. The default implementation reads the information from the
- * {@link ClientWidget} annotation.
+ * {@link Connect} annotation.
* <p>
* The method can be overridden to optimize the widget loading mechanism. If
* the Widgetset is wanted to be optimized for a network with a high latency
* or for a one with a very fast throughput, it may be good to return false
* for every component.
*
- * @param paintableType
+ * @param connector
* @return true iff the widget for given component should be lazy loaded by
* the client side engine
*/
- protected LoadStyle getLoadStyle(Class<? extends Paintable> paintableType) {
- ClientWidget annotation = paintableType
- .getAnnotation(ClientWidget.class);
+ protected LoadStyle getLoadStyle(
+ Class<? extends ComponentConnector> connector) {
+ Connect annotation = connector.getAnnotation(Connect.class);
return annotation.loadStyle();
}
private void generateInstantiatorMethod(
SourceWriter sourceWriter,
- Collection<Class<? extends Paintable>> paintablesHavingWidgetAnnotation) {
+ Collection<Class<? extends ComponentConnector>> connectorsHavingComponentAnnotation) {
Collection<Class<?>> deferredWidgets = new LinkedList<Class<?>>();
// TODO detect if it would be noticably faster to instantiate with a
// lookup with index than with the hashmap
- sourceWriter
- .println("public void ensureInstantiator(Class<? extends Paintable> classType) {");
+ sourceWriter.println("public void ensureInstantiator(Class<? extends "
+ + componentConnectorClassName + "> classType) {");
sourceWriter.println("if(!instmap.containsKey(classType)){");
boolean first = true;
- ArrayList<Class<? extends Paintable>> lazyLoadedWidgets = new ArrayList<Class<? extends Paintable>>();
-
- HashSet<Class<? extends com.vaadin.terminal.gwt.client.Paintable>> widgetsWithInstantiator = new HashSet<Class<? extends com.vaadin.terminal.gwt.client.Paintable>>();
-
- for (Class<? extends Paintable> class1 : paintablesHavingWidgetAnnotation) {
- ClientWidget annotation = class1.getAnnotation(ClientWidget.class);
- Class<? extends com.vaadin.terminal.gwt.client.Paintable> clientClass = annotation
- .value();
- if(widgetsWithInstantiator.contains(clientClass)) {
+ ArrayList<Class<? extends ComponentConnector>> lazyLoadedWidgets = new ArrayList<Class<? extends ComponentConnector>>();
+
+ HashSet<Class<? extends com.vaadin.terminal.gwt.client.ComponentConnector>> connectorsWithInstantiator = new HashSet<Class<? extends com.vaadin.terminal.gwt.client.ComponentConnector>>();
+
+ for (Class<? extends ComponentConnector> class1 : connectorsHavingComponentAnnotation) {
+ Class<? extends ComponentConnector> clientClass = class1;
+ if (connectorsWithInstantiator.contains(clientClass)) {
continue;
}
- if (clientClass == VView.class) {
- // VView's are not instantiated by widgetset
+ if (clientClass == RootConnector.class) {
+ // Roots are not instantiated by widgetset
continue;
}
if (!first) {
@@ -267,8 +283,10 @@ public class WidgetMapGenerator extends Generator {
sourceWriter.print("if( classType == " + clientClass.getName()
+ ".class) {");
- String instantiator = "new WidgetInstantiator() {\n public Paintable get() {\n return GWT.create("
- + clientClass.getName() + ".class );\n}\n}\n";
+ String instantiator = "new WidgetInstantiator() {\n public "
+ + componentConnectorClassName
+ + " get() {\n return GWT.create(" + clientClass.getName()
+ + ".class );\n}\n}\n";
LoadStyle loadStyle = getLoadStyle(class1);
@@ -295,15 +313,16 @@ public class WidgetMapGenerator extends Generator {
sourceWriter.print(");");
}
sourceWriter.print("}");
- widgetsWithInstantiator.add(clientClass);
+ connectorsWithInstantiator.add(clientClass);
}
sourceWriter.println("}");
sourceWriter.println("}");
- sourceWriter
- .println("public Class<? extends Paintable>[] getDeferredLoadedWidgets() {");
+ sourceWriter.println("public Class<? extends "
+ + componentConnectorClassName
+ + ">[] getDeferredLoadedWidgets() {");
sourceWriter.println("return new Class[] {");
first = true;
@@ -312,10 +331,7 @@ public class WidgetMapGenerator extends Generator {
sourceWriter.println(",");
}
first = false;
- ClientWidget annotation = class2.getAnnotation(ClientWidget.class);
- Class<? extends com.vaadin.terminal.gwt.client.Paintable> value = annotation
- .value();
- sourceWriter.print(value.getName() + ".class");
+ sourceWriter.print(class2.getName() + ".class");
}
sourceWriter.println("};");
@@ -328,11 +344,12 @@ public class WidgetMapGenerator extends Generator {
// TODO an index of last ensured widget in array
- sourceWriter
- .println("public Paintable instantiate(Class<? extends Paintable> classType) {");
+ sourceWriter.println("public " + componentConnectorClassName
+ + " instantiate(Class<? extends " + componentConnectorClassName
+ + "> classType) {");
sourceWriter.indent();
- sourceWriter
- .println("Paintable p = super.instantiate(classType); if(p!= null) return p;");
+ sourceWriter.println(componentConnectorClassName
+ + " p = super.instantiate(classType); if(p!= null) return p;");
sourceWriter.println("return instmap.get(classType).get();");
sourceWriter.outdent();
@@ -348,30 +365,36 @@ public class WidgetMapGenerator extends Generator {
*/
private void generateImplementationDetector(
SourceWriter sourceWriter,
- Collection<Class<? extends Paintable>> paintablesHavingWidgetAnnotation) {
+ Collection<Class<? extends ComponentConnector>> paintablesHavingWidgetAnnotation) {
sourceWriter
- .println("public Class<? extends Paintable> "
- + "getImplementationByServerSideClassName(String fullyQualifiedName) {");
+ .println("public Class<? extends "
+ + componentConnectorClassName
+ + "> "
+ + "getConnectorClassForServerSideClassName(String fullyQualifiedName) {");
sourceWriter.indent();
sourceWriter
.println("fullyQualifiedName = fullyQualifiedName.intern();");
- for (Class<? extends Paintable> class1 : paintablesHavingWidgetAnnotation) {
- ClientWidget annotation = class1.getAnnotation(ClientWidget.class);
- Class<? extends com.vaadin.terminal.gwt.client.Paintable> clientClass = annotation
- .value();
+ for (Class<? extends ComponentConnector> connectorClass : paintablesHavingWidgetAnnotation) {
+ Class<? extends ClientConnector> clientConnectorClass = getClientConnectorClass(connectorClass);
sourceWriter.print("if ( fullyQualifiedName == \"");
- sourceWriter.print(class1.getName());
+ sourceWriter.print(clientConnectorClass.getName());
sourceWriter.print("\" ) { ensureInstantiator("
- + clientClass.getName() + ".class); return ");
- sourceWriter.print(clientClass.getName());
+ + connectorClass.getName() + ".class); return ");
+ sourceWriter.print(connectorClass.getName());
sourceWriter.println(".class;}");
sourceWriter.print("else ");
}
- sourceWriter
- .println("return com.vaadin.terminal.gwt.client.ui.VUnknownComponent.class;");
+ sourceWriter.println("return "
+ + UnknownComponentConnector.class.getName() + ".class;");
sourceWriter.outdent();
sourceWriter.println("}");
}
+
+ private static Class<? extends ClientConnector> getClientConnectorClass(
+ Class<? extends ComponentConnector> connectorClass) {
+ Connect annotation = connectorClass.getAnnotation(Connect.class);
+ return (Class<? extends ClientConnector>) annotation.value();
+ }
}
diff --git a/src/com/vaadin/tools/ReflectTools.java b/src/com/vaadin/tools/ReflectTools.java
index 9f0667f90e..ea2afae301 100644
--- a/src/com/vaadin/tools/ReflectTools.java
+++ b/src/com/vaadin/tools/ReflectTools.java
@@ -3,6 +3,9 @@
*/
package com.vaadin.tools;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
@@ -37,4 +40,87 @@ public class ReflectTools {
throw new ExceptionInInitializerError(e);
}
}
+
+ /**
+ * Returns the value of the java field.
+ * <p>
+ * Uses getter if present, otherwise tries to access even private fields
+ * directly.
+ *
+ * @param object
+ * The object containing the field
+ * @param field
+ * The field we want to get the value for
+ * @return The value of the field in the object
+ * @throws InvocationTargetException
+ * If the value could not be retrieved
+ * @throws IllegalAccessException
+ * If the value could not be retrieved
+ * @throws IllegalArgumentException
+ * If the value could not be retrieved
+ */
+ public static Object getJavaFieldValue(Object object,
+ java.lang.reflect.Field field) throws IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException {
+ PropertyDescriptor pd;
+ try {
+ pd = new PropertyDescriptor(field.getName(), object.getClass());
+ Method getter = pd.getReadMethod();
+ if (getter != null) {
+ return getter.invoke(object, (Object[]) null);
+ }
+ } catch (IntrospectionException e1) {
+ // Ignore this and try to get directly using the field
+ }
+
+ // Try to get the value or throw an exception
+ if (!field.isAccessible()) {
+ // Try to gain access even if field is private
+ field.setAccessible(true);
+ }
+ return field.get(object);
+ }
+
+ /**
+ * Sets the value of a java field.
+ * <p>
+ * Uses setter if present, otherwise tries to access even private fields
+ * directly.
+ *
+ * @param object
+ * The object containing the field
+ * @param field
+ * The field we want to set the value for
+ * @param value
+ * The value to set
+ * @throws IllegalAccessException
+ * If the value could not be assigned to the field
+ * @throws IllegalArgumentException
+ * If the value could not be assigned to the field
+ * @throws InvocationTargetException
+ * If the value could not be assigned to the field
+ */
+ public static void setJavaFieldValue(Object object,
+ java.lang.reflect.Field field, Object value)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException {
+ PropertyDescriptor pd;
+ try {
+ pd = new PropertyDescriptor(field.getName(), object.getClass());
+ Method setter = pd.getWriteMethod();
+ if (setter != null) {
+ // Exceptions are thrown forward if this fails
+ setter.invoke(object, value);
+ }
+ } catch (IntrospectionException e1) {
+ // Ignore this and try to set directly using the field
+ }
+
+ // Try to set the value directly to the field or throw an exception
+ if (!field.isAccessible()) {
+ // Try to gain access even if field is private
+ field.setAccessible(true);
+ }
+ field.set(object, value);
+ }
}
diff --git a/src/com/vaadin/ui/AbsoluteLayout.java b/src/com/vaadin/ui/AbsoluteLayout.java
index 7872e05774..9ba005f75a 100644
--- a/src/com/vaadin/ui/AbsoluteLayout.java
+++ b/src/com/vaadin/ui/AbsoluteLayout.java
@@ -4,20 +4,20 @@
package com.vaadin.ui;
import java.io.Serializable;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
+import java.util.LinkedHashMap;
import java.util.Map;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Sizeable;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutServerRpc;
+import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutState;
/**
* AbsoluteLayout is a layout implementation that mimics html absolute
@@ -25,31 +25,39 @@ import com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout;
*
*/
@SuppressWarnings("serial")
-@ClientWidget(VAbsoluteLayout.class)
public class AbsoluteLayout extends AbstractLayout implements
LayoutClickNotifier {
- private static final String CLICK_EVENT = EventId.LAYOUT_CLICK;
-
- // The components in the layout
- private Collection<Component> components = new LinkedHashSet<Component>();
+ private AbsoluteLayoutServerRpc rpc = new AbsoluteLayoutServerRpc() {
+ public void layoutClick(MouseEventDetails mouseDetails,
+ Connector clickedConnector) {
+ fireEvent(LayoutClickEvent.createEvent(AbsoluteLayout.this,
+ mouseDetails, clickedConnector));
+ }
+ };
// Maps each component to a position
- private Map<Component, ComponentPosition> componentToCoordinates = new HashMap<Component, ComponentPosition>();
+ private LinkedHashMap<Component, ComponentPosition> componentToCoordinates = new LinkedHashMap<Component, ComponentPosition>();
/**
* Creates an AbsoluteLayout with full size.
*/
public AbsoluteLayout() {
+ registerRpc(rpc);
setSizeFull();
}
+ @Override
+ public AbsoluteLayoutState getState() {
+ return (AbsoluteLayoutState) super.getState();
+ }
+
/**
* Gets an iterator for going through all components enclosed in the
* absolute layout.
*/
public Iterator<Component> getComponentIterator() {
- return components.iterator();
+ return componentToCoordinates.keySet().iterator();
}
/**
@@ -59,7 +67,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* @return the number of contained components
*/
public int getComponentCount() {
- return components.size();
+ return componentToCoordinates.size();
}
/**
@@ -69,8 +77,7 @@ public class AbsoluteLayout extends AbstractLayout implements
public void replaceComponent(Component oldComponent, Component newComponent) {
ComponentPosition position = getPosition(oldComponent);
removeComponent(oldComponent);
- addComponent(newComponent);
- componentToCoordinates.put(newComponent, position);
+ addComponent(newComponent, position);
}
/*
@@ -82,29 +89,7 @@ public class AbsoluteLayout extends AbstractLayout implements
*/
@Override
public void addComponent(Component c) {
- components.add(c);
- try {
- super.addComponent(c);
- requestRepaint();
- } catch (IllegalArgumentException e) {
- components.remove(c);
- throw e;
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui
- * .Component)
- */
- @Override
- public void removeComponent(Component c) {
- components.remove(c);
- componentToCoordinates.remove(c);
- super.removeComponent(c);
- requestRepaint();
+ addComponent(c, new ComponentPosition());
}
/**
@@ -122,28 +107,90 @@ public class AbsoluteLayout extends AbstractLayout implements
* The css position string
*/
public void addComponent(Component c, String cssPosition) {
+ ComponentPosition position = new ComponentPosition();
+ position.setCSSString(cssPosition);
+ addComponent(c, position);
+ }
+
+ /**
+ * Adds the component using the given position. Ensures the position is only
+ * set if the component is added correctly.
+ *
+ * @param c
+ * The component to add
+ * @param position
+ * The position info for the component. Must not be null.
+ * @throws IllegalArgumentException
+ * If adding the component failed
+ */
+ private void addComponent(Component c, ComponentPosition position)
+ throws IllegalArgumentException {
/*
* Create position instance and add it to componentToCoordinates map. We
* need to do this before we call addComponent so the attachListeners
* can access this position. #6368
*/
- ComponentPosition position = new ComponentPosition();
- position.setCSSString(cssPosition);
- componentToCoordinates.put(c, position);
-
+ internalSetPosition(c, position);
try {
- addComponent(c);
-
+ super.addComponent(c);
} catch (IllegalArgumentException e) {
- // Remove component coordinates if adding fails
- componentToCoordinates.remove(c);
+ internalRemoveComponent(c);
throw e;
}
+ requestRepaint();
+ }
+
+ /**
+ * Removes the component from all internal data structures. Does not
+ * actually remove the component from the layout (this is assumed to have
+ * been done by the caller).
+ *
+ * @param c
+ * The component to remove
+ */
+ private void internalRemoveComponent(Component c) {
+ componentToCoordinates.remove(c);
+ }
+
+ @Override
+ public void updateState() {
+ super.updateState();
+
+ // This could be in internalRemoveComponent and internalSetComponent if
+ // Map<Connector,String> was supported. We cannot get the child
+ // connectorId unless the component is attached to the application so
+ // the String->String map cannot be populated in internal* either.
+ Map<String, String> connectorToPosition = new HashMap<String, String>();
+ for (Component c : this) {
+ connectorToPosition.put(c.getConnectorId(), getPosition(c)
+ .getCSSString());
+ }
+ getState().setConnectorToCssPosition(connectorToPosition);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui
+ * .Component)
+ */
+ @Override
+ public void removeComponent(Component c) {
+ internalRemoveComponent(c);
+ super.removeComponent(c);
+ requestRepaint();
}
/**
* Gets the position of a component in the layout. Returns null if component
* is not attached to the layout.
+ * <p>
+ * Note that you cannot update the position by updating this object. Call
+ * {@link #setPosition(Component, ComponentPosition)} with the updated
+ * {@link ComponentPosition} object.
+ * </p>
*
* @param component
* The component which position is needed
@@ -152,15 +199,36 @@ public class AbsoluteLayout extends AbstractLayout implements
* layout.
*/
public ComponentPosition getPosition(Component component) {
- if (component.getParent() != this) {
- return null;
- } else if (componentToCoordinates.containsKey(component)) {
- return componentToCoordinates.get(component);
- } else {
- ComponentPosition coords = new ComponentPosition();
- componentToCoordinates.put(component, coords);
- return coords;
+ return componentToCoordinates.get(component);
+ }
+
+ /**
+ * Sets the position of a component in the layout.
+ *
+ * @param component
+ * @param position
+ */
+ public void setPosition(Component component, ComponentPosition position) {
+ if (!componentToCoordinates.containsKey(component)) {
+ throw new IllegalArgumentException(
+ "Component must be a child of this layout");
}
+ internalSetPosition(component, position);
+ }
+
+ /**
+ * Updates the position for a component. Caller must ensure component is a
+ * child of this layout.
+ *
+ * @param component
+ * The component. Must be a child for this layout. Not enforced.
+ * @param position
+ * New position. Must not be null.
+ */
+ private void internalSetPosition(Component component,
+ ComponentPosition position) {
+ componentToCoordinates.put(component, position);
+ requestRepaint();
}
/**
@@ -176,10 +244,10 @@ public class AbsoluteLayout extends AbstractLayout implements
private Float bottomValue = null;
private Float leftValue = null;
- private int topUnits;
- private int rightUnits;
- private int bottomUnits;
- private int leftUnits;
+ private Unit topUnits = Unit.PIXELS;
+ private Unit rightUnits = Unit.PIXELS;
+ private Unit bottomUnits = Unit.PIXELS;
+ private Unit leftUnits = Unit.PIXELS;
/**
* Sets the position attributes using CSS syntax. Attributes not
@@ -193,7 +261,7 @@ public class AbsoluteLayout extends AbstractLayout implements
*/
public void setCSSString(String css) {
topValue = rightValue = bottomValue = leftValue = null;
- topUnits = rightUnits = bottomUnits = leftUnits = 0;
+ topUnits = rightUnits = bottomUnits = leftUnits = Unit.PIXELS;
zIndex = -1;
if (css == null) {
return;
@@ -215,24 +283,25 @@ public class AbsoluteLayout extends AbstractLayout implements
} else {
value = "";
}
- String unit = value.replaceAll("[0-9\\.\\-]+", "");
- if (!unit.equals("")) {
- value = value.substring(0, value.indexOf(unit)).trim();
+ String symbol = value.replaceAll("[0-9\\.\\-]+", "");
+ if (!symbol.equals("")) {
+ value = value.substring(0, value.indexOf(symbol))
+ .trim();
}
float v = Float.parseFloat(value);
- int unitInt = parseCssUnit(unit);
+ Unit unit = Unit.getUnitFromSymbol(symbol);
if (key.equals("top")) {
topValue = v;
- topUnits = unitInt;
+ topUnits = unit;
} else if (key.equals("right")) {
rightValue = v;
- rightUnits = unitInt;
+ rightUnits = unit;
} else if (key.equals("bottom")) {
bottomValue = v;
- bottomUnits = unitInt;
+ bottomUnits = unit;
} else if (key.equals("left")) {
leftValue = v;
- leftUnits = unitInt;
+ leftUnits = unit;
}
}
}
@@ -240,23 +309,6 @@ public class AbsoluteLayout extends AbstractLayout implements
}
/**
- * Parses a string and checks if a unit is found. If a unit is not found
- * from the string the unit pixels is used.
- *
- * @param string
- * The string to parse the unit from
- * @return The found unit
- */
- private int parseCssUnit(String string) {
- for (int i = 0; i < UNIT_SYMBOLS.length; i++) {
- if (UNIT_SYMBOLS[i].equals(string)) {
- return i;
- }
- }
- return 0; // defaults to px (eg. top:0;)
- }
-
- /**
* Converts the internal values into a valid CSS string.
*
* @return A valid CSS string
@@ -264,16 +316,16 @@ public class AbsoluteLayout extends AbstractLayout implements
public String getCSSString() {
String s = "";
if (topValue != null) {
- s += "top:" + topValue + UNIT_SYMBOLS[topUnits] + ";";
+ s += "top:" + topValue + topUnits.getSymbol() + ";";
}
if (rightValue != null) {
- s += "right:" + rightValue + UNIT_SYMBOLS[rightUnits] + ";";
+ s += "right:" + rightValue + rightUnits.getSymbol() + ";";
}
if (bottomValue != null) {
- s += "bottom:" + bottomValue + UNIT_SYMBOLS[bottomUnits] + ";";
+ s += "bottom:" + bottomValue + bottomUnits.getSymbol() + ";";
}
if (leftValue != null) {
- s += "left:" + leftValue + UNIT_SYMBOLS[leftUnits] + ";";
+ s += "left:" + leftValue + leftUnits.getSymbol() + ";";
}
if (zIndex >= 0) {
s += "z-index:" + zIndex + ";";
@@ -291,7 +343,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* The unit of the 'top' attribute. See UNIT_SYMBOLS for a
* description of the available units.
*/
- public void setTop(Float topValue, int topUnits) {
+ public void setTop(Float topValue, Unit topUnits) {
this.topValue = topValue;
this.topUnits = topUnits;
requestRepaint();
@@ -307,7 +359,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* The unit of the 'right' attribute. See UNIT_SYMBOLS for a
* description of the available units.
*/
- public void setRight(Float rightValue, int rightUnits) {
+ public void setRight(Float rightValue, Unit rightUnits) {
this.rightValue = rightValue;
this.rightUnits = rightUnits;
requestRepaint();
@@ -323,7 +375,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* The unit of the 'bottom' attribute. See UNIT_SYMBOLS for a
* description of the available units.
*/
- public void setBottom(Float bottomValue, int bottomUnits) {
+ public void setBottom(Float bottomValue, Unit bottomUnits) {
this.bottomValue = bottomValue;
this.bottomUnits = bottomUnits;
requestRepaint();
@@ -339,7 +391,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* The unit of the 'left' attribute. See UNIT_SYMBOLS for a
* description of the available units.
*/
- public void setLeft(Float leftValue, int leftUnits) {
+ public void setLeft(Float leftValue, Unit leftUnits) {
this.leftValue = leftValue;
this.leftUnits = leftUnits;
requestRepaint();
@@ -456,7 +508,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public int getTopUnits() {
+ public Unit getTopUnits() {
return topUnits;
}
@@ -467,7 +519,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public void setTopUnits(int topUnits) {
+ public void setTopUnits(Unit topUnits) {
this.topUnits = topUnits;
requestRepaint();
}
@@ -478,7 +530,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public int getRightUnits() {
+ public Unit getRightUnits() {
return rightUnits;
}
@@ -489,7 +541,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public void setRightUnits(int rightUnits) {
+ public void setRightUnits(Unit rightUnits) {
this.rightUnits = rightUnits;
requestRepaint();
}
@@ -500,7 +552,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public int getBottomUnits() {
+ public Unit getBottomUnits() {
return bottomUnits;
}
@@ -511,7 +563,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public void setBottomUnits(int bottomUnits) {
+ public void setBottomUnits(Unit bottomUnits) {
this.bottomUnits = bottomUnits;
requestRepaint();
}
@@ -522,7 +574,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public int getLeftUnits() {
+ public Unit getLeftUnits() {
return leftUnits;
}
@@ -533,7 +585,7 @@ public class AbsoluteLayout extends AbstractLayout implements
* See {@link Sizeable} UNIT_SYMBOLS for a description of the
* available units.
*/
- public void setLeftUnits(int leftUnits) {
+ public void setLeftUnits(Unit leftUnits) {
this.leftUnits = leftUnits;
requestRepaint();
}
@@ -559,31 +611,15 @@ public class AbsoluteLayout extends AbstractLayout implements
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.ui.AbstractLayout#paintContent(com.vaadin.terminal.PaintTarget
- * )
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
- for (Component component : components) {
- target.startTag("cc");
- target.addAttribute("css", getPosition(component).getCSSString());
- component.paint(target);
- target.endTag("cc");
- }
- }
-
public void addListener(LayoutClickListener listener) {
- addListener(CLICK_EVENT, LayoutClickEvent.class, listener,
+ addListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener,
LayoutClickListener.clickMethod);
}
public void removeListener(LayoutClickListener listener) {
- removeListener(CLICK_EVENT, LayoutClickEvent.class, listener);
+ removeListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener);
}
}
diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java
index 0ca9cc6bd4..79a07ae00e 100644
--- a/src/com/vaadin/ui/AbstractComponent.java
+++ b/src/com/vaadin/ui/AbstractComponent.java
@@ -5,28 +5,41 @@
package com.vaadin.ui;
import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.vaadin.Application;
+import com.vaadin.event.ActionManager;
import com.vaadin.event.EventRouter;
import com.vaadin.event.MethodEventSource;
+import com.vaadin.event.ShortcutListener;
import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Terminal;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
+import com.vaadin.terminal.gwt.server.ResourceReference;
+import com.vaadin.terminal.gwt.server.RpcManager;
+import com.vaadin.terminal.gwt.server.RpcTarget;
+import com.vaadin.terminal.gwt.server.ServerRpcManager;
import com.vaadin.tools.ReflectTools;
/**
@@ -46,50 +59,15 @@ public abstract class AbstractComponent implements Component, MethodEventSource
/* Private members */
/**
- * Style names.
- */
- private ArrayList<String> styles;
-
- /**
- * Caption text.
- */
- private String caption;
-
- /**
* Application specific data object. The component does not use or modify
* this.
*/
private Object applicationData;
/**
- * Icon to be shown together with caption.
- */
- private Resource icon;
-
- /**
- * Is the component enabled (its normal usage is allowed).
- */
- private boolean enabled = true;
-
- /**
- * Is the component visible (it is rendered).
- */
- private boolean visible = true;
-
- /**
- * Is the component read-only ?
- */
- private boolean readOnly = false;
-
- /**
- * Description of the usage (XML).
- */
- private String description = null;
-
- /**
* The container this component resides in.
*/
- private Component parent = null;
+ private HasComponents parent = null;
/**
* The EventRouter used for the event model.
@@ -97,22 +75,11 @@ public abstract class AbstractComponent implements Component, MethodEventSource
private EventRouter eventRouter = null;
/**
- * A set of event identifiers with registered listeners.
- */
- private Set<String> eventIdentifiers = null;
-
- /**
* The internal error message of the component.
*/
private ErrorMessage componentError = null;
/**
- * Immediate mode: if true, all variable changes are required to be sent
- * from the terminal immediately.
- */
- private boolean immediate = false;
-
- /**
* Locale of this component.
*/
private Locale locale;
@@ -127,24 +94,48 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
private LinkedList<RepaintRequestListener> repaintRequestListeners = null;
- /**
- * Are all the repaint listeners notified about recent changes ?
- */
- private boolean repaintRequestListenersNotified = false;
-
- private String testingId;
-
/* Sizeable fields */
private float width = SIZE_UNDEFINED;
private float height = SIZE_UNDEFINED;
- private int widthUnit = UNITS_PIXELS;
- private int heightUnit = UNITS_PIXELS;
+ private Unit widthUnit = Unit.PIXELS;
+ private Unit heightUnit = Unit.PIXELS;
private static final Pattern sizePattern = Pattern
.compile("^(-?\\d+(\\.\\d+)?)(%|px|em|ex|in|cm|mm|pt|pc)?$");
private ComponentErrorHandler errorHandler = null;
+ /**
+ * Keeps track of the Actions added to this component; the actual
+ * handling/notifying is delegated, usually to the containing window.
+ */
+ private ActionManager actionManager;
+
+ /**
+ * A map from client to server RPC interface class to the RPC call manager
+ * that handles incoming RPC calls for that interface.
+ */
+ private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>();
+
+ /**
+ * A map from server to client RPC interface class to the RPC proxy that
+ * sends ourgoing RPC calls for that interface.
+ */
+ private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>();
+
+ /**
+ * Shared state object to be communicated from the server to the client when
+ * modified.
+ */
+ private ComponentState sharedState;
+
+ /**
+ * Pending RPC method invocations to be sent.
+ */
+ private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+
+ private String connectorId;
+
/* Constructor */
/**
@@ -157,11 +148,11 @@ public abstract class AbstractComponent implements Component, MethodEventSource
/* Get/Set component properties */
public void setDebugId(String id) {
- testingId = id;
+ getState().setDebugId(id);
}
public String getDebugId() {
- return testingId;
+ return getState().getDebugId();
}
/**
@@ -179,8 +170,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
/**
* Sets and replaces all previous style names of the component. This method
- * will trigger a {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * will trigger a {@link RepaintRequestEvent}.
*
* @param style
* the new style of the component.
@@ -199,8 +189,9 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
public String getStyleName() {
String s = "";
- if (styles != null) {
- for (final Iterator<String> it = styles.iterator(); it.hasNext();) {
+ if (getState().getStyles() != null) {
+ for (final Iterator<String> it = getState().getStyles().iterator(); it
+ .hasNext();) {
s += it.next();
if (it.hasNext()) {
s += " ";
@@ -216,13 +207,14 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
public void setStyleName(String style) {
if (style == null || "".equals(style)) {
- styles = null;
+ getState().setStyles(null);
requestRepaint();
return;
}
- if (styles == null) {
- styles = new ArrayList<String>();
+ if (getState().getStyles() == null) {
+ getState().setStyles(new ArrayList<String>());
}
+ List<String> styles = getState().getStyles();
styles.clear();
String[] styleParts = style.split(" +");
for (String part : styleParts) {
@@ -237,9 +229,18 @@ public abstract class AbstractComponent implements Component, MethodEventSource
if (style == null || "".equals(style)) {
return;
}
- if (styles == null) {
- styles = new ArrayList<String>();
+ if (style.contains(" ")) {
+ // Split space separated style names and add them one by one.
+ for (String realStyle : style.split(" ")) {
+ addStyleName(realStyle);
+ }
+ return;
}
+
+ if (getState().getStyles() == null) {
+ getState().setStyles(new ArrayList<String>());
+ }
+ List<String> styles = getState().getStyles();
if (!styles.contains(style)) {
styles.add(style);
requestRepaint();
@@ -247,11 +248,11 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
public void removeStyleName(String style) {
- if (styles != null) {
+ if (getState().getStyles() != null) {
String[] styleParts = style.split(" +");
for (String part : styleParts) {
if (part.length() > 0) {
- styles.remove(part);
+ getState().getStyles().remove(part);
}
}
requestRepaint();
@@ -263,20 +264,19 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* the default documentation from implemented interface.
*/
public String getCaption() {
- return caption;
+ return getState().getCaption();
}
/**
* Sets the component's caption <code>String</code>. Caption is the visible
* name of the component. This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * {@link RepaintRequestEvent}.
*
* @param caption
* the new caption <code>String</code> for the component.
*/
public void setCaption(String caption) {
- this.caption = caption;
+ getState().setCaption(caption);
requestRepaint();
}
@@ -319,6 +319,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*/
public void setLocale(Locale locale) {
this.locale = locale;
+
+ // FIXME: Reload value if there is a converter
requestRepaint();
}
@@ -327,58 +329,71 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* use the default documentation from implemented interface.
*/
public Resource getIcon() {
- return icon;
+ ResourceReference ref = ((ResourceReference) getState().getIcon());
+ if (ref == null) {
+ return null;
+ } else {
+ return ref.getResource();
+ }
}
/**
* Sets the component's icon. This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * {@link RepaintRequestEvent}.
*
* @param icon
* the icon to be shown with the component's caption.
*/
public void setIcon(Resource icon) {
- this.icon = icon;
+ if (icon == null) {
+ getState().setIcon(null);
+ } else {
+ getState().setIcon(new ResourceReference(icon));
+ }
requestRepaint();
}
/*
- * Tests if the component is enabled or not. Don't add a JavaDoc comment
- * here, we use the default documentation from implemented interface.
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#isEnabled()
*/
public boolean isEnabled() {
- return enabled && (parent == null || parent.isEnabled()) && isVisible();
+ return getState().isEnabled();
}
/*
- * Enables or disables the component. Don't add a JavaDoc comment here, we
- * use the default documentation from implemented interface.
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#setEnabled(boolean)
*/
public void setEnabled(boolean enabled) {
- if (this.enabled != enabled) {
- boolean wasEnabled = this.enabled;
- boolean wasEnabledInContext = isEnabled();
-
- this.enabled = enabled;
-
- boolean isEnabled = enabled;
- boolean isEnabledInContext = isEnabled();
-
- // If the actual enabled state (as rendered, in context) has not
- // changed we do not need to repaint except if the parent is
- // invisible.
- // If the parent is invisible we must request a repaint so the
- // component is repainted with the new enabled state when the parent
- // is set visible again. This workaround is needed as isEnabled
- // checks isVisible.
- boolean needRepaint = (wasEnabledInContext != isEnabledInContext)
- || (wasEnabled != isEnabled && (getParent() == null || !getParent()
- .isVisible()));
-
- if (needRepaint) {
- requestRepaint();
+ if (getState().isEnabled() != enabled) {
+ getState().setEnabled(enabled);
+ requestRepaint();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
+ */
+ public boolean isConnectorEnabled() {
+ if (getParent() == null) {
+ // No parent -> the component cannot receive updates from the client
+ return false;
+ } else {
+ boolean thisEnabledAndVisible = isEnabled() && isVisible();
+ if (!thisEnabledAndVisible) {
+ return false;
+ }
+
+ if (!getParent().isConnectorEnabled()) {
+ return false;
}
+
+ return getParent().isComponentVisible(this);
}
}
@@ -388,13 +403,12 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* interface.
*/
public boolean isImmediate() {
- return immediate;
+ return getState().isImmediate();
}
/**
* Sets the component's immediate mode to the specified status. This method
- * will trigger a {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * will trigger a {@link RepaintRequestEvent}.
*
* @param immediate
* the boolean value specifying if the component should be in the
@@ -402,7 +416,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see Component#isImmediate()
*/
public void setImmediate(boolean immediate) {
- this.immediate = immediate;
+ getState().setImmediate(immediate);
requestRepaint();
}
@@ -412,7 +426,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.ui.Component#isVisible()
*/
public boolean isVisible() {
- return visible && (getParent() == null || getParent().isVisible());
+ return getState().isVisible();
}
/*
@@ -421,14 +435,16 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.ui.Component#setVisible(boolean)
*/
public void setVisible(boolean visible) {
+ if (getState().isVisible() == visible) {
+ return;
+ }
- if (this.visible != visible) {
- this.visible = visible;
- // Instead of requesting repaint normally we
- // fire the event directly to assure that the
- // event goes through event in the component might
- // now be invisible
- fireRequestRepaintEvent(null);
+ getState().setVisible(visible);
+ requestRepaint();
+ if (getParent() != null) {
+ // Must always repaint the parent (at least the hierarchy) when
+ // visibility of a child component changes.
+ getParent().requestRepaint();
}
}
@@ -490,14 +506,13 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @return component's description <code>String</code>
*/
public String getDescription() {
- return description;
+ return getState().getDescription();
}
/**
* Sets the component's description. See {@link #getDescription()} for more
* information on what the description is. This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * {@link RepaintRequestEvent}.
*
* The description is displayed as HTML/XHTML in tooltips or directly in
* certain components so care should be taken to avoid creating the
@@ -507,7 +522,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* the new description string for the component.
*/
public void setDescription(String description) {
- this.description = description;
+ getState().setDescription(description);
requestRepaint();
}
@@ -515,15 +530,40 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* Gets the component's parent component. Don't add a JavaDoc comment here,
* we use the default documentation from implemented interface.
*/
- public Component getParent() {
+ public HasComponents getParent() {
return parent;
}
+ /**
+ * Returns the closest ancestor with the given type.
+ * <p>
+ * To find the Window that contains the component, use {@code Window w =
+ * getParent(Window.class);}
+ * </p>
+ *
+ * @param <T>
+ * The type of the ancestor
+ * @param parentType
+ * The ancestor class we are looking for
+ * @return The first ancestor that can be assigned to the given class. Null
+ * if no ancestor with the correct type could be found.
+ */
+ public <T extends HasComponents> T findAncestor(Class<T> parentType) {
+ HasComponents p = getParent();
+ while (p != null) {
+ if (parentType.isAssignableFrom(p.getClass())) {
+ return parentType.cast(p);
+ }
+ p = p.getParent();
+ }
+ return null;
+ }
+
/*
* Sets the parent component. Don't add a JavaDoc comment here, we use the
* default documentation from implemented interface.
*/
- public void setParent(Component parent) {
+ public void setParent(HasComponents parent) {
// If the parent is not changed, don't do anything
if (parent == this.parent) {
@@ -593,7 +633,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* here, we use the default documentation from implemented interface.
*/
public boolean isReadOnly() {
- return readOnly;
+ return getState().isReadOnly();
}
/*
@@ -601,7 +641,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* use the default documentation from implemented interface.
*/
public void setReadOnly(boolean readOnly) {
- this.readOnly = readOnly;
+ getState().setReadOnly(readOnly);
requestRepaint();
}
@@ -609,11 +649,11 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* Gets the parent window of the component. Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
*/
- public Window getWindow() {
+ public Root getRoot() {
if (parent == null) {
return null;
} else {
- return parent.getWindow();
+ return parent.getRoot();
}
}
@@ -623,18 +663,12 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* interface.
*/
public void attach() {
+ getRoot().componentAttached(this);
requestRepaint();
- if (!visible) {
- /*
- * Bypass the repaint optimization in childRequestedRepaint method
- * when attaching. When reattaching (possibly moving) -> must
- * repaint
- */
- fireRequestRepaintEvent(null);
- }
if (delayedFocus) {
focus();
}
+ setActionManagerViewer();
}
/*
@@ -642,6 +676,12 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* we use the default documentation from implemented interface.
*/
public void detach() {
+ if (actionManager != null) {
+ // Remove any existing viewer. Root cast is just to make the
+ // compiler happy
+ actionManager.setViewer((Root) null);
+ }
+ getRoot().componentDetached(this);
}
/**
@@ -651,7 +691,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
if (this instanceof Focusable) {
final Application app = getApplication();
if (app != null) {
- getWindow().setFocusedComponent((Focusable) this);
+ getRoot().setFocusedComponent((Focusable) this);
delayedFocus = false;
} else {
delayedFocus = true;
@@ -686,115 +726,16 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
}
- /* Component painting */
-
- /* Documented in super interface */
- public void requestRepaintRequests() {
- repaintRequestListenersNotified = false;
- }
-
- /**
- *
- * <p>
- * Paints the Paintable into a UIDL stream. This method creates the UIDL
- * sequence describing it and outputs it to the given UIDL stream.
- * </p>
- *
- * <p>
- * It is called when the contents of the component should be painted in
- * response to the component first being shown or having been altered so
- * that its visual representation is changed.
- * </p>
- *
- * <p>
- * <b>Do not override this to paint your component.</b> Override
- * {@link #paintContent(PaintTarget)} instead.
- * </p>
- *
- *
- * @param target
- * the target UIDL stream where the component should paint itself
- * to.
- * @throws PaintException
- * if the paint operation failed.
- */
- public void paint(PaintTarget target) throws PaintException {
- final String tag = target.getTag(this);
- if (!target.startTag(this, tag) || repaintRequestListenersNotified) {
-
- // Paint the contents of the component
-
- // Only paint content of visible components.
- if (isVisible()) {
- if (getHeight() >= 0
- && (getHeightUnits() != UNITS_PERCENTAGE || ComponentSizeValidator
- .parentCanDefineHeight(this))) {
- target.addAttribute("height", "" + getCSSHeight());
- }
-
- if (getWidth() >= 0
- && (getWidthUnits() != UNITS_PERCENTAGE || ComponentSizeValidator
- .parentCanDefineWidth(this))) {
- target.addAttribute("width", "" + getCSSWidth());
- }
- if (styles != null && styles.size() > 0) {
- target.addAttribute("style", getStyle());
- }
- if (isReadOnly()) {
- target.addAttribute("readonly", true);
- }
-
- if (isImmediate()) {
- target.addAttribute("immediate", true);
- }
- if (!isEnabled()) {
- target.addAttribute("disabled", true);
- }
- if (getCaption() != null) {
- target.addAttribute("caption", getCaption());
- }
- if (getIcon() != null) {
- target.addAttribute("icon", getIcon());
- }
-
- if (getDescription() != null && getDescription().length() > 0) {
- target.addAttribute("description", getDescription());
- }
-
- if (eventIdentifiers != null) {
- target.addAttribute("eventListeners",
- eventIdentifiers.toArray());
- }
-
- paintContent(target);
-
- final ErrorMessage error = getErrorMessage();
- if (error != null) {
- error.paint(target);
- }
- } else {
- target.addAttribute("invisible", true);
- }
- } else {
-
- // Contents have not changed, only cached presentation can be used
- target.addAttribute("cached", true);
- }
- target.endTag(tag);
-
- repaintRequestListenersNotified = false;
- }
-
/**
* Build CSS compatible string representation of height.
*
* @return CSS height
*/
private String getCSSHeight() {
- if (getHeightUnits() == UNITS_PIXELS) {
- return ((int) getHeight()) + UNIT_SYMBOLS[getHeightUnits()];
+ if (getHeightUnits() == Unit.PIXELS) {
+ return ((int) getHeight()) + getHeightUnits().getSymbol();
} else {
- return getHeight() + UNIT_SYMBOLS[getHeightUnits()];
+ return getHeight() + getHeightUnits().getSymbol();
}
}
@@ -804,47 +745,106 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @return CSS width
*/
private String getCSSWidth() {
- if (getWidthUnits() == UNITS_PIXELS) {
- return ((int) getWidth()) + UNIT_SYMBOLS[getWidthUnits()];
+ if (getWidthUnits() == Unit.PIXELS) {
+ return ((int) getWidth()) + getWidthUnits().getSymbol();
} else {
- return getWidth() + UNIT_SYMBOLS[getWidthUnits()];
+ return getWidth() + getWidthUnits().getSymbol();
}
}
/**
- * Paints any needed component-specific things to the given UIDL stream. The
- * more general {@link #paint(PaintTarget)} method handles all general
- * attributes common to all components, and it calls this method to paint
- * any component-specific attributes to the UIDL stream.
+ * Returns the shared state bean with information to be sent from the server
+ * to the client.
*
- * @param target
- * the target UIDL stream where the component should paint itself
- * to
- * @throws PaintException
- * if the paint operation failed.
+ * Subclasses should override this method and set any relevant fields of the
+ * state returned by super.getState().
+ *
+ * @since 7.0
+ *
+ * @return updated component shared state
+ */
+ public ComponentState getState() {
+ if (null == sharedState) {
+ sharedState = createState();
+ }
+ return sharedState;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#updateState()
*/
- public void paintContent(PaintTarget target) throws PaintException {
+ public void updateState() {
+ // TODO This logic should be on the client side and the state should
+ // simply be a data object with "width" and "height".
+ if (getHeight() >= 0
+ && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
+ .parentCanDefineHeight(this))) {
+ getState().setHeight("" + getCSSHeight());
+ } else {
+ getState().setHeight("");
+ }
+
+ if (getWidth() >= 0
+ && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
+ .parentCanDefineWidth(this))) {
+ getState().setWidth("" + getCSSWidth());
+ } else {
+ getState().setWidth("");
+ }
+ ErrorMessage error = getErrorMessage();
+ if (null != error) {
+ getState().setErrorMessage(error.getFormattedHtmlMessage());
+ } else {
+ getState().setErrorMessage(null);
+ }
}
- /* Documentation copied from interface */
- public void requestRepaint() {
+ /**
+ * Creates the shared state bean to be used in server to client
+ * communication.
+ * <p>
+ * By default a state object of the defined return type of
+ * {@link #getState()} is created. Subclasses can override this method and
+ * return a new instance of the correct state class but this should rarely
+ * be necessary.
+ * </p>
+ * <p>
+ * No configuration of the values of the state should be performed in
+ * {@link #createState()}.
+ *
+ * @since 7.0
+ *
+ * @return new shared state object
+ */
+ protected ComponentState createState() {
+ try {
+ Method m = getClass().getMethod("getState", (Class[]) null);
+ Class<? extends ComponentState> type = (Class<? extends ComponentState>) m
+ .getReturnType();
+ return type.newInstance();
+ } catch (Exception e) {
+ getLogger().log(
+ Level.INFO,
+ "Error determining state object class for "
+ + getClass().getName());
+ }
- // The effect of the repaint request is identical to case where a
- // child requests repaint
- childRequestedRepaint(null);
+ // Fall back to ComponentState if detection fails for some reason.
+ return new ComponentState();
}
/* Documentation copied from interface */
- public void childRequestedRepaint(
- Collection<RepaintRequestListener> alreadyNotified) {
+ public void requestRepaint() {
// Invisible components (by flag in this particular component) do not
// need repaints
- if (!visible) {
+ if (!getState().isVisible()) {
return;
}
- fireRequestRepaintEvent(alreadyNotified);
+ fireRequestRepaintEvent();
}
/**
@@ -852,33 +852,15 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*
* @param alreadyNotified
*/
- private void fireRequestRepaintEvent(
- Collection<RepaintRequestListener> alreadyNotified) {
- // Notify listeners only once
- if (!repaintRequestListenersNotified) {
- // Notify the listeners
- if (repaintRequestListeners != null
- && !repaintRequestListeners.isEmpty()) {
- final Object[] listeners = repaintRequestListeners.toArray();
- final RepaintRequestEvent event = new RepaintRequestEvent(this);
- for (int i = 0; i < listeners.length; i++) {
- if (alreadyNotified == null) {
- alreadyNotified = new LinkedList<RepaintRequestListener>();
- }
- if (!alreadyNotified.contains(listeners[i])) {
- ((RepaintRequestListener) listeners[i])
- .repaintRequested(event);
- alreadyNotified
- .add((RepaintRequestListener) listeners[i]);
- repaintRequestListenersNotified = true;
- }
- }
- }
-
- // Notify the parent
- final Component parent = getParent();
- if (parent != null) {
- parent.childRequestedRepaint(alreadyNotified);
+ // Notify listeners only once
+ private void fireRequestRepaintEvent() {
+ // Notify the listeners
+ if (repaintRequestListeners != null
+ && !repaintRequestListeners.isEmpty()) {
+ final Object[] listeners = repaintRequestListeners.toArray();
+ final RepaintRequestEvent event = new RepaintRequestEvent(this);
+ for (int i = 0; i < listeners.length; i++) {
+ ((RepaintRequestListener) listeners[i]).repaintRequested(event);
}
}
}
@@ -903,17 +885,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
}
- /* Component variable changes */
-
- /*
- * Invoked when the value of a variable has changed. Don't add a JavaDoc
- * comment here, we use the default documentation from implemented
- * interface.
- */
- public void changeVariables(Object source, Map<String, Object> variables) {
-
- }
-
/* General event framework */
private static final Method COMPONENT_EVENT_METHOD = ReflectTools
@@ -955,14 +926,11 @@ public abstract class AbstractComponent implements Component, MethodEventSource
if (eventRouter == null) {
eventRouter = new EventRouter();
}
- if (eventIdentifiers == null) {
- eventIdentifiers = new HashSet<String>();
- }
boolean needRepaint = !eventRouter.hasListeners(eventType);
eventRouter.addListener(eventType, target, method);
if (needRepaint) {
- eventIdentifiers.add(eventIdentifier);
+ getState().addRegisteredEventListener(eventIdentifier);
requestRepaint();
}
}
@@ -1011,7 +979,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
if (eventRouter != null) {
eventRouter.removeListener(eventType, target);
if (!eventRouter.hasListeners(eventType)) {
- eventIdentifiers.remove(eventIdentifier);
+ getState().removeRegisteredEventListener(eventIdentifier);
requestRepaint();
}
}
@@ -1283,7 +1251,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*
* @see com.vaadin.terminal.Sizeable#getHeightUnits()
*/
- public int getHeightUnits() {
+ public Unit getHeightUnits() {
return heightUnit;
}
@@ -1301,36 +1269,19 @@ public abstract class AbstractComponent implements Component, MethodEventSource
*
* @see com.vaadin.terminal.Sizeable#getWidthUnits()
*/
- public int getWidthUnits() {
+ public Unit getWidthUnits() {
return widthUnit;
}
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.Sizeable#setHeight(float)
- */
- @Deprecated
- public void setHeight(float height) {
- setHeight(height, getHeightUnits());
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Sizeable#setHeightUnits(int)
- */
- @Deprecated
- public void setHeightUnits(int unit) {
- setHeight(getHeight(), unit);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Sizeable#setHeight(float, int)
+ * @see com.vaadin.terminal.Sizeable#setHeight(float, Unit)
*/
- public void setHeight(float height, int unit) {
+ public void setHeight(float height, Unit unit) {
+ if (unit == null) {
+ throw new IllegalArgumentException("Unit can not be null");
+ }
this.height = height;
heightUnit = unit;
requestRepaint();
@@ -1343,8 +1294,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.terminal.Sizeable#setSizeFull()
*/
public void setSizeFull() {
- setWidth(100, UNITS_PERCENTAGE);
- setHeight(100, UNITS_PERCENTAGE);
+ setWidth(100, Unit.PERCENTAGE);
+ setHeight(100, Unit.PERCENTAGE);
}
/*
@@ -1353,36 +1304,19 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.terminal.Sizeable#setSizeUndefined()
*/
public void setSizeUndefined() {
- setWidth(-1, UNITS_PIXELS);
- setHeight(-1, UNITS_PIXELS);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Sizeable#setWidth(float)
- */
- @Deprecated
- public void setWidth(float width) {
- setWidth(width, getWidthUnits());
+ setWidth(-1, Unit.PIXELS);
+ setHeight(-1, Unit.PIXELS);
}
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.Sizeable#setWidthUnits(int)
+ * @see com.vaadin.terminal.Sizeable#setWidth(float, Unit)
*/
- @Deprecated
- public void setWidthUnits(int unit) {
- setWidth(getWidth(), unit);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.terminal.Sizeable#setWidth(float, int)
- */
- public void setWidth(float width, int unit) {
+ public void setWidth(float width, Unit unit) {
+ if (unit == null) {
+ throw new IllegalArgumentException("Unit can not be null");
+ }
this.width = width;
widthUnit = unit;
requestRepaint();
@@ -1395,8 +1329,12 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.terminal.Sizeable#setWidth(java.lang.String)
*/
public void setWidth(String width) {
- float[] p = parseStringSize(width);
- setWidth(p[0], (int) p[1]);
+ Size size = parseStringSize(width);
+ if (size != null) {
+ setWidth(size.getSize(), size.getUnit());
+ } else {
+ setWidth(-1, Unit.PIXELS);
+ }
}
/*
@@ -1405,58 +1343,61 @@ public abstract class AbstractComponent implements Component, MethodEventSource
* @see com.vaadin.terminal.Sizeable#setHeight(java.lang.String)
*/
public void setHeight(String height) {
- float[] p = parseStringSize(height);
- setHeight(p[0], (int) p[1]);
+ Size size = parseStringSize(height);
+ if (size != null) {
+ setHeight(size.getSize(), size.getUnit());
+ } else {
+ setHeight(-1, Unit.PIXELS);
+ }
}
/*
* Returns array with size in index 0 unit in index 1. Null or empty string
- * will produce {-1,UNITS_PIXELS}
+ * will produce {-1,Unit#PIXELS}
*/
- private static float[] parseStringSize(String s) {
- float[] values = { -1, UNITS_PIXELS };
+ private static Size parseStringSize(String s) {
if (s == null) {
- return values;
+ return null;
}
s = s.trim();
if ("".equals(s)) {
- return values;
+ return null;
}
-
+ float size = 0;
+ Unit unit = null;
Matcher matcher = sizePattern.matcher(s);
if (matcher.find()) {
- values[0] = Float.parseFloat(matcher.group(1));
- if (values[0] < 0) {
- values[0] = -1;
+ size = Float.parseFloat(matcher.group(1));
+ if (size < 0) {
+ size = -1;
+ unit = Unit.PIXELS;
} else {
- String unit = matcher.group(3);
- if (unit == null) {
- values[1] = UNITS_PIXELS;
- } else if (unit.equals("px")) {
- values[1] = UNITS_PIXELS;
- } else if (unit.equals("%")) {
- values[1] = UNITS_PERCENTAGE;
- } else if (unit.equals("em")) {
- values[1] = UNITS_EM;
- } else if (unit.equals("ex")) {
- values[1] = UNITS_EX;
- } else if (unit.equals("in")) {
- values[1] = UNITS_INCH;
- } else if (unit.equals("cm")) {
- values[1] = UNITS_CM;
- } else if (unit.equals("mm")) {
- values[1] = UNITS_MM;
- } else if (unit.equals("pt")) {
- values[1] = UNITS_POINTS;
- } else if (unit.equals("pc")) {
- values[1] = UNITS_PICAS;
- }
+ String symbol = matcher.group(3);
+ unit = Unit.getUnitFromSymbol(symbol);
}
} else {
throw new IllegalArgumentException("Invalid size argument: \"" + s
+ "\" (should match " + sizePattern.pattern() + ")");
}
- return values;
+ return new Size(size, unit);
+ }
+
+ private static class Size implements Serializable {
+ float size;
+ Unit unit;
+
+ public Size(float size, Unit unit) {
+ this.size = size;
+ this.unit = unit;
+ }
+
+ public float getSize() {
+ return size;
+ }
+
+ public Unit getUnit() {
+ return unit;
+ }
}
public interface ComponentErrorEvent extends Terminal.ErrorEvent {
@@ -1517,4 +1458,206 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
-} \ No newline at end of file
+ /*
+ * Actions
+ */
+
+ /**
+ * Gets the {@link ActionManager} used to manage the
+ * {@link ShortcutListener}s added to this {@link Field}.
+ *
+ * @return the ActionManager in use
+ */
+ protected ActionManager getActionManager() {
+ if (actionManager == null) {
+ actionManager = new ActionManager();
+ setActionManagerViewer();
+ }
+ return actionManager;
+ }
+
+ /**
+ * Set a viewer for the action manager to be the parent sub window (if the
+ * component is in a window) or the root (otherwise). This is still a
+ * simplification of the real case as this should be handled by the parent
+ * VOverlay (on the client side) if the component is inside an VOverlay
+ * component.
+ */
+ private void setActionManagerViewer() {
+ if (actionManager != null && getRoot() != null) {
+ // Attached and has action manager
+ Window w = findAncestor(Window.class);
+ if (w != null) {
+ actionManager.setViewer(w);
+ } else {
+ actionManager.setViewer(getRoot());
+ }
+ }
+
+ }
+
+ public void addShortcutListener(ShortcutListener shortcut) {
+ getActionManager().addAction(shortcut);
+ }
+
+ public void removeShortcutListener(ShortcutListener shortcut) {
+ if (actionManager != null) {
+ actionManager.removeAction(shortcut);
+ }
+ }
+
+ /**
+ * Registers an RPC interface implementation for this component.
+ *
+ * A component can listen to multiple RPC interfaces, and subclasses can
+ * register additional implementations.
+ *
+ * @since 7.0
+ *
+ * @param implementation
+ * RPC interface implementation
+ * @param rpcInterfaceType
+ * RPC interface class for which the implementation should be
+ * registered
+ */
+ protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) {
+ rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(
+ implementation, rpcInterfaceType));
+ }
+
+ /**
+ * Registers an RPC interface implementation for this component.
+ *
+ * A component can listen to multiple RPC interfaces, and subclasses can
+ * register additional implementations.
+ *
+ * @since 7.0
+ *
+ * @param implementation
+ * RPC interface implementation. Also used to deduce the type.
+ */
+ protected <T extends ServerRpc> void registerRpc(T implementation) {
+ Class<?> cls = implementation.getClass();
+ Class<?>[] interfaces = cls.getInterfaces();
+ while (interfaces.length == 0) {
+ // Search upwards until an interface is found. It must be found as T
+ // extends ServerRpc
+ cls = cls.getSuperclass();
+ interfaces = cls.getInterfaces();
+ }
+ if (interfaces.length != 1
+ || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) {
+ throw new RuntimeException(
+ "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface");
+ }
+ Class<T> type = (Class<T>) interfaces[0];
+ registerRpc(implementation, type);
+ }
+
+ /**
+ * Returns an RPC proxy for a given server to client RPC interface for this
+ * component.
+ *
+ * TODO more javadoc, subclasses, ...
+ *
+ * @param rpcInterface
+ * RPC interface type
+ *
+ * @since 7.0
+ */
+ public <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) {
+ // create, initialize and return a dynamic proxy for RPC
+ try {
+ if (!rpcProxyMap.containsKey(rpcInterface)) {
+ Class<T> proxyClass = (Class) Proxy.getProxyClass(
+ rpcInterface.getClassLoader(), rpcInterface);
+ Constructor<T> constructor = proxyClass
+ .getConstructor(InvocationHandler.class);
+ T rpcProxy = constructor.newInstance(new RpcInvoicationHandler(
+ rpcInterface));
+ // cache the proxy
+ rpcProxyMap.put(rpcInterface, rpcProxy);
+ }
+ return (T) rpcProxyMap.get(rpcInterface);
+ } catch (Exception e) {
+ // TODO exception handling?
+ throw new RuntimeException(e);
+ }
+ }
+
+ private class RpcInvoicationHandler implements InvocationHandler,
+ Serializable {
+
+ private String rpcInterfaceName;
+
+ public RpcInvoicationHandler(Class<?> rpcInterface) {
+ rpcInterfaceName = rpcInterface.getName().replaceAll("\\$", ".");
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ addMethodInvocationToQueue(rpcInterfaceName, method, args);
+ // TODO no need to do full repaint if only RPC calls
+ requestRepaint();
+ return null;
+ }
+
+ }
+
+ /**
+ * For internal use: adds a method invocation to the pending RPC call queue.
+ *
+ * @param interfaceName
+ * RPC interface name
+ * @param methodName
+ * RPC method name
+ * @param parameters
+ * RPC vall parameters
+ *
+ * @since 7.0
+ */
+ protected void addMethodInvocationToQueue(String interfaceName,
+ Method method, Object[] parameters) {
+ // add to queue
+ pendingInvocations.add(new ClientMethodInvocation(this, interfaceName,
+ method, parameters));
+ }
+
+ /**
+ * @see RpcTarget#getRpcManager(Class)
+ *
+ * @param rpcInterface
+ * RPC interface for which a call was made
+ * @return RPC Manager handling calls for the interface
+ *
+ * @since 7.0
+ */
+ public RpcManager getRpcManager(Class<?> rpcInterface) {
+ return rpcManagerMap.get(rpcInterface);
+ }
+
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ if (pendingInvocations.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ List<ClientMethodInvocation> result = pendingInvocations;
+ pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ return Collections.unmodifiableList(result);
+ }
+ }
+
+ public String getConnectorId() {
+ if (connectorId == null) {
+ if (getApplication() == null) {
+ throw new RuntimeException(
+ "Component must be attached to an application when getConnectorId() is called for the first time");
+ }
+ connectorId = getApplication().createConnectorId(this);
+ }
+ return connectorId;
+ }
+
+ private Logger getLogger() {
+ return Logger.getLogger(AbstractComponent.class.getName());
+ }
+}
diff --git a/src/com/vaadin/ui/AbstractComponentContainer.java b/src/com/vaadin/ui/AbstractComponentContainer.java
index 5d5218307a..1c857a03cd 100644
--- a/src/com/vaadin/ui/AbstractComponentContainer.java
+++ b/src/com/vaadin/ui/AbstractComponentContainer.java
@@ -215,18 +215,20 @@ public abstract class AbstractComponentContainer extends AbstractComponent
}
@Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- if (getParent() != null && !getParent().isEnabled()) {
- // some ancestor still disabled, don't update children
+ public void setVisible(boolean visible) {
+ if (getState().isVisible() == visible) {
return;
- } else {
- requestRepaintAll();
}
+
+ super.setVisible(visible);
+ // If the visibility state is toggled it might affect all children
+ // aswell, e.g. make container visible should make children visible if
+ // they were only hidden because the container was hidden.
+ requestRepaintAll();
}
@Override
- public void setWidth(float width, int unit) {
+ public void setWidth(float width, Unit unit) {
/*
* child tree repaints may be needed, due to our fall back support for
* invalid relative sizes
@@ -237,9 +239,9 @@ public abstract class AbstractComponentContainer extends AbstractComponent
// children currently in invalid state may need repaint
dirtyChildren = getInvalidSizedChildren(false);
} else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED)
- || (unit == UNITS_PERCENTAGE
- && getWidthUnits() != UNITS_PERCENTAGE && !ComponentSizeValidator
- .parentCanDefineWidth(this))) {
+ || (unit == Unit.PERCENTAGE
+ && getWidthUnits() != Unit.PERCENTAGE && !ComponentSizeValidator
+ .parentCanDefineWidth(this))) {
/*
* relative width children may get to invalid state if width becomes
* invalid. Width may also become invalid if units become percentage
@@ -326,7 +328,7 @@ public abstract class AbstractComponentContainer extends AbstractComponent
}
@Override
- public void setHeight(float height, int unit) {
+ public void setHeight(float height, Unit unit) {
/*
* child tree repaints may be needed, due to our fall back support for
* invalid relative sizes
@@ -337,9 +339,9 @@ public abstract class AbstractComponentContainer extends AbstractComponent
// children currently in invalid state may need repaint
dirtyChildren = getInvalidSizedChildren(true);
} else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED)
- || (unit == UNITS_PERCENTAGE
- && getHeightUnits() != UNITS_PERCENTAGE && !ComponentSizeValidator
- .parentCanDefineHeight(this))) {
+ || (unit == Unit.PERCENTAGE
+ && getHeightUnits() != Unit.PERCENTAGE && !ComponentSizeValidator
+ .parentCanDefineHeight(this))) {
/*
* relative height children may get to invalid state if height
* becomes invalid. Height may also become invalid if units become
@@ -354,22 +356,56 @@ public abstract class AbstractComponentContainer extends AbstractComponent
}
public void requestRepaintAll() {
- requestRepaint();
- for (Iterator<Component> childIterator = getComponentIterator(); childIterator
- .hasNext();) {
+ requestRepaintAll(this);
+ }
+
+ /**
+ * Helper that implements the logic needed by requestRepaintAll. Calls
+ * requestRepaintAll/requestRepaint for the component container and all its
+ * children recursively.
+ *
+ * @param container
+ */
+ public static void requestRepaintAll(HasComponents container) {
+ container.requestRepaint();
+ if (container instanceof Panel) {
+ Panel p = (Panel) container;
+ // #2924 Panel is invalid, really invalid.
+ // Panel.getComponentIterator returns the children of content, not
+ // of Panel...
+ if (p.getContent() != null) {
+ p.getContent().requestRepaint();
+ }
+ }
+ for (Iterator<Component> childIterator = container
+ .getComponentIterator(); childIterator.hasNext();) {
Component c = childIterator.next();
- if (c instanceof Form) {
- // Form has children in layout, but is not ComponentContainer
- c.requestRepaint();
- ((Form) c).getLayout().requestRepaintAll();
- } else if (c instanceof Table) {
- ((Table) c).requestRepaintAll();
- } else if (c instanceof ComponentContainer) {
- ((ComponentContainer) c).requestRepaintAll();
+ if (c instanceof HasComponents) {
+ requestRepaintAll((HasComponents) c);
} else {
c.requestRepaint();
}
}
}
+ /**
+ * Returns an iterator for the child components.
+ *
+ * @return An iterator for the child components.
+ * @see #getComponentIterator()
+ * @since 7.0.0
+ */
+ public Iterator<Component> iterator() {
+ return getComponentIterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.HasComponents#isComponentVisible(com.vaadin.ui.Component)
+ */
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ }
} \ No newline at end of file
diff --git a/src/com/vaadin/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java
index e1d3270225..4efed11e2c 100644
--- a/src/com/vaadin/ui/AbstractField.java
+++ b/src/com/vaadin/ui/AbstractField.java
@@ -6,25 +6,29 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.Map;
+import java.util.List;
+import java.util.logging.Logger;
+import com.vaadin.Application;
import com.vaadin.data.Buffered;
import com.vaadin.data.Property;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterFactory;
import com.vaadin.event.Action;
-import com.vaadin.event.ActionManager;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
+import com.vaadin.terminal.AbstractErrorMessage;
import com.vaadin.terminal.CompositeErrorMessage;
import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.gwt.client.AbstractFieldState;
/**
* <p>
@@ -53,21 +57,29 @@ import com.vaadin.terminal.PaintTarget;
* @since 3.0
*/
@SuppressWarnings("serial")
-public abstract class AbstractField extends AbstractComponent implements Field,
- Property.ReadOnlyStatusChangeListener,
+public abstract class AbstractField<T> extends AbstractComponent implements
+ Field<T>, Property.ReadOnlyStatusChangeListener,
Property.ReadOnlyStatusChangeNotifier, Action.ShortcutNotifier {
/* Private members */
+ private static final Logger logger = Logger.getLogger(AbstractField.class
+ .getName());
+
/**
* Value of the abstract field.
*/
- private Object value;
+ private T value;
/**
+ * A converter used to convert from the data model type to the field type
+ * and vice versa.
+ */
+ private Converter<T, Object> converter = null;
+ /**
* Connected data-source.
*/
- private Property dataSource = null;
+ private Property<?> dataSource = null;
/**
* The list of validators.
@@ -85,11 +97,6 @@ public abstract class AbstractField extends AbstractComponent implements Field,
private boolean readThroughMode = true;
/**
- * Is the field modified but not committed.
- */
- private boolean modified = false;
-
- /**
* Flag to indicate that the field is currently committing its value to the
* datasource.
*/
@@ -111,31 +118,20 @@ public abstract class AbstractField extends AbstractComponent implements Field,
private boolean invalidCommitted = false;
/**
- * The tab order number of this field.
- */
- private int tabIndex = 0;
-
- /**
- * Required field.
- */
- private boolean required = false;
-
- /**
* The error message for the exception that is thrown when the field is
* required but empty.
*/
private String requiredError = "";
/**
- * Is automatic validation enabled.
+ * The error message that is shown when the field value cannot be converted.
*/
- private boolean validationVisible = true;
+ private String conversionError = "Could not convert value to {0}";
/**
- * Keeps track of the Actions added to this component; the actual
- * handling/notifying is delegated, usually to the containing window.
+ * Is automatic validation enabled.
*/
- private ActionManager actionManager;
+ private boolean validationVisible = true;
private boolean valueWasModifiedByDataSourceDuringCommit;
@@ -155,29 +151,6 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* Paints the field. Don't add a JavaDoc comment here, we use the default
* documentation from the implemented interface.
*/
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
-
- // The tab ordering number
- if (getTabIndex() != 0) {
- target.addAttribute("tabindex", getTabIndex());
- }
-
- // If the field is modified, but not committed, set modified attribute
- if (isModified()) {
- target.addAttribute("modified", true);
- }
-
- // Adds the required attribute
- if (!isReadOnly() && isRequired()) {
- target.addAttribute("required", true);
- }
-
- // Hide the error indicator if needed
- if (shouldHideErrors()) {
- target.addAttribute("hideErrors", true);
- }
- }
/**
* Returns true if the error indicator be hidden when painting the component
@@ -191,15 +164,21 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* to show it when there are errors
*/
protected boolean shouldHideErrors() {
- return isRequired() && isEmpty() && getComponentError() == null
- && getErrorMessage() != null;
+ // getErrorMessage() can still return something else than null based on
+ // validation etc.
+ return isRequired() && isEmpty() && getComponentError() == null;
}
- /*
- * Gets the field type Don't add a JavaDoc comment here, we use the default
- * documentation from the implemented interface.
+ /**
+ * Returns the type of the Field. The methods <code>getValue</code> and
+ * <code>setValue</code> must be compatible with this type: one must be able
+ * to safely cast the value returned from <code>getValue</code> to the given
+ * type and pass any variable assignable to this type as an argument to
+ * <code>setValue</code>.
+ *
+ * @return the type of the Field
*/
- public abstract Class<?> getType();
+ public abstract Class<? extends T> getType();
/**
* The abstract field is read only also if the data source is in read only
@@ -247,23 +226,21 @@ public abstract class AbstractField extends AbstractComponent implements Field,
public void commit() throws Buffered.SourceException, InvalidValueException {
if (dataSource != null && !dataSource.isReadOnly()) {
if ((isInvalidCommitted() || isValid())) {
- final Object newValue = getValue();
try {
// Commits the value to datasource.
valueWasModifiedByDataSourceDuringCommit = false;
committingValueToDataSource = true;
- dataSource.setValue(newValue);
-
+ getPropertyDataSource().setValue(getConvertedValue());
} catch (final Throwable e) {
// Sets the buffering state.
- currentBufferedSourceException = new Buffered.SourceException(
+ SourceException sourceException = new Buffered.SourceException(
this, e);
- requestRepaint();
+ setCurrentBufferedSourceException(sourceException);
// Throws the source exception.
- throw currentBufferedSourceException;
+ throw sourceException;
} finally {
committingValueToDataSource = false;
}
@@ -273,25 +250,19 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
}
- boolean repaintNeeded = false;
-
// The abstract field is not modified anymore
- if (modified) {
- modified = false;
- repaintNeeded = true;
+ if (isModified()) {
+ setModified(false);
}
// If successful, remove set the buffering state to be ok
- if (currentBufferedSourceException != null) {
- currentBufferedSourceException = null;
- repaintNeeded = true;
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
}
if (valueWasModifiedByDataSourceDuringCommit) {
valueWasModifiedByDataSourceDuringCommit = false;
fireValueChange(false);
- } else if (repaintNeeded) {
- requestRepaint();
}
}
@@ -304,19 +275,18 @@ public abstract class AbstractField extends AbstractComponent implements Field,
if (dataSource != null) {
// Gets the correct value from datasource
- Object newValue;
+ T newFieldValue;
try {
// Discards buffer by overwriting from datasource
- newValue = String.class == getType() ? dataSource.toString()
- : dataSource.getValue();
+ newFieldValue = convertFromDataSource(getDataSourceValue());
// If successful, remove set the buffering state to be ok
- if (currentBufferedSourceException != null) {
- currentBufferedSourceException = null;
- requestRepaint();
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
}
} catch (final Throwable e) {
+ // FIXME: What should really be done here if conversion fails?
// Sets the buffering state
currentBufferedSourceException = new Buffered.SourceException(
@@ -328,29 +298,58 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
final boolean wasModified = isModified();
- modified = false;
+ setModified(false);
// If the new value differs from the previous one
- if ((newValue == null && value != null)
- || (newValue != null && !newValue.equals(value))) {
- setInternalValue(newValue);
+ if (!equals(newFieldValue, getInternalValue())) {
+ setInternalValue(newFieldValue);
fireValueChange(false);
- }
-
- // If the value did not change, but the modification status did
- else if (wasModified) {
+ } else if (wasModified) {
+ // If the value did not change, but the modification status did
requestRepaint();
}
}
}
+ /**
+ * Gets the value from the data source. This is only here because of clarity
+ * in the code that handles both the data model value and the field value.
+ *
+ * @return The value of the property data source
+ */
+ private Object getDataSourceValue() {
+ return dataSource.getValue();
+ }
+
+ /**
+ * Returns the field value. This is always identical to {@link #getValue()}
+ * and only here because of clarity in the code that handles both the data
+ * model value and the field value.
+ *
+ * @return The value of the field
+ */
+ private T getFieldValue() {
+ // Give the value from abstract buffers if the field if possible
+ if (dataSource == null || !isReadThrough() || isModified()) {
+ return getInternalValue();
+ }
+
+ // There is no buffered value so use whatever the data model provides
+ return convertFromDataSource(getDataSourceValue());
+ }
+
/*
* Has the field been modified since the last commit()? Don't add a JavaDoc
* comment here, we use the default documentation from the implemented
* interface.
*/
public boolean isModified() {
- return modified;
+ return getState().isModified();
+ }
+
+ private void setModified(boolean modified) {
+ getState().setModified(modified);
+ requestRepaint();
}
/*
@@ -361,11 +360,28 @@ public abstract class AbstractField extends AbstractComponent implements Field,
return writeThroughMode;
}
- /*
- * Sets the field's write-through mode to the specified status Don't add a
- * JavaDoc comment here, we use the default documentation from the
- * implemented interface.
+ /**
+ * Sets the field's write-through mode to the specified status. When
+ * switching the write-through mode on, a {@link #commit()} will be
+ * performed.
+ *
+ * @see #setBuffered(boolean) for an easier way to control read through and
+ * write through modes
+ *
+ * @param writeThrough
+ * Boolean value to indicate if the object should be in
+ * write-through mode after the call.
+ * @throws SourceException
+ * If the operation fails because of an exception is thrown by
+ * the data source.
+ * @throws InvalidValueException
+ * If the implicit commit operation fails because of a
+ * validation error.
+ * @deprecated Use {@link #setBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public void setWriteThrough(boolean writeThrough)
throws Buffered.SourceException, InvalidValueException {
if (writeThroughMode == writeThrough) {
@@ -385,38 +401,96 @@ public abstract class AbstractField extends AbstractComponent implements Field,
return readThroughMode;
}
- /*
- * Sets the field's read-through mode to the specified status Don't add a
- * JavaDoc comment here, we use the default documentation from the
- * implemented interface.
+ /**
+ * Sets the field's read-through mode to the specified status. When
+ * switching read-through mode on, the object's value is updated from the
+ * data source.
+ *
+ * @see #setBuffered(boolean) for an easier way to control read through and
+ * write through modes
+ *
+ * @param readThrough
+ * Boolean value to indicate if the object should be in
+ * read-through mode after the call.
+ *
+ * @throws SourceException
+ * If the operation fails because of an exception is thrown by
+ * the data source. The cause is included in the exception.
+ * @deprecated Use {@link #setBuffered(boolean)} instead. Note that
+ * setReadThrough(true), setWriteThrough(true) equals
+ * setBuffered(false)
*/
+ @Deprecated
public void setReadThrough(boolean readThrough)
throws Buffered.SourceException {
if (readThroughMode == readThrough) {
return;
}
readThroughMode = readThrough;
- if (!isModified() && readThroughMode && dataSource != null) {
- setInternalValue(String.class == getType() ? dataSource.toString()
- : dataSource.getValue());
+ if (!isModified() && readThroughMode && getPropertyDataSource() != null) {
+ setInternalValue(convertFromDataSource(getDataSourceValue()));
fireValueChange(false);
}
}
+ /**
+ * Sets the buffered mode of this Field.
+ * <p>
+ * When the field is in buffered mode, changes will not be committed to the
+ * property data source until {@link #commit()} is called.
+ * </p>
+ * <p>
+ * Changing buffered mode will change the read through and write through
+ * state for the field.
+ * </p>
+ * <p>
+ * Mixing calls to {@link #setBuffered(boolean)} and
+ * {@link #setReadThrough(boolean)} or {@link #setWriteThrough(boolean)} is
+ * generally a bad idea.
+ * </p>
+ *
+ * @param buffered
+ * true if buffered mode should be turned on, false otherwise
+ */
+ public void setBuffered(boolean buffered) {
+ setReadThrough(!buffered);
+ setWriteThrough(!buffered);
+ }
+
+ /**
+ * Checks the buffered mode of this Field.
+ * <p>
+ * This method only returns true if both read and write buffering is used.
+ *
+ * @return true if buffered mode is on, false otherwise
+ */
+ public boolean isBuffered() {
+ return !isReadThrough() && !isWriteThrough();
+ }
+
/* Property interface implementation */
/**
- * Returns the value of the Property in human readable textual format.
+ * Returns the (field) value converted to a String using toString().
*
* @see java.lang.Object#toString()
+ * @deprecated Instead use {@link #getValue()} to get the value of the
+ * field, {@link #getConvertedValue()} to get the field value
+ * converted to the data model type or
+ * {@link #getPropertyDataSource()} .getValue() to get the value
+ * of the data source.
*/
+ @Deprecated
@Override
public String toString() {
- final Object value = getValue();
+ logger.warning("You are using AbstractField.toString() to get the value for a "
+ + getClass().getSimpleName()
+ + ". This is not recommended and will not be supported in future versions.");
+ final Object value = getFieldValue();
if (value == null) {
return null;
}
- return getValue().toString();
+ return value.toString();
}
/**
@@ -424,67 +498,63 @@ public abstract class AbstractField extends AbstractComponent implements Field,
*
* <p>
* This is the visible, modified and possible invalid value the user have
- * entered to the field. In the read-through mode, the abstract buffer is
- * also updated and validation is performed.
+ * entered to the field.
* </p>
*
* <p>
* Note that the object returned is compatible with getType(). For example,
* if the type is String, this returns Strings even when the underlying
- * datasource is of some other type. In order to access the datasources
- * native type, use getPropertyDatasource().getValue() instead.
+ * datasource is of some other type. In order to access the converted value,
+ * use {@link #getConvertedValue()} and to access the value of the property
+ * data source, use {@link Property#getValue()} for the property data
+ * source.
* </p>
*
* <p>
- * Note that when you extend AbstractField, you must reimplement this method
- * if datasource.getValue() is not assignable to class returned by getType()
- * AND getType() is not String. In case of Strings, getValue() calls
- * datasource.toString() instead of datasource.getValue().
+ * Since Vaadin 7.0, no implicit conversions between other data types and
+ * String are performed, but a converter is used if set.
* </p>
*
* @return the current value of the field.
*/
- public Object getValue() {
-
- // Give the value from abstract buffers if the field if possible
- if (dataSource == null || !isReadThrough() || isModified()) {
- return value;
- }
-
- Object newValue = String.class == getType() ? dataSource.toString()
- : dataSource.getValue();
-
- return newValue;
+ public T getValue() {
+ return getFieldValue();
}
/**
* Sets the value of the field.
*
- * @param newValue
+ * @param newFieldValue
* the New value of the field.
* @throws Property.ReadOnlyException
- * @throws Property.ConversionException
*/
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException {
- setValue(newValue, false);
+ public void setValue(Object newFieldValue)
+ throws Property.ReadOnlyException, Converter.ConversionException {
+ // This check is needed as long as setValue accepts Object instead of T
+ if (newFieldValue != null) {
+ if (!getType().isAssignableFrom(newFieldValue.getClass())) {
+ throw new Converter.ConversionException("Value of type "
+ + newFieldValue.getClass() + " cannot be assigned to "
+ + getType().getName());
+ }
+ }
+ setValue((T) newFieldValue, false);
}
/**
* Sets the value of the field.
*
- * @param newValue
+ * @param newFieldValue
* the New value of the field.
* @param repaintIsNotNeeded
* True iff caller is sure that repaint is not needed.
* @throws Property.ReadOnlyException
- * @throws Property.ConversionException
*/
- protected void setValue(Object newValue, boolean repaintIsNotNeeded)
- throws Property.ReadOnlyException, Property.ConversionException {
+ protected void setValue(T newFieldValue, boolean repaintIsNotNeeded)
+ throws Property.ReadOnlyException, Converter.ConversionException,
+ InvalidValueException {
- if ((newValue == null && value != null)
- || (newValue != null && !newValue.equals(value))) {
+ if (!equals(newFieldValue, getInternalValue())) {
// Read only fields can not be changed
if (isReadOnly()) {
@@ -493,24 +563,24 @@ public abstract class AbstractField extends AbstractComponent implements Field,
// Repaint is needed even when the client thinks that it knows the
// new state if validity of the component may change
- if (repaintIsNotNeeded && (isRequired() || getValidators() != null)) {
+ if (repaintIsNotNeeded
+ && (isRequired() || getValidators() != null || getConverter() != null)) {
repaintIsNotNeeded = false;
}
- // If invalid values are not allowed, the value must be checked
if (!isInvalidAllowed()) {
- final Collection<Validator> v = getValidators();
- if (v != null) {
- for (final Iterator<Validator> i = v.iterator(); i
- .hasNext();) {
- (i.next()).validate(newValue);
- }
- }
+ /*
+ * If invalid values are not allowed the value must be validated
+ * before it is set. If validation fails, the
+ * InvalidValueException is thrown and the internal value is not
+ * updated.
+ */
+ validate(newFieldValue);
}
// Changes the value
- setInternalValue(newValue);
- modified = dataSource != null;
+ setInternalValue(newFieldValue);
+ setModified(dataSource != null);
valueWasModifiedByDataSourceDuringCommit = false;
// In write through mode , try to commit
@@ -520,10 +590,11 @@ public abstract class AbstractField extends AbstractComponent implements Field,
// Commits the value to datasource
committingValueToDataSource = true;
- dataSource.setValue(newValue);
+ getPropertyDataSource().setValue(
+ convertToDataSource(newFieldValue));
// The buffer is now unmodified
- modified = false;
+ setModified(false);
} catch (final Throwable e) {
@@ -540,9 +611,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
// If successful, remove set the buffering state to be ok
- if (currentBufferedSourceException != null) {
- currentBufferedSourceException = null;
- requestRepaint();
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
}
if (valueWasModifiedByDataSourceDuringCommit) {
@@ -559,6 +629,13 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
}
+ private static boolean equals(Object value1, Object value2) {
+ if (value1 == null) {
+ return value2 == null;
+ }
+ return value1.equals(value2);
+ }
+
/* External data source */
/**
@@ -612,25 +689,42 @@ public abstract class AbstractField extends AbstractComponent implements Field,
public void setPropertyDataSource(Property newDataSource) {
// Saves the old value
- final Object oldValue = value;
+ final Object oldValue = getInternalValue();
// Stop listening to the old data source
removePropertyListeners();
// Sets the new data source
dataSource = newDataSource;
-
+ getState().setPropertyReadOnly(
+ dataSource == null ? false : dataSource.isReadOnly());
+
+ // Check if the current converter is compatible.
+ if (newDataSource != null
+ && (getConverter() == null || !newDataSource.getType()
+ .isAssignableFrom(getConverter().getModelType()))) {
+ // Changing from e.g. Number -> Double should set a new converter,
+ // changing from Double -> Number can keep the old one (Property
+ // accepts Number)
+
+ // Set a new converter if there is a new data source and
+ // there is no old converter or the old is incompatible.
+ setConverter(newDataSource.getType());
+ }
// Gets the value from source
try {
if (dataSource != null) {
- setInternalValue(String.class == getType() ? dataSource
- .toString() : dataSource.getValue());
+ T fieldValue = convertFromDataSource(getDataSourceValue());
+ setInternalValue(fieldValue);
+ }
+ setModified(false);
+ if (getCurrentBufferedSourceException() != null) {
+ setCurrentBufferedSourceException(null);
}
- modified = false;
} catch (final Throwable e) {
- currentBufferedSourceException = new Buffered.SourceException(this,
- e);
- modified = true;
+ setCurrentBufferedSourceException(new Buffered.SourceException(
+ this, e));
+ setModified(true);
}
// Listen to new data source if possible
@@ -649,12 +743,159 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
// Fires value change if the value has changed
+ T value = getInternalValue();
if ((value != oldValue)
&& ((value != null && !value.equals(oldValue)) || value == null)) {
fireValueChange(false);
}
}
+ /**
+ * Retrieves a converter for the field from the converter factory defined
+ * for the application. Clears the converter if no application reference is
+ * available or if the factory returns null.
+ *
+ * @param datamodelType
+ * The type of the data model that we want to be able to convert
+ * from
+ */
+ public void setConverter(Class<?> datamodelType) {
+ Converter<T, ?> converter = null;
+
+ Application app = Application.getCurrentApplication();
+ if (app != null) {
+ ConverterFactory factory = app.getConverterFactory();
+ converter = (Converter<T, ?>) factory.createConverter(getType(),
+ datamodelType);
+ }
+ setConverter(converter);
+ }
+
+ /**
+ * Convert the given value from the data source type to the UI type.
+ *
+ * @param newValue
+ * The data source value to convert.
+ * @return The converted value that is compatible with the UI type or the
+ * original value if its type is compatible and no converter is set.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ @SuppressWarnings("unchecked")
+ private T convertFromDataSource(Object newValue)
+ throws Converter.ConversionException {
+ if (converter != null) {
+ return converter.convertToPresentation(newValue, getLocale());
+ }
+ if (newValue == null) {
+ return null;
+ }
+
+ if (getType().isAssignableFrom(newValue.getClass())) {
+ return (T) newValue;
+ } else {
+ throw new Converter.ConversionException(
+ "Unable to convert value of type "
+ + newValue.getClass().getName()
+ + " to "
+ + getType()
+ + ". No converter is set and the types are not compatible.");
+ }
+ }
+
+ /**
+ * Convert the given value from the UI type to the data source type.
+ *
+ * @param fieldValue
+ * The value to convert. Typically returned by
+ * {@link #getFieldValue()}
+ * @return The converted value that is compatible with the data source type.
+ * @throws Converter.ConversionException
+ * if there is no converter and the type is not compatible with
+ * the data source type.
+ */
+ private Object convertToDataSource(T fieldValue)
+ throws Converter.ConversionException {
+ if (converter != null) {
+ /*
+ * If there is a converter, always use it. It must convert or throw
+ * an exception.
+ */
+ try {
+ return converter.convertToModel(fieldValue, getLocale());
+ } catch (com.vaadin.data.util.converter.Converter.ConversionException e) {
+ throw new Converter.ConversionException(
+ getConversionError(converter.getModelType()), e);
+ }
+ }
+
+ if (fieldValue == null) {
+ // Null should always be passed through the converter but if there
+ // is no converter we can safely return null
+ return null;
+ }
+
+ // check that the value class is compatible with the data source type
+ // (if data source set) or field type
+ Class<?> type;
+ if (getPropertyDataSource() != null) {
+ type = getPropertyDataSource().getType();
+ } else {
+ type = getType();
+ }
+
+ if (type.isAssignableFrom(fieldValue.getClass())) {
+ return fieldValue;
+ } else {
+ throw new Converter.ConversionException(getConversionError(type));
+ }
+ }
+
+ /**
+ * Returns the conversion error with {0} replaced by the data source type.
+ *
+ * @param dataSourceType
+ * The type of the data source
+ * @return The value conversion error string with parameters replaced.
+ */
+ protected String getConversionError(Class<?> dataSourceType) {
+ if (dataSourceType == null) {
+ return getConversionError();
+ } else {
+ return getConversionError().replace("{0}",
+ dataSourceType.getSimpleName());
+ }
+ }
+
+ /**
+ * Returns the current value (as returned by {@link #getValue()}) converted
+ * to the data source type.
+ * <p>
+ * This returns the same as {@link AbstractField#getValue()} if no converter
+ * has been set. The value is not necessarily the same as the data source
+ * value e.g. if the field is in buffered mode and has been modified.
+ * </p>
+ *
+ * @return The converted value that is compatible with the data source type
+ */
+ public Object getConvertedValue() {
+ return convertToDataSource(getFieldValue());
+ }
+
+ /**
+ * Sets the value of the field using a value of the data source type. The
+ * value given is converted to the field type and then assigned to the
+ * field. This will update the property data source in the same way as when
+ * {@link #setValue(Object)} is called.
+ *
+ * @param value
+ * The value to set. Must be the same type as the data source.
+ */
+ public void setConvertedValue(Object value) {
+ setValue(convertFromDataSource(value));
+ }
+
/* Validation */
/**
@@ -713,102 +954,99 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* empty. If the field is empty it is considered valid if it is not required
* and invalid otherwise. Validators are never checked for empty fields.
*
+ * In most cases, {@link #validate()} should be used instead of
+ * {@link #isValid()} to also get the error message.
+ *
* @return <code>true</code> if all registered validators claim that the
* current value is valid or if the field is empty and not required,
* <code>false</code> otherwise.
*/
public boolean isValid() {
- if (isEmpty()) {
- if (isRequired()) {
- return false;
- } else {
- return true;
- }
- }
-
- if (validators == null) {
+ try {
+ validate();
return true;
+ } catch (InvalidValueException e) {
+ return false;
}
-
- final Object value = getValue();
- for (final Iterator<Validator> i = validators.iterator(); i.hasNext();) {
- if (!(i.next()).isValid(value)) {
- return false;
- }
- }
-
- return true;
}
/**
- * Checks the validity of the Validatable by validating the field with all
- * attached validators except when the field is empty. An empty field is
- * invalid if it is required and valid otherwise.
+ * Checks the validity of the Field.
+ *
+ * A field is invalid if it is set as required (using
+ * {@link #setRequired(boolean)} and is empty, if one or several of the
+ * validators added to the field indicate it is invalid or if the value
+ * cannot be converted provided a converter has been set.
*
* The "required" validation is a built-in validation feature. If the field
- * is required, but empty, validation will throw an EmptyValueException with
- * the error message set with setRequiredError().
+ * is required and empty this method throws an EmptyValueException with the
+ * error message set using {@link #setRequiredError(String)}.
*
* @see com.vaadin.data.Validatable#validate()
*/
public void validate() throws Validator.InvalidValueException {
- if (isEmpty()) {
- if (isRequired()) {
- throw new Validator.EmptyValueException(requiredError);
- } else {
- return;
- }
+ if (isRequired() && isEmpty()) {
+ throw new Validator.EmptyValueException(requiredError);
}
+ validate(getFieldValue());
+ }
- // If there is no validator, there can not be any errors
- if (validators == null) {
- return;
- }
+ /**
+ * Validates that the given value pass the validators for the field.
+ * <p>
+ * This method does not check the requiredness of the field.
+ *
+ * @param fieldValue
+ * The value to check
+ * @throws Validator.InvalidValueException
+ * if one or several validators fail
+ */
+ protected void validate(T fieldValue)
+ throws Validator.InvalidValueException {
- // Initialize temps
- Validator.InvalidValueException firstError = null;
- LinkedList<InvalidValueException> errors = null;
- final Object value = getValue();
+ Object valueToValidate = fieldValue;
- // Gets all the validation errors
- for (final Iterator<Validator> i = validators.iterator(); i.hasNext();) {
+ // If there is a converter we start by converting the value as we want
+ // to validate the converted value
+ if (getConverter() != null) {
try {
- (i.next()).validate(value);
- } catch (final Validator.InvalidValueException e) {
- if (firstError == null) {
- firstError = e;
- } else {
- if (errors == null) {
- errors = new LinkedList<InvalidValueException>();
- errors.add(firstError);
- }
- errors.add(e);
+ valueToValidate = getConverter().convertToModel(fieldValue,
+ getLocale());
+ } catch (Exception e) {
+ throw new InvalidValueException(
+ getConversionError(getConverter().getModelType()));
+ }
+ }
+
+ List<InvalidValueException> validationExceptions = new ArrayList<InvalidValueException>();
+ if (validators != null) {
+ // Gets all the validation errors
+ for (Validator v : validators) {
+ try {
+ v.validate(valueToValidate);
+ } catch (final Validator.InvalidValueException e) {
+ validationExceptions.add(e);
}
}
}
- // If there were no error
- if (firstError == null) {
+ // If there were no errors
+ if (validationExceptions.isEmpty()) {
return;
}
// If only one error occurred, throw it forwards
- if (errors == null) {
- throw firstError;
+ if (validationExceptions.size() == 1) {
+ throw validationExceptions.get(0);
}
- // Creates composite validator
- final Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
- .size()];
- int index = 0;
- for (final Iterator<InvalidValueException> i = errors.iterator(); i
- .hasNext();) {
- exceptions[index++] = i.next();
- }
+ InvalidValueException[] exceptionArray = validationExceptions
+ .toArray(new InvalidValueException[validationExceptions.size()]);
- throw new Validator.InvalidValueException(null, exceptions);
+ // Create a composite validator and include all exceptions
+ throw new Validator.InvalidValueException(null, exceptionArray);
}
/**
@@ -856,7 +1094,7 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* the requiredError string. For these fields the exclamation mark will
* be hidden but the error must still be sent to the client.
*/
- ErrorMessage validationError = null;
+ Validator.InvalidValueException validationError = null;
if (isValidationVisible()) {
try {
validate();
@@ -872,13 +1110,18 @@ public abstract class AbstractField extends AbstractComponent implements Field,
// Return if there are no errors at all
if (superError == null && validationError == null
- && currentBufferedSourceException == null) {
+ && getCurrentBufferedSourceException() == null) {
return null;
}
// Throw combination of the error types
- return new CompositeErrorMessage(new ErrorMessage[] { superError,
- validationError, currentBufferedSourceException });
+ return new CompositeErrorMessage(
+ new ErrorMessage[] {
+ superError,
+ AbstractErrorMessage
+ .getErrorMessageForException(validationError),
+ AbstractErrorMessage
+ .getErrorMessageForException(getCurrentBufferedSourceException()) });
}
@@ -952,6 +1195,7 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* @see Property.ReadOnlyStatusChangeListener
*/
public void readOnlyStatusChange(Property.ReadOnlyStatusChangeEvent event) {
+ getState().setPropertyReadOnly(event.getProperty().isReadOnly());
requestRepaint();
}
@@ -964,8 +1208,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* @VERSION@
* @since 3.0
*/
- public class ReadOnlyStatusChangeEvent extends Component.Event implements
- Property.ReadOnlyStatusChangeEvent, Serializable {
+ public static class ReadOnlyStatusChangeEvent extends Component.Event
+ implements Property.ReadOnlyStatusChangeEvent, Serializable {
/**
* New instance of text change event.
@@ -1029,10 +1273,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
public void valueChange(Property.ValueChangeEvent event) {
if (isReadThrough()) {
if (committingValueToDataSource) {
- boolean propertyNotifiesOfTheBufferedValue = event
- .getProperty().getValue() == value
- || (value != null && value.equals(event.getProperty()
- .getValue()));
+ boolean propertyNotifiesOfTheBufferedValue = equals(event
+ .getProperty().getValue(), getInternalValue());
if (!propertyNotifiesOfTheBufferedValue) {
/*
* Property (or chained property like PropertyFormatter) now
@@ -1055,12 +1297,7 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
private void readValueFromProperty(Property.ValueChangeEvent event) {
- setInternalValue(event.getProperty().getValue());
- }
-
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
+ setInternalValue(convertFromDataSource(event.getProperty().getValue()));
}
/**
@@ -1071,32 +1308,13 @@ public abstract class AbstractField extends AbstractComponent implements Field,
super.focus();
}
- /**
- * Creates abstract field by the type of the property.
- *
- * <p>
- * This returns most suitable field type for editing property of given type.
- * </p>
- *
- * @param propertyType
- * the Type of the property, that needs to be edited.
- * @deprecated use e.g.
- * {@link DefaultFieldFactory#createFieldByPropertyType(Class)}
- * instead
- */
- @Deprecated
- public static AbstractField constructField(Class<?> propertyType) {
- return (AbstractField) DefaultFieldFactory
- .createFieldByPropertyType(propertyType);
- }
-
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.Component.Focusable#getTabIndex()
*/
public int getTabIndex() {
- return tabIndex;
+ return getState().getTabIndex();
}
/*
@@ -1105,20 +1323,39 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
*/
public void setTabIndex(int tabIndex) {
- this.tabIndex = tabIndex;
+ getState().setTabIndex(tabIndex);
requestRepaint();
}
/**
+ * Returns the internal field value, which might not match the data source
+ * value e.g. if the field has been modified and is not in write-through
+ * mode.
+ *
+ * This method can be overridden by subclasses together with
+ * {@link #setInternalValue(Object)} to compute internal field value at
+ * runtime. When doing so, typically also {@link #isModified()} needs to be
+ * overridden and care should be taken in the management of the empty state
+ * and buffering support.
+ *
+ * @return internal field value
+ */
+ protected T getInternalValue() {
+ return value;
+ }
+
+ /**
* Sets the internal field value. This is purely used by AbstractField to
* change the internal Field value. It does not trigger valuechange events.
* It can be overridden by the inheriting classes to update all dependent
* variables.
*
+ * Subclasses can also override {@link #getInternalValue()} if necessary.
+ *
* @param newValue
* the new value to be set.
*/
- protected void setInternalValue(Object newValue) {
+ protected void setInternalValue(T newValue) {
value = newValue;
if (validators != null && !validators.isEmpty()) {
requestRepaint();
@@ -1133,9 +1370,6 @@ public abstract class AbstractField extends AbstractComponent implements Field,
@Override
public void attach() {
super.attach();
- if (actionManager != null) {
- actionManager.setViewer(getWindow());
- }
if (!isListeningToPropertyEvents) {
addPropertyListeners();
@@ -1149,9 +1383,6 @@ public abstract class AbstractField extends AbstractComponent implements Field,
@Override
public void detach() {
super.detach();
- if (actionManager != null) {
- actionManager.setViewer((Window) null);
- }
// Stop listening to data source events on detach to avoid a potential
// memory leak. See #6155.
removePropertyListeners();
@@ -1170,11 +1401,11 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* field isEmpty() regardless of any attached validators.
*
*
- * @return <code>true</code> if the field is required .otherwise
+ * @return <code>true</code> if the field is required, otherwise
* <code>false</code>.
*/
public boolean isRequired() {
- return required;
+ return getState().isRequired();
}
/**
@@ -1193,7 +1424,7 @@ public abstract class AbstractField extends AbstractComponent implements Field,
* Is the field required.
*/
public void setRequired(boolean required) {
- this.required = required;
+ getState().setRequired(required);
requestRepaint();
}
@@ -1216,13 +1447,36 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
/**
+ * Gets the error that is shown if the field value cannot be converted to
+ * the data source type.
+ *
+ * @return The error that is shown if conversion of the field value fails
+ */
+ public String getConversionError() {
+ return conversionError;
+ }
+
+ /**
+ * Sets the error that is shown if the field value cannot be converted to
+ * the data source type. If {0} is present in the message, it will be
+ * replaced by the simple name of the data source type.
+ *
+ * @param valueConversionError
+ * Message to be shown when conversion of the value fails
+ */
+ public void setConversionError(String valueConversionError) {
+ this.conversionError = valueConversionError;
+ requestRepaint();
+ }
+
+ /**
* Is the field empty?
*
* In general, "empty" state is same as null. As an exception, TextField
* also treats empty string as "empty".
*/
protected boolean isEmpty() {
- return (getValue() == null);
+ return (getFieldValue() == null);
}
/**
@@ -1270,34 +1524,13 @@ public abstract class AbstractField extends AbstractComponent implements Field,
requestRepaint();
}
- /*
- * Actions
- */
-
/**
- * Gets the {@link ActionManager} used to manage the
- * {@link ShortcutListener}s added to this {@link Field}.
+ * Gets the current buffered source exception.
*
- * @return the ActionManager in use
+ * @return The current source exception
*/
- protected ActionManager getActionManager() {
- if (actionManager == null) {
- actionManager = new ActionManager();
- if (getWindow() != null) {
- actionManager.setViewer(getWindow());
- }
- }
- return actionManager;
- }
-
- public void addShortcutListener(ShortcutListener shortcut) {
- getActionManager().addAction(shortcut);
- }
-
- public void removeShortcutListener(ShortcutListener shortcut) {
- if (actionManager != null) {
- actionManager.removeAction(shortcut);
- }
+ protected Buffered.SourceException getCurrentBufferedSourceException() {
+ return currentBufferedSourceException;
}
/**
@@ -1357,6 +1590,42 @@ public abstract class AbstractField extends AbstractComponent implements Field,
}
/**
+ * Gets the converter used to convert the property data source value to the
+ * field value.
+ *
+ * @return The converter or null if none is set.
+ */
+ public Converter<T, Object> getConverter() {
+ return converter;
+ }
+
+ /**
+ * Sets the converter used to convert the field value to property data
+ * source type. The converter must have a presentation type that matches the
+ * field type.
+ *
+ * @param converter
+ * The new converter to use.
+ */
+ public void setConverter(Converter<T, ?> converter) {
+ this.converter = (Converter<T, Object>) converter;
+ requestRepaint();
+ }
+
+ @Override
+ public AbstractFieldState getState() {
+ return (AbstractFieldState) super.getState();
+ }
+
+ @Override
+ public void updateState() {
+ super.updateState();
+
+ // Hide the error indicator if needed
+ getState().setHideErrors(shouldHideErrors());
+ }
+
+ /**
* Registers this as an event listener for events sent by the data source
* (if any). Does nothing if
* <code>isListeningToPropertyEvents == true</code>.
@@ -1391,4 +1660,4 @@ public abstract class AbstractField extends AbstractComponent implements Field,
isListeningToPropertyEvents = false;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/ui/AbstractLayout.java b/src/com/vaadin/ui/AbstractLayout.java
index 378a59a4ad..4876b40265 100644
--- a/src/com/vaadin/ui/AbstractLayout.java
+++ b/src/com/vaadin/ui/AbstractLayout.java
@@ -4,14 +4,7 @@
package com.vaadin.ui;
-import java.util.Map;
-
-import com.vaadin.event.LayoutEvents.LayoutClickEvent;
-import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.AbstractLayoutState;
import com.vaadin.ui.Layout.MarginHandler;
/**
@@ -27,10 +20,13 @@ import com.vaadin.ui.Layout.MarginHandler;
public abstract class AbstractLayout extends AbstractComponentContainer
implements Layout, MarginHandler {
- private static final String CLICK_EVENT = EventId.LAYOUT_CLICK;
-
protected MarginInfo margins = new MarginInfo(false);
+ @Override
+ public AbstractLayoutState getState() {
+ return (AbstractLayoutState) super.getState();
+ }
+
/*
* (non-Javadoc)
*
@@ -38,6 +34,7 @@ public abstract class AbstractLayout extends AbstractComponentContainer
*/
public void setMargin(boolean enabled) {
margins.setMargins(enabled);
+ getState().setMarginsBitmask(margins.getBitMask());
requestRepaint();
}
@@ -57,6 +54,7 @@ public abstract class AbstractLayout extends AbstractComponentContainer
*/
public void setMargin(MarginInfo marginInfo) {
margins.setMargins(marginInfo);
+ getState().setMarginsBitmask(margins.getBitMask());
requestRepaint();
}
@@ -68,62 +66,8 @@ public abstract class AbstractLayout extends AbstractComponentContainer
public void setMargin(boolean topEnabled, boolean rightEnabled,
boolean bottomEnabled, boolean leftEnabled) {
margins.setMargins(topEnabled, rightEnabled, bottomEnabled, leftEnabled);
+ getState().setMarginsBitmask(margins.getBitMask());
requestRepaint();
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#paintContent(com.vaadin
- * .terminal.PaintTarget)
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
-
- // Add margin info. Defaults to false.
- target.addAttribute("margins", margins.getBitMask());
-
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
- * java.util.Map)
- */
- @SuppressWarnings("unchecked")
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
- // not all subclasses use these events
- if (this instanceof LayoutClickNotifier
- && variables.containsKey(CLICK_EVENT)) {
- fireClick((Map<String, Object>) variables.get(CLICK_EVENT));
- }
-
- }
-
- /**
- * Fire a layout click event.
- *
- * Note that this method is only used by the subclasses that implement
- * {@link LayoutClickNotifier}, and can be overridden for custom click event
- * firing.
- *
- * @param parameters
- * The parameters received from the client side implementation
- */
- protected void fireClick(Map<String, Object> parameters) {
- MouseEventDetails mouseDetails = MouseEventDetails
- .deSerialize((String) parameters.get("mouseDetails"));
- Component clickedComponent = (Component) parameters.get("component");
- Component childComponent = clickedComponent;
- while (childComponent != null && childComponent.getParent() != this) {
- childComponent = childComponent.getParent();
- }
-
- fireEvent(new LayoutClickEvent(this, mouseDetails, clickedComponent,
- childComponent));
- }
-
}
diff --git a/src/com/vaadin/ui/AbstractMedia.java b/src/com/vaadin/ui/AbstractMedia.java
index 9117bce997..09cfd5ff12 100644
--- a/src/com/vaadin/ui/AbstractMedia.java
+++ b/src/com/vaadin/ui/AbstractMedia.java
@@ -8,18 +8,22 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VMediaBase;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector;
+import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector.MediaControl;
/**
* Abstract base class for the HTML5 media components.
*
* @author Vaadin Ltd
*/
-public class AbstractMedia extends AbstractComponent {
+public class AbstractMedia extends AbstractComponent implements
+ Vaadin6Component {
private List<Resource> sources = new ArrayList<Resource>();
@@ -33,10 +37,6 @@ public class AbstractMedia extends AbstractComponent {
private boolean muted;
- private boolean pause;
-
- private boolean play;
-
/**
* Sets a single media file as the source of the media component.
*
@@ -182,47 +182,35 @@ public class AbstractMedia extends AbstractComponent {
* Pauses the media.
*/
public void pause() {
- // cancel any possible play command
- play = false;
-
- pause = true;
- requestRepaint();
+ getRpcProxy(MediaControl.class).pause();
}
/**
* Starts playback of the media.
*/
public void play() {
- // cancel any possible pause command.
- pause = false;
-
- play = true;
- requestRepaint();
+ getRpcProxy(MediaControl.class).play();
}
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
- target.addAttribute(VMediaBase.ATTR_CONTROLS, isShowControls());
+ target.addAttribute(MediaBaseConnector.ATTR_CONTROLS, isShowControls());
if (getAltText() != null) {
- target.addAttribute(VMediaBase.ATTR_ALT_TEXT, getAltText());
+ target.addAttribute(MediaBaseConnector.ATTR_ALT_TEXT, getAltText());
}
- target.addAttribute(VMediaBase.ATTR_HTML, isHtmlContentAllowed());
- target.addAttribute(VMediaBase.ATTR_AUTOPLAY, isAutoplay());
+ target.addAttribute(MediaBaseConnector.ATTR_HTML,
+ isHtmlContentAllowed());
+ target.addAttribute(MediaBaseConnector.ATTR_AUTOPLAY, isAutoplay());
for (Resource r : getSources()) {
- target.startTag(VMediaBase.TAG_SOURCE);
- target.addAttribute(VMediaBase.ATTR_RESOURCE, r);
- target.addAttribute(VMediaBase.ATTR_RESOURCE_TYPE, r.getMIMEType());
- target.endTag(VMediaBase.TAG_SOURCE);
- }
- target.addAttribute(VMediaBase.ATTR_MUTED, isMuted());
- if (play) {
- target.addAttribute(VMediaBase.ATTR_PLAY, true);
- play = false;
- }
- if (pause) {
- target.addAttribute(VMediaBase.ATTR_PAUSE, true);
- pause = false;
+ target.startTag(MediaBaseConnector.TAG_SOURCE);
+ target.addAttribute(MediaBaseConnector.ATTR_RESOURCE, r);
+ target.addAttribute(MediaBaseConnector.ATTR_RESOURCE_TYPE,
+ r.getMIMEType());
+ target.endTag(MediaBaseConnector.TAG_SOURCE);
}
+ target.addAttribute(MediaBaseConnector.ATTR_MUTED, isMuted());
+ }
+
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
}
}
diff --git a/src/com/vaadin/ui/AbstractOrderedLayout.java b/src/com/vaadin/ui/AbstractOrderedLayout.java
index fc3ef5056d..3606fa6572 100644
--- a/src/com/vaadin/ui/AbstractOrderedLayout.java
+++ b/src/com/vaadin/ui/AbstractOrderedLayout.java
@@ -15,13 +15,26 @@ import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Sizeable;
-import com.vaadin.terminal.gwt.client.EventId;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutState;
@SuppressWarnings("serial")
public abstract class AbstractOrderedLayout extends AbstractLayout implements
- Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier {
+ Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier,
+ Vaadin6Component {
- private static final String CLICK_EVENT = EventId.LAYOUT_CLICK;
+ private AbstractOrderedLayoutServerRpc rpc = new AbstractOrderedLayoutServerRpc() {
+
+ public void layoutClick(MouseEventDetails mouseDetails,
+ Connector clickedConnector) {
+ fireEvent(LayoutClickEvent.createEvent(AbstractOrderedLayout.this,
+ mouseDetails, clickedConnector));
+ }
+ };
public static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT;
@@ -39,10 +52,14 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
private final Map<Component, Float> componentToExpandRatio = new HashMap<Component, Float>();
- /**
- * Is spacing between contained components enabled. Defaults to false.
- */
- private boolean spacing = false;
+ public AbstractOrderedLayout() {
+ registerRpc(rpc);
+ }
+
+ @Override
+ public AbstractOrderedLayoutState getState() {
+ return (AbstractOrderedLayoutState) super.getState();
+ }
/**
* Add a component into this container. The component is added to the right
@@ -54,7 +71,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
@Override
public void addComponent(Component c) {
// Add to components before calling super.addComponent
- // so that it is available to AttachListeners
+ // so that it is available to AttachListeners
components.add(c);
try {
super.addComponent(c);
@@ -75,7 +92,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
public void addComponentAsFirst(Component c) {
// If c is already in this, we must remove it before proceeding
// see ticket #7668
- if(c.getParent() == this) {
+ if (c.getParent() == this) {
removeComponent(c);
}
components.addFirst(c);
@@ -100,9 +117,9 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
public void addComponent(Component c, int index) {
// If c is already in this, we must remove it before proceeding
// see ticket #7668
- if(c.getParent() == this) {
+ if (c.getParent() == this) {
// When c is removed, all components after it are shifted down
- if(index > getComponentIndex(c)) {
+ if (index > getComponentIndex(c)) {
index--;
}
removeComponent(c);
@@ -160,26 +177,16 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- // Add spacing attribute (omitted if false)
- if (spacing) {
- target.addAttribute("spacing", spacing);
- }
-
- // Adds all items in all the locations
- for (Component c : components) {
- // Paint child component UIDL
- c.paint(target);
- }
-
// Add child component alignment info to layout tag
target.addAttribute("alignments", componentToAlignment);
target.addAttribute("expandRatios", componentToExpandRatio);
}
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
+
/* Documented in superclass */
public void replaceComponent(Component oldComponent, Component newComponent) {
@@ -275,8 +282,8 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
*
* @see com.vaadin.ui.Layout.SpacingHandler#setSpacing(boolean)
*/
- public void setSpacing(boolean enabled) {
- spacing = enabled;
+ public void setSpacing(boolean spacing) {
+ getState().setSpacing(spacing);
requestRepaint();
}
@@ -285,18 +292,8 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
*
* @see com.vaadin.ui.Layout.SpacingHandler#isSpacing()
*/
- @Deprecated
- public boolean isSpacingEnabled() {
- return spacing;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.Layout.SpacingHandler#isSpacing()
- */
public boolean isSpacing() {
- return spacing;
+ return getState().isSpacing();
}
/**
@@ -350,29 +347,15 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
return (ratio == null) ? 0 : ratio.floatValue();
}
- /**
- * Sets the component alignment using a short hand string notation.
- *
- * @deprecated Replaced by
- * {@link #setComponentAlignment(Component, Alignment)}
- *
- * @param component
- * A child component in this layout
- * @param alignment
- * A short hand notation described in {@link AlignmentUtils}
- */
- @Deprecated
- public void setComponentAlignment(Component component, String alignment) {
- AlignmentUtils.setComponentAlignment(this, component, alignment);
- }
-
public void addListener(LayoutClickListener listener) {
- addListener(CLICK_EVENT, LayoutClickEvent.class, listener,
+ addListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener,
LayoutClickListener.clickMethod);
}
public void removeListener(LayoutClickListener listener) {
- removeListener(CLICK_EVENT, LayoutClickEvent.class, listener);
+ removeListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener);
}
/**
diff --git a/src/com/vaadin/ui/AbstractSelect.java b/src/com/vaadin/ui/AbstractSelect.java
index bb49626741..e586810b2d 100644
--- a/src/com/vaadin/ui/AbstractSelect.java
+++ b/src/com/vaadin/ui/AbstractSelect.java
@@ -32,9 +32,11 @@ import com.vaadin.terminal.KeyMapper;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.client.ui.dd.VIsOverId;
import com.vaadin.terminal.gwt.client.ui.dd.VItemIdIs;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.ui.AbstractSelect.ItemCaptionMode;
/**
* <p>
@@ -55,46 +57,91 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
* @since 5.0
*/
@SuppressWarnings("serial")
-public abstract class AbstractSelect extends AbstractField implements
+// TODO currently cannot specify type more precisely in case of multi-select
+public abstract class AbstractSelect extends AbstractField<Object> implements
Container, Container.Viewer, Container.PropertySetChangeListener,
Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier,
- Container.ItemSetChangeListener {
+ Container.ItemSetChangeListener, Vaadin6Component {
+
+ public enum ItemCaptionMode {
+ /**
+ * Item caption mode: Item's ID's <code>String</code> representation is
+ * used as caption.
+ */
+ ID,
+ /**
+ * Item caption mode: Item's <code>String</code> representation is used
+ * as caption.
+ */
+ ITEM,
+ /**
+ * Item caption mode: Index of the item is used as caption. The index
+ * mode can only be used with the containers implementing the
+ * {@link com.vaadin.data.Container.Indexed} interface.
+ */
+ INDEX,
+ /**
+ * Item caption mode: If an Item has a caption it's used, if not, Item's
+ * ID's <code>String</code> representation is used as caption. <b>This
+ * is the default</b>.
+ */
+ EXPLICIT_DEFAULTS_ID,
+ /**
+ * Item caption mode: Captions must be explicitly specified.
+ */
+ EXPLICIT,
+ /**
+ * Item caption mode: Only icons are shown, captions are hidden.
+ */
+ ICON_ONLY,
+ /**
+ * Item caption mode: Item captions are read from property specified
+ * with <code>setItemCaptionPropertyId</code>.
+ */
+ PROPERTY;
+ }
/**
- * Item caption mode: Item's ID's <code>String</code> representation is used
- * as caption.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_ID = 0;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_ID = ItemCaptionMode.ID;
+
/**
- * Item caption mode: Item's <code>String</code> representation is used as
- * caption.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_ITEM = 1;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_ITEM = ItemCaptionMode.ITEM;
+
/**
- * Item caption mode: Index of the item is used as caption. The index mode
- * can only be used with the containers implementing the
- * {@link com.vaadin.data.Container.Indexed} interface.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_INDEX = 2;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_INDEX = ItemCaptionMode.INDEX;
+
/**
- * Item caption mode: If an Item has a caption it's used, if not, Item's
- * ID's <code>String</code> representation is used as caption. <b>This is
- * the default</b>.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = 3;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
+
/**
- * Item caption mode: Captions must be explicitly specified.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_EXPLICIT = 4;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT = ItemCaptionMode.EXPLICIT;
+
/**
- * Item caption mode: Only icons are shown, captions are hidden.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_ICON_ONLY = 5;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_ICON_ONLY = ItemCaptionMode.ICON_ONLY;
+
/**
- * Item caption mode: Item captions are read from property specified with
- * <code>setItemCaptionPropertyId</code>.
+ * @deprecated from 7.0, use {@link ItemCaptionMode.ID} instead
*/
- public static final int ITEM_CAPTION_MODE_PROPERTY = 6;
+ @Deprecated
+ public static final ItemCaptionMode ITEM_CAPTION_MODE_PROPERTY = ItemCaptionMode.PROPERTY;
/**
* Interface for option filtering, used to filter options based on user
@@ -159,7 +206,7 @@ public abstract class AbstractSelect extends AbstractField implements
/**
* Keymapper used to map key values.
*/
- protected KeyMapper itemIdMapper = new KeyMapper();
+ protected KeyMapper<Object> itemIdMapper = new KeyMapper<Object>();
/**
* Item icons.
@@ -174,7 +221,7 @@ public abstract class AbstractSelect extends AbstractField implements
/**
* Item caption mode.
*/
- private int itemCaptionMode = ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
+ private ItemCaptionMode itemCaptionMode = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
/**
* Item caption source property id.
@@ -276,12 +323,8 @@ public abstract class AbstractSelect extends AbstractField implements
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- // Paints field properties
- super.paintContent(target);
-
// Paints select attributes
if (isMultiSelect()) {
target.addAttribute("selectmode", "multi");
@@ -382,9 +425,7 @@ public abstract class AbstractSelect extends AbstractField implements
* @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
* java.util.Map)
*/
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
// New option entered (and it is allowed)
if (isNewItemsAllowed()) {
@@ -515,16 +556,9 @@ public abstract class AbstractSelect extends AbstractField implements
// Sets the caption property, if used
if (getItemCaptionPropertyId() != null) {
- try {
- getContainerProperty(newItemCaption,
- getItemCaptionPropertyId()).setValue(
- newItemCaption);
- } catch (final Property.ConversionException ignored) {
- /*
- * The conversion exception is safely ignored, the
- * caption is just missing
- */
- }
+ getContainerProperty(newItemCaption,
+ getItemCaptionPropertyId())
+ .setValue(newItemCaption);
}
if (isMultiSelect()) {
Set values = new HashSet((Collection) getValue());
@@ -543,10 +577,7 @@ public abstract class AbstractSelect extends AbstractField implements
* to the terminal or null if no items is visible.
*/
public Collection<?> getVisibleItemIds() {
- if (isVisible()) {
- return getItemIds();
- }
- return null;
+ return getItemIds();
}
/* Property methods */
@@ -614,8 +645,7 @@ public abstract class AbstractSelect extends AbstractField implements
* @see com.vaadin.ui.AbstractField#setValue(java.lang.Object)
*/
@Override
- public void setValue(Object newValue) throws Property.ReadOnlyException,
- Property.ConversionException {
+ public void setValue(Object newValue) throws Property.ReadOnlyException {
if (newValue == getNullSelectionItemId()) {
newValue = null;
}
@@ -641,7 +671,7 @@ public abstract class AbstractSelect extends AbstractField implements
*/
@Override
protected void setValue(Object newValue, boolean repaintIsNotNeeded)
- throws Property.ReadOnlyException, Property.ConversionException {
+ throws Property.ReadOnlyException {
if (isMultiSelect()) {
if (newValue == null) {
@@ -729,7 +759,7 @@ public abstract class AbstractSelect extends AbstractField implements
*
* @see com.vaadin.data.Container#getContainerProperty(Object, Object)
*/
- public Property getContainerProperty(Object itemId, Object propertyId) {
+ public Property<?> getContainerProperty(Object itemId, Object propertyId) {
return items.getContainerProperty(itemId, propertyId);
}
@@ -939,10 +969,13 @@ public abstract class AbstractSelect extends AbstractField implements
}
/**
- * Sets the multiselect mode. Setting multiselect mode false may loose
+ * Sets the multiselect mode. Setting multiselect mode false may lose
* selection information: if selected items set contains one or more
* selected items, only one of the selected items is kept as selected.
*
+ * Subclasses of AbstractSelect can choose not to support changing the
+ * multiselect mode, and may throw {@link UnsupportedOperationException}.
+ *
* @param multiSelect
* the New value of property multiSelect.
*/
@@ -1045,11 +1078,11 @@ public abstract class AbstractSelect extends AbstractField implements
switch (getItemCaptionMode()) {
- case ITEM_CAPTION_MODE_ID:
+ case ID:
caption = itemId.toString();
break;
- case ITEM_CAPTION_MODE_INDEX:
+ case INDEX:
if (items instanceof Container.Indexed) {
caption = String.valueOf(((Container.Indexed) items)
.indexOfId(itemId));
@@ -1058,29 +1091,32 @@ public abstract class AbstractSelect extends AbstractField implements
}
break;
- case ITEM_CAPTION_MODE_ITEM:
+ case ITEM:
final Item i = getItem(itemId);
if (i != null) {
caption = i.toString();
}
break;
- case ITEM_CAPTION_MODE_EXPLICIT:
+ case EXPLICIT:
caption = itemCaptions.get(itemId);
break;
- case ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID:
+ case EXPLICIT_DEFAULTS_ID:
caption = itemCaptions.get(itemId);
if (caption == null) {
caption = itemId.toString();
}
break;
- case ITEM_CAPTION_MODE_PROPERTY:
- final Property p = getContainerProperty(itemId,
+ case PROPERTY:
+ final Property<?> p = getContainerProperty(itemId,
getItemCaptionPropertyId());
if (p != null) {
- caption = p.toString();
+ Object value = p.getValue();
+ if (value != null) {
+ caption = value.toString();
+ }
}
break;
}
@@ -1090,7 +1126,7 @@ public abstract class AbstractSelect extends AbstractField implements
}
/**
- * Sets the icon for an item.
+ * Sets tqhe icon for an item.
*
* @param itemId
* the id of the item to be assigned an icon.
@@ -1125,7 +1161,7 @@ public abstract class AbstractSelect extends AbstractField implements
return null;
}
- final Property ip = getContainerProperty(itemId,
+ final Property<?> ip = getContainerProperty(itemId,
getItemIconPropertyId());
if (ip == null) {
return null;
@@ -1167,8 +1203,8 @@ public abstract class AbstractSelect extends AbstractField implements
* @param mode
* the One of the modes listed above.
*/
- public void setItemCaptionMode(int mode) {
- if (ITEM_CAPTION_MODE_ID <= mode && mode <= ITEM_CAPTION_MODE_PROPERTY) {
+ public void setItemCaptionMode(ItemCaptionMode mode) {
+ if (mode != null) {
itemCaptionMode = mode;
requestRepaint();
}
@@ -1202,7 +1238,7 @@ public abstract class AbstractSelect extends AbstractField implements
*
* @return the One of the modes listed above.
*/
- public int getItemCaptionMode() {
+ public ItemCaptionMode getItemCaptionMode() {
return itemCaptionMode;
}
@@ -1216,7 +1252,9 @@ public abstract class AbstractSelect extends AbstractField implements
* null resets the item caption mode to
* <code>ITEM_CAPTION_EXPLICIT_DEFAULTS_ID</code>.
* </p>
- *
+ * <p>
+ * Note that the type of the property used for caption must be String
+ * </p>
* <p>
* Setting the property id to null disables this feature. The id is null by
* default
@@ -1691,7 +1729,7 @@ public abstract class AbstractSelect extends AbstractField implements
public void addNotifierForItem(Object itemId) {
switch (getItemCaptionMode()) {
- case ITEM_CAPTION_MODE_ITEM:
+ case ITEM:
final Item i = getItem(itemId);
if (i == null) {
return;
@@ -1704,7 +1742,7 @@ public abstract class AbstractSelect extends AbstractField implements
Collection<?> pids = i.getItemPropertyIds();
if (pids != null) {
for (Iterator<?> it = pids.iterator(); it.hasNext();) {
- Property p = i.getItemProperty(it.next());
+ Property<?> p = i.getItemProperty(it.next());
if (p != null
&& p instanceof Property.ValueChangeNotifier) {
((Property.ValueChangeNotifier) p)
@@ -1715,8 +1753,8 @@ public abstract class AbstractSelect extends AbstractField implements
}
break;
- case ITEM_CAPTION_MODE_PROPERTY:
- final Property p = getContainerProperty(itemId,
+ case PROPERTY:
+ final Property<?> p = getContainerProperty(itemId,
getItemCaptionPropertyId());
if (p != null && p instanceof Property.ValueChangeNotifier) {
((Property.ValueChangeNotifier) p)
diff --git a/src/com/vaadin/ui/AbstractSplitPanel.java b/src/com/vaadin/ui/AbstractSplitPanel.java
index b507b88478..5205952621 100644
--- a/src/com/vaadin/ui/AbstractSplitPanel.java
+++ b/src/com/vaadin/ui/AbstractSplitPanel.java
@@ -7,15 +7,15 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Iterator;
-import java.util.Map;
import com.vaadin.event.ComponentEventListener;
import com.vaadin.event.MouseEvents.ClickEvent;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanel;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelRpc;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelState;
+import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelState.SplitterState;
import com.vaadin.tools.ReflectTools;
/**
@@ -29,21 +29,26 @@ import com.vaadin.tools.ReflectTools;
* @VERSION@
* @since 6.5
*/
-public abstract class AbstractSplitPanel extends AbstractLayout {
+public abstract class AbstractSplitPanel extends AbstractComponentContainer {
- private Component firstComponent;
+ private Unit posUnit;
- private Component secondComponent;
+ private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() {
- private float pos = 50;
-
- private int posUnit = UNITS_PERCENTAGE;
-
- private boolean posReversed = false;
+ public void splitterClick(MouseEventDetails mouseDetails) {
+ fireEvent(new SplitterClickEvent(AbstractSplitPanel.this,
+ mouseDetails));
+ }
- private boolean locked = false;
+ public void setSplitterPosition(float position) {
+ getState().getSplitterState().setPosition(position);
+ }
+ };
- private static final String SPLITTER_CLICK_EVENT = VSplitPanel.SPLITTER_CLICK_EVENT_IDENTIFIER;
+ public AbstractSplitPanel() {
+ registerRpc(rpc);
+ setSplitPosition(50, Unit.PERCENTAGE, false);
+ }
/**
* Modifiable and Serializable Iterator for the components, used by
@@ -67,17 +72,17 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
}
i++;
if (i == 1) {
- return firstComponent == null ? secondComponent
- : firstComponent;
+ return (getFirstComponent() == null ? getSecondComponent()
+ : getFirstComponent());
} else if (i == 2) {
- return secondComponent;
+ return getSecondComponent();
}
return null;
}
public void remove() {
if (i == 1) {
- if (firstComponent != null) {
+ if (getFirstComponent() != null) {
setFirstComponent(null);
i = 0;
} else {
@@ -98,60 +103,83 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
*/
@Override
public void addComponent(Component c) {
- if (firstComponent == null) {
- firstComponent = c;
- } else if (secondComponent == null) {
- secondComponent = c;
+ if (getFirstComponent() == null) {
+ setFirstComponent(c);
+ } else if (getSecondComponent() == null) {
+ setSecondComponent(c);
} else {
throw new UnsupportedOperationException(
"Split panel can contain only two components");
}
- super.addComponent(c);
- requestRepaint();
}
+ /**
+ * Sets the first component of this split panel. Depending on the direction
+ * the first component is shown at the top or to the left.
+ *
+ * @param c
+ * The component to use as first component
+ */
public void setFirstComponent(Component c) {
- if (firstComponent == c) {
+ if (getFirstComponent() == c) {
// Nothing to do
return;
}
- if (firstComponent != null) {
+ if (getFirstComponent() != null) {
// detach old
- removeComponent(firstComponent);
+ removeComponent(getFirstComponent());
}
- firstComponent = c;
- super.addComponent(c);
+ getState().setFirstChild(c);
+ if (c != null) {
+ super.addComponent(c);
+ }
+
requestRepaint();
}
+ /**
+ * Sets the second component of this split panel. Depending on the direction
+ * the second component is shown at the bottom or to the left.
+ *
+ * @param c
+ * The component to use as first component
+ */
public void setSecondComponent(Component c) {
- if (c == secondComponent) {
+ if (getSecondComponent() == c) {
// Nothing to do
return;
}
- if (secondComponent != null) {
+ if (getSecondComponent() != null) {
// detach old
- removeComponent(secondComponent);
+ removeComponent(getSecondComponent());
+ }
+ getState().setSecondChild(c);
+ if (c != null) {
+ super.addComponent(c);
}
- secondComponent = c;
- super.addComponent(c);
requestRepaint();
}
/**
- * @return the first component of this SplitPanel.
+ * Gets the first component of this split panel. Depending on the direction
+ * this is either the component shown at the top or to the left.
+ *
+ * @return the first component of this split panel
*/
public Component getFirstComponent() {
- return firstComponent;
+ return (Component) getState().getFirstChild();
}
/**
- * @return the second component of this SplitPanel.
+ * Gets the second component of this split panel. Depending on the direction
+ * this is either the component shown at the top or to the left.
+ *
+ * @return the second component of this split panel
*/
public Component getSecondComponent() {
- return secondComponent;
+ return (Component) getState().getSecondChild();
}
/**
@@ -163,10 +191,10 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
@Override
public void removeComponent(Component c) {
super.removeComponent(c);
- if (c == firstComponent) {
- firstComponent = null;
- } else if (c == secondComponent) {
- secondComponent = null;
+ if (c == getFirstComponent()) {
+ getState().setFirstChild(null);
+ } else if (c == getSecondComponent()) {
+ getState().setSecondChild(null);
}
requestRepaint();
}
@@ -188,58 +216,20 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
*/
public int getComponentCount() {
int count = 0;
- if (firstComponent != null) {
+ if (getFirstComponent() != null) {
count++;
}
- if (secondComponent != null) {
+ if (getSecondComponent() != null) {
count++;
}
return count;
}
- /**
- * Paints the content of this component.
- *
- * @param target
- * the Paint Event.
- * @throws PaintException
- * if the paint operation failed.
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- final String position = pos + UNIT_SYMBOLS[posUnit];
-
- target.addAttribute("position", position);
-
- if (isLocked()) {
- target.addAttribute("locked", true);
- }
-
- target.addAttribute("reversed", posReversed);
-
- if (firstComponent != null) {
- firstComponent.paint(target);
- } else {
- VerticalLayout temporaryComponent = new VerticalLayout();
- temporaryComponent.setParent(this);
- temporaryComponent.paint(target);
- }
- if (secondComponent != null) {
- secondComponent.paint(target);
- } else {
- VerticalLayout temporaryComponent = new VerticalLayout();
- temporaryComponent.setParent(this);
- temporaryComponent.paint(target);
- }
- }
-
/* Documented in superclass */
public void replaceComponent(Component oldComponent, Component newComponent) {
- if (oldComponent == firstComponent) {
+ if (oldComponent == getFirstComponent()) {
setFirstComponent(newComponent);
- } else if (oldComponent == secondComponent) {
+ } else if (oldComponent == getSecondComponent()) {
setSecondComponent(newComponent);
}
requestRepaint();
@@ -254,7 +244,7 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* unit is percentage.
*/
public void setSplitPosition(float pos) {
- setSplitPosition(pos, posUnit, true, false);
+ setSplitPosition(pos, posUnit, false);
}
/**
@@ -270,7 +260,7 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* second region else it is measured by the first region
*/
public void setSplitPosition(float pos, boolean reverse) {
- setSplitPosition(pos, posUnit, true, reverse);
+ setSplitPosition(pos, posUnit, reverse);
}
/**
@@ -282,8 +272,8 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* @param unit
* the unit (from {@link Sizeable}) in which the size is given.
*/
- public void setSplitPosition(float pos, int unit) {
- setSplitPosition(pos, unit, true, false);
+ public void setSplitPosition(float pos, Unit unit) {
+ setSplitPosition(pos, unit, false);
}
/**
@@ -299,8 +289,21 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* second region else it is measured by the first region
*
*/
- public void setSplitPosition(float pos, int unit, boolean reverse) {
- setSplitPosition(pos, unit, true, reverse);
+ public void setSplitPosition(float pos, Unit unit, boolean reverse) {
+ if (unit != Unit.PERCENTAGE && unit != Unit.PIXELS) {
+ throw new IllegalArgumentException(
+ "Only percentage and pixel units are allowed");
+ }
+ if (unit != Unit.PERCENTAGE) {
+ pos = Math.round(pos);
+ }
+ SplitterState splitterState = getState().getSplitterState();
+ splitterState.setPosition(pos);
+ splitterState.setPositionUnit(unit.getSymbol());
+ splitterState.setPositionReversed(reverse);
+ posUnit = unit;
+
+ requestRepaint();
}
/**
@@ -310,7 +313,7 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* @return position of the splitter
*/
public float getSplitPosition() {
- return pos;
+ return getState().getSplitterState().getPosition();
}
/**
@@ -318,41 +321,11 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
*
* @return unit of position of the splitter
*/
- public int getSplitPositionUnit() {
+ public Unit getSplitPositionUnit() {
return posUnit;
}
/**
- * Moves the position of the splitter.
- *
- * @param pos
- * the new size of the first region. Fractions are only allowed
- * when unit is percentage.
- * @param unit
- * the unit (from {@link Sizeable}) in which the size is given.
- * @param repaintNotNeeded
- * true if client side needs to be updated. Use false if the
- * position info has come from the client side, thus it already
- * knows the position.
- */
- private void setSplitPosition(float pos, int unit, boolean repaintNeeded,
- boolean reverse) {
- if (unit != UNITS_PERCENTAGE && unit != UNITS_PIXELS) {
- throw new IllegalArgumentException(
- "Only percentage and pixel units are allowed");
- }
- if (unit != UNITS_PERCENTAGE) {
- pos = Math.round(pos);
- }
- this.pos = pos;
- posUnit = unit;
- posReversed = reverse;
- if (repaintNeeded) {
- requestRepaint();
- }
- }
-
- /**
* Lock the SplitPanels position, disabling the user from dragging the split
* handle.
*
@@ -360,7 +333,7 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* Set <code>true</code> if locked, <code>false</code> otherwise.
*/
public void setLocked(boolean locked) {
- this.locked = locked;
+ getState().getSplitterState().setLocked(locked);
requestRepaint();
}
@@ -371,37 +344,7 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
* @return <code>true</code> if locked, <code>false</code> otherwise.
*/
public boolean isLocked() {
- return locked;
- }
-
- /*
- * Invoked when a variable of the component changes. Don't add a JavaDoc
- * comment here, we use the default documentation from implemented
- * interface.
- */
- @SuppressWarnings("unchecked")
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
-
- super.changeVariables(source, variables);
-
- if (variables.containsKey("position") && !isLocked()) {
- Float newPos = (Float) variables.get("position");
- setSplitPosition(newPos, posUnit, posReversed);
- }
-
- if (variables.containsKey(SPLITTER_CLICK_EVENT)) {
- fireClick((Map<String, Object>) variables.get(SPLITTER_CLICK_EVENT));
- }
-
- }
-
- @Override
- protected void fireClick(Map<String, Object> parameters) {
- MouseEventDetails mouseDetails = MouseEventDetails
- .deSerialize((String) parameters.get("mouseDetails"));
-
- fireEvent(new SplitterClickEvent(this, mouseDetails));
+ return getState().getSplitterState().isLocked();
}
/**
@@ -436,12 +379,19 @@ public abstract class AbstractSplitPanel extends AbstractLayout {
}
public void addListener(SplitterClickListener listener) {
- addListener(SPLITTER_CLICK_EVENT, SplitterClickEvent.class, listener,
+ addListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER,
+ SplitterClickEvent.class, listener,
SplitterClickListener.clickMethod);
}
public void removeListener(SplitterClickListener listener) {
- removeListener(SPLITTER_CLICK_EVENT, SplitterClickEvent.class, listener);
+ removeListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER,
+ SplitterClickEvent.class, listener);
+ }
+
+ @Override
+ public AbstractSplitPanelState getState() {
+ return (AbstractSplitPanelState) super.getState();
}
}
diff --git a/src/com/vaadin/ui/AbstractTextField.java b/src/com/vaadin/ui/AbstractTextField.java
index 346d370bd5..acb1d71ed8 100644
--- a/src/com/vaadin/ui/AbstractTextField.java
+++ b/src/com/vaadin/ui/AbstractTextField.java
@@ -18,10 +18,11 @@ import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.event.FieldEvents.TextChangeNotifier;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VTextField;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
-public abstract class AbstractTextField extends AbstractField implements
- BlurNotifier, FocusNotifier, TextChangeNotifier {
+public abstract class AbstractTextField extends AbstractField<String> implements
+ BlurNotifier, FocusNotifier, TextChangeNotifier, Vaadin6Component {
/**
* Value formatter used to format the string contents.
@@ -99,9 +100,7 @@ public abstract class AbstractTextField extends AbstractField implements
super();
}
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
if (getMaxLength() >= 0) {
target.addAttribute("maxLength", getMaxLength());
@@ -173,8 +172,8 @@ public abstract class AbstractTextField extends AbstractField implements
}
@Override
- public Object getValue() {
- Object v = super.getValue();
+ public String getValue() {
+ String v = super.getValue();
if (format == null || v == null) {
return v;
}
@@ -185,12 +184,10 @@ public abstract class AbstractTextField extends AbstractField implements
}
}
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
changingVariables = true;
try {
- super.changeVariables(source, variables);
if (variables.containsKey(VTextField.VAR_CURSOR)) {
Integer object = (Integer) variables.get(VTextField.VAR_CURSOR);
@@ -252,7 +249,7 @@ public abstract class AbstractTextField extends AbstractField implements
}
@Override
- public Class getType() {
+ public Class<String> getType() {
return String.class;
}
@@ -375,7 +372,7 @@ public abstract class AbstractTextField extends AbstractField implements
@Override
protected boolean isEmpty() {
- return super.isEmpty() || toString().length() == 0;
+ return super.isEmpty() || getValue().length() == 0;
}
/**
@@ -463,7 +460,7 @@ public abstract class AbstractTextField extends AbstractField implements
}
@Override
- protected void setInternalValue(Object newValue) {
+ protected void setInternalValue(String newValue) {
if (changingVariables && !textChangeEventPending) {
/*
@@ -505,8 +502,7 @@ public abstract class AbstractTextField extends AbstractField implements
}
@Override
- public void setValue(Object newValue) throws ReadOnlyException,
- ConversionException {
+ public void setValue(Object newValue) throws ReadOnlyException {
super.setValue(newValue);
/*
* Make sure w reset lastKnownTextContent field on value change. The
@@ -515,7 +511,7 @@ public abstract class AbstractTextField extends AbstractField implements
* case. AbstractField optimizes value change if the existing value is
* reset. Also we need to force repaint if the flag is on.
*/
- if(lastKnownTextContent != null) {
+ if (lastKnownTextContent != null) {
lastKnownTextContent = null;
requestRepaint();
}
@@ -753,4 +749,4 @@ public abstract class AbstractTextField extends AbstractField implements
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
}
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/ui/Accordion.java b/src/com/vaadin/ui/Accordion.java
index 5cf805615c..b937c7bc2b 100644
--- a/src/com/vaadin/ui/Accordion.java
+++ b/src/com/vaadin/ui/Accordion.java
@@ -3,8 +3,6 @@
*/
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VAccordion;
-
/**
* An accordion is a component similar to a {@link TabSheet}, but with a
* vertical orientation and the selected component presented between tabs.
@@ -16,8 +14,6 @@ import com.vaadin.terminal.gwt.client.ui.VAccordion;
*
* @see TabSheet
*/
-@SuppressWarnings("serial")
-@ClientWidget(VAccordion.class)
public class Accordion extends TabSheet {
}
diff --git a/src/com/vaadin/ui/AlignmentUtils.java b/src/com/vaadin/ui/AlignmentUtils.java
deleted file mode 100644
index 029fc8c9b5..0000000000
--- a/src/com/vaadin/ui/AlignmentUtils.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.vaadin.ui.Layout.AlignmentHandler;
-
-/**
- * Helper class for setting alignments using a short notation.
- *
- * Supported notation is:
- *
- * t,top for top alignment
- *
- * m,middle for vertical center alignment
- *
- * b,bottom for bottom alignment
- *
- * l,left for left alignment
- *
- * c,center for horizontal center alignment
- *
- * r,right for right alignment
- *
- * @deprecated {@code AlignmentUtils} has been replaced by {@link Alignment}.
- */
-@SuppressWarnings({ "serial" })
-@Deprecated
-public class AlignmentUtils implements Serializable {
-
- private static int horizontalMask = AlignmentHandler.ALIGNMENT_LEFT
- | AlignmentHandler.ALIGNMENT_HORIZONTAL_CENTER
- | AlignmentHandler.ALIGNMENT_RIGHT;
-
- private static int verticalMask = AlignmentHandler.ALIGNMENT_TOP
- | AlignmentHandler.ALIGNMENT_VERTICAL_CENTER
- | AlignmentHandler.ALIGNMENT_BOTTOM;
-
- private static Map<String, Integer> alignmentStrings = new HashMap<String, Integer>();
-
- private static void addMapping(int alignment, String... values) {
- for (String s : values) {
- alignmentStrings.put(s, alignment);
- }
- }
-
- static {
- addMapping(AlignmentHandler.ALIGNMENT_TOP, "t", "top");
- addMapping(AlignmentHandler.ALIGNMENT_BOTTOM, "b", "bottom");
- addMapping(AlignmentHandler.ALIGNMENT_VERTICAL_CENTER, "m", "middle");
-
- addMapping(AlignmentHandler.ALIGNMENT_LEFT, "l", "left");
- addMapping(AlignmentHandler.ALIGNMENT_RIGHT, "r", "right");
- addMapping(AlignmentHandler.ALIGNMENT_HORIZONTAL_CENTER, "c", "center");
- }
-
- /**
- * Set the alignment for the component using short notation
- *
- * @param parent
- * @param component
- * @param alignment
- * String containing one or two alignment strings. If short
- * notation "r","t",etc is used valid strings include
- * "r","rt","tr","t". If the longer notation is used the
- * alignments should be separated by a space e.g.
- * "right","right top","top right","top". It is valid to mix
- * short and long notation but they must be separated by a space
- * e.g. "r top".
- * @throws IllegalArgumentException
- */
- public static void setComponentAlignment(AlignmentHandler parent,
- Component component, String alignment)
- throws IllegalArgumentException {
- if (alignment == null || alignment.length() == 0) {
- throw new IllegalArgumentException(
- "alignment for setComponentAlignment() cannot be null or empty");
- }
-
- Integer currentAlignment = parent.getComponentAlignment(component)
- .getBitMask();
-
- if (alignment.length() == 1) {
- // Use short form "t","l",...
- currentAlignment = parseAlignment(alignment.substring(0, 1),
- currentAlignment);
- } else if (alignment.length() == 2) {
- // Use short form "tr","lb",...
- currentAlignment = parseAlignment(alignment.substring(0, 1),
- currentAlignment);
- currentAlignment = parseAlignment(alignment.substring(1, 2),
- currentAlignment);
- } else {
- // Alignments are separated by space
- String[] strings = alignment.split(" ");
- if (strings.length > 2) {
- throw new IllegalArgumentException(
- "alignment for setComponentAlignment() should not contain more than 2 alignments");
- }
- for (String alignmentString : strings) {
- currentAlignment = parseAlignment(alignmentString,
- currentAlignment);
- }
- }
-
- int horizontalAlignment = currentAlignment & horizontalMask;
- int verticalAlignment = currentAlignment & verticalMask;
- parent.setComponentAlignment(component, new Alignment(
- horizontalAlignment + verticalAlignment));
- }
-
- /**
- * Parse alignmentString which contains one alignment (horizontal or
- * vertical) and return and updated version of the passed alignment where
- * the alignment in one direction has been changed. If the passed
- * alignmentString is unknown an exception is thrown
- *
- * @param alignmentString
- * @param alignment
- * @return
- * @throws IllegalArgumentException
- */
- private static int parseAlignment(String alignmentString, int alignment)
- throws IllegalArgumentException {
- Integer parsed = alignmentStrings.get(alignmentString.toLowerCase());
-
- if (parsed == null) {
- throw new IllegalArgumentException(
- "Could not parse alignment string '" + alignmentString
- + "'");
- }
-
- if ((parsed & horizontalMask) != 0) {
- // Get the vertical alignment from the current alignment
- int vertical = (alignment & verticalMask);
- // Add the parsed horizontal alignment
- alignment = (vertical | parsed);
- } else {
- // Get the horizontal alignment from the current alignment
- int horizontal = (alignment & horizontalMask);
- // Add the parsed vertical alignment
- alignment = (horizontal | parsed);
- }
-
- return alignment;
- }
-}
diff --git a/src/com/vaadin/ui/Audio.java b/src/com/vaadin/ui/Audio.java
index 574c1f4186..ac2ee869a6 100644
--- a/src/com/vaadin/ui/Audio.java
+++ b/src/com/vaadin/ui/Audio.java
@@ -5,7 +5,6 @@
package com.vaadin.ui;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VAudio;
/**
* The Audio component translates into an HTML5 &lt;audio&gt; element and as
@@ -28,7 +27,6 @@ import com.vaadin.terminal.gwt.client.ui.VAudio;
* @author Vaadin Ltd
* @since 6.7.0
*/
-@ClientWidget(VAudio.class)
public class Audio extends AbstractMedia {
public Audio() {
diff --git a/src/com/vaadin/ui/BaseFieldFactory.java b/src/com/vaadin/ui/BaseFieldFactory.java
deleted file mode 100644
index fe271aabe4..0000000000
--- a/src/com/vaadin/ui/BaseFieldFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.ui;
-
-import com.vaadin.data.Container;
-import com.vaadin.data.Item;
-import com.vaadin.data.Property;
-
-/**
- * Default implementation of the the following Field types are used by default:
- * <p>
- * <b>Boolean</b>: Button(switchMode:true).<br/>
- * <b>Date</b>: DateField(resolution: day).<br/>
- * <b>Item</b>: Form. <br/>
- * <b>default field type</b>: TextField.
- * <p>
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.1
- * @deprecated use {@link DefaultFieldFactory} or own implementations on
- * {@link FormFieldFactory} or {@link TableFieldFactory} instead.
- */
-
-@Deprecated
-@SuppressWarnings("serial")
-public class BaseFieldFactory implements FieldFactory {
-
- /**
- * Creates the field based on type of data.
- *
- *
- * @param type
- * the type of data presented in field.
- * @param uiContext
- * the context where the Field is presented.
- *
- * @see com.vaadin.ui.FieldFactory#createField(Class, Component)
- */
- public Field createField(Class<?> type, Component uiContext) {
- return DefaultFieldFactory.createFieldByPropertyType(type);
- }
-
- /**
- * Creates the field based on the datasource property.
- *
- * @see com.vaadin.ui.FieldFactory#createField(Property, Component)
- */
- public Field createField(Property property, Component uiContext) {
- if (property != null) {
- return createField(property.getType(), uiContext);
- } else {
- return null;
- }
- }
-
- /**
- * Creates the field based on the item and property id.
- *
- * @see com.vaadin.ui.FieldFactory#createField(Item, Object, Component)
- */
- public Field createField(Item item, Object propertyId, Component uiContext) {
- if (item != null && propertyId != null) {
- final Field f = createField(item.getItemProperty(propertyId),
- uiContext);
- if (f instanceof AbstractComponent) {
- String name = DefaultFieldFactory
- .createCaptionByPropertyId(propertyId);
- f.setCaption(name);
- }
- return f;
- } else {
- return null;
- }
- }
-
- /**
- * @see com.vaadin.ui.FieldFactory#createField(com.vaadin.data.Container,
- * java.lang.Object, java.lang.Object, com.vaadin.ui.Component)
- */
- public Field createField(Container container, Object itemId,
- Object propertyId, Component uiContext) {
- return createField(container.getContainerProperty(itemId, propertyId),
- uiContext);
- }
-
-}
diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java
index 3c99784592..f5e45ef3ef 100644
--- a/src/com/vaadin/ui/Button.java
+++ b/src/com/vaadin/ui/Button.java
@@ -4,27 +4,25 @@
package com.vaadin.ui;
-import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
-import java.util.Map;
-import com.vaadin.data.Property;
+import com.vaadin.event.Action;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
+import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.event.ShortcutListener;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VButton;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-import com.vaadin.ui.themes.BaseTheme;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonServerRpc;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonState;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.Component.Focusable;
/**
* A generic button component.
@@ -35,29 +33,39 @@ import com.vaadin.ui.themes.BaseTheme;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VButton.class, loadStyle = LoadStyle.EAGER)
-public class Button extends AbstractField implements FieldEvents.BlurNotifier,
- FieldEvents.FocusNotifier {
+public class Button extends AbstractComponent implements
+ FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Focusable,
+ Action.ShortcutNotifier {
- /* Private members */
+ private ButtonServerRpc rpc = new ButtonServerRpc() {
+ public void click(MouseEventDetails mouseEventDetails) {
+ fireClick(mouseEventDetails);
+ }
- boolean switchMode = false;
+ public void disableOnClick() {
+ // Could be optimized so the button is not repainted because of
+ // this (client side has already disabled the button)
+ setEnabled(false);
+ }
+ };
- boolean disableOnClick = false;
+ FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) {
+ @Override
+ protected void fireEvent(Event event) {
+ Button.this.fireEvent(event);
+ }
+ };
/**
- * Creates a new push button. The value of the push button is false and it
- * is immediate by default.
- *
+ * Creates a new push button.
*/
public Button() {
- setValue(Boolean.FALSE);
+ registerRpc(rpc);
+ registerRpc(focusBlurRpc);
}
/**
- * Creates a new push button.
- *
- * The value of the push button is false and it is immediate by default.
+ * Creates a new push button with the given caption.
*
* @param caption
* the Button caption.
@@ -68,7 +76,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
}
/**
- * Creates a new push button with click listener.
+ * Creates a new push button with a click listener.
*
* @param caption
* the Button caption.
@@ -81,239 +89,6 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
}
/**
- * Creates a new push button with a method listening button clicks. Using
- * this method is discouraged because it cannot be checked during
- * compilation. Use
- * {@link #Button(String, com.vaadin.ui.Button.ClickListener)} instead. The
- * method must have either no parameters, or only one parameter of
- * Button.ClickEvent type.
- *
- * @param caption
- * the Button caption.
- * @param target
- * the Object having the method for listening button clicks.
- * @param methodName
- * the name of the method in target object, that receives button
- * click events.
- */
- public Button(String caption, Object target, String methodName) {
- this(caption);
- addListener(ClickEvent.class, target, methodName);
- }
-
- /**
- * Creates a new switch button with initial value.
- *
- * @param state
- * the Initial state of the switch-button.
- * @param initialState
- * @deprecated use {@link CheckBox} instead of Button in "switchmode"
- */
- @Deprecated
- public Button(String caption, boolean initialState) {
- setCaption(caption);
- setValue(Boolean.valueOf(initialState));
- setSwitchMode(true);
- }
-
- /**
- * Creates a new switch button that is connected to a boolean property.
- *
- * @param state
- * the Initial state of the switch-button.
- * @param dataSource
- * @deprecated use {@link CheckBox} instead of Button in "switchmode"
- */
- @Deprecated
- public Button(String caption, Property dataSource) {
- setCaption(caption);
- setSwitchMode(true);
- setPropertyDataSource(dataSource);
- }
-
- /**
- * Paints the content of this component.
- *
- * @param event
- * the PaintEvent.
- * @throws IOException
- * if the writing failed due to input/output error.
- * @throws PaintException
- * if the paint operation failed.
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- if (isSwitchMode()) {
- target.addAttribute("type", "switch");
- }
- target.addVariable(this, "state", booleanValue());
-
- if (isDisableOnClick()) {
- target.addAttribute(VButton.ATTR_DISABLE_ON_CLICK, true);
- }
- if (clickShortcut != null) {
- target.addAttribute("keycode", clickShortcut.getKeyCode());
- }
- }
-
- /**
- * Invoked when the value of a variable has changed. Button listeners are
- * notified if the button is clicked.
- *
- * @param source
- * @param variables
- */
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
-
- if (variables.containsKey("disabledOnClick")) {
- // Could be optimized so the button is not repainted because of this
- // (client side has already disabled the button)
- setEnabled(false);
- }
-
- if (!isReadOnly() && variables.containsKey("state")) {
- // Gets the new and old button states
- final Boolean newValue = (Boolean) variables.get("state");
- final Boolean oldValue = (Boolean) getValue();
-
- if (isSwitchMode()) {
-
- // For switch button, the event is only sent if the
- // switch state is changed
- if (newValue != null && !newValue.equals(oldValue)
- && !isReadOnly()) {
- setValue(newValue);
- if (variables.containsKey("mousedetails")) {
- fireClick(MouseEventDetails
- .deSerialize((String) variables
- .get("mousedetails")));
- } else {
- // for compatibility with custom implementations which
- // don't send mouse details
- fireClick();
- }
- }
- } else {
-
- // Only send click event if the button is pushed
- if (newValue.booleanValue()) {
- if (variables.containsKey("mousedetails")) {
- fireClick(MouseEventDetails
- .deSerialize((String) variables
- .get("mousedetails")));
- } else {
- // for compatibility with custom implementations which
- // don't send mouse details
- fireClick();
- }
- }
-
- // If the button is true for some reason, release it
- if (null == oldValue || oldValue.booleanValue()) {
- setValue(Boolean.FALSE);
- }
- }
- }
-
- if (variables.containsKey(FocusEvent.EVENT_ID)) {
- fireEvent(new FocusEvent(this));
- }
- if (variables.containsKey(BlurEvent.EVENT_ID)) {
- fireEvent(new BlurEvent(this));
- }
- }
-
- /**
- * Checks if it is switchMode.
- *
- * @return <code>true</code> if it is in Switch Mode, otherwise
- * <code>false</code>.
- * @deprecated the {@link CheckBox} component should be used instead of
- * Button in switch mode
- */
- @Deprecated
- public boolean isSwitchMode() {
- return switchMode;
- }
-
- /**
- * Sets the switchMode.
- *
- * @param switchMode
- * The switchMode to set.
- * @deprecated the {@link CheckBox} component should be used instead of
- * Button in switch mode
- */
- @Deprecated
- public void setSwitchMode(boolean switchMode) {
- this.switchMode = switchMode;
- if (!switchMode) {
- setImmediate(true);
- if (booleanValue()) {
- setValue(Boolean.FALSE);
- }
- }
- }
-
- /**
- * Get the boolean value of the button state.
- *
- * @return True iff the button is pressed down or checked.
- */
- public boolean booleanValue() {
- Boolean value = (Boolean) getValue();
- return (null == value) ? false : value.booleanValue();
- }
-
- /**
- * Sets immediate mode. Push buttons can not be set in non-immediate mode.
- *
- * @see com.vaadin.ui.AbstractComponent#setImmediate(boolean)
- */
- @Override
- public void setImmediate(boolean immediate) {
- // Push buttons are always immediate
- super.setImmediate(!isSwitchMode() || immediate);
- }
-
- /**
- * The type of the button as a property.
- *
- * @see com.vaadin.data.Property#getType()
- */
- @Override
- public Class getType() {
- return Boolean.class;
- }
-
- /* Click event */
-
- private static final Method BUTTON_CLICK_METHOD;
-
- /**
- * Button style with no decorations. Looks like a link, acts like a button
- *
- * @deprecated use {@link BaseTheme#BUTTON_LINK} instead.
- */
- @Deprecated
- public static final String STYLE_LINK = "link";
-
- static {
- try {
- BUTTON_CLICK_METHOD = ClickListener.class.getDeclaredMethod(
- "buttonClick", new Class[] { ClickEvent.class });
- } catch (final java.lang.NoSuchMethodException e) {
- // This should never happen
- throw new java.lang.RuntimeException(
- "Internal error finding methods in Button");
- }
- }
-
- /**
* Click event. This event is thrown, when the button is clicked.
*
* @author Vaadin Ltd.
@@ -484,6 +259,10 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
*/
public interface ClickListener extends Serializable {
+ public static final Method BUTTON_CLICK_METHOD = ReflectTools
+ .findMethod(ClickListener.class, "buttonClick",
+ ClickEvent.class);
+
/**
* Called when a {@link Button} has been clicked. A reference to the
* button is given by {@link ClickEvent#getButton()}.
@@ -502,7 +281,8 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
* the Listener to be added.
*/
public void addListener(ClickListener listener) {
- addListener(ClickEvent.class, listener, BUTTON_CLICK_METHOD);
+ addListener(ClickEvent.class, listener,
+ ClickListener.BUTTON_CLICK_METHOD);
}
/**
@@ -512,7 +292,8 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
* the Listener to be removed.
*/
public void removeListener(ClickListener listener) {
- removeListener(ClickEvent.class, listener, BUTTON_CLICK_METHOD);
+ removeListener(ClickEvent.class, listener,
+ ClickListener.BUTTON_CLICK_METHOD);
}
/**
@@ -549,16 +330,6 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
fireEvent(new Button.ClickEvent(this, details));
}
- @Override
- protected void setInternalValue(Object newValue) {
- // Make sure only booleans get through
- if (null != newValue && !(newValue instanceof Boolean)) {
- throw new IllegalArgumentException(getClass().getSimpleName()
- + " only accepts Boolean values");
- }
- super.setInternalValue(newValue);
- }
-
public void addListener(BlurListener listener) {
addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
BlurListener.blurMethod);
@@ -584,6 +355,8 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
protected ClickShortcut clickShortcut;
+ private int tabIndex = 0;
+
/**
* Makes it possible to invoke a click on this button by pressing the given
* {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
@@ -601,6 +374,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
}
clickShortcut = new ClickShortcut(this, keyCode, modifiers);
addShortcutListener(clickShortcut);
+ getState().setClickShortcutKeyCode(clickShortcut.getKeyCode());
}
/**
@@ -611,6 +385,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
if (clickShortcut != null) {
removeShortcutListener(clickShortcut);
clickShortcut = null;
+ getState().setClickShortcutKeyCode(0);
}
}
@@ -678,7 +453,7 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
* @return true if the button is disabled when clicked, false otherwise
*/
public boolean isDisableOnClick() {
- return disableOnClick;
+ return getState().isDisableOnClick();
}
/**
@@ -690,8 +465,28 @@ public class Button extends AbstractField implements FieldEvents.BlurNotifier,
* true to disable button when it is clicked, false otherwise
*/
public void setDisableOnClick(boolean disableOnClick) {
- this.disableOnClick = disableOnClick;
+ getState().setDisableOnClick(disableOnClick);
requestRepaint();
}
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+
+ }
+
+ @Override
+ public void focus() {
+ // Overridden only to make public
+ super.focus();
+ }
+
+ @Override
+ public ButtonState getState() {
+ return (ButtonState) super.getState();
+ }
+
}
diff --git a/src/com/vaadin/ui/CheckBox.java b/src/com/vaadin/ui/CheckBox.java
index 00a248cdf3..147a270059 100644
--- a/src/com/vaadin/ui/CheckBox.java
+++ b/src/com/vaadin/ui/CheckBox.java
@@ -4,110 +4,137 @@
package com.vaadin.ui;
-import java.lang.reflect.Method;
-
import com.vaadin.data.Property;
+import com.vaadin.event.FieldEvents.BlurEvent;
+import com.vaadin.event.FieldEvents.BlurListener;
+import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
+import com.vaadin.event.FieldEvents.FocusEvent;
+import com.vaadin.event.FieldEvents.FocusListener;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.checkbox.CheckBoxServerRpc;
+import com.vaadin.terminal.gwt.client.ui.checkbox.CheckBoxState;
-@ClientWidget(com.vaadin.terminal.gwt.client.ui.VCheckBox.class)
-public class CheckBox extends Button {
- /**
- * Creates a new switch button.
- */
- public CheckBox() {
- setSwitchMode(true);
- }
+public class CheckBox extends AbstractField<Boolean> {
+
+ private CheckBoxServerRpc rpc = new CheckBoxServerRpc() {
+
+ public void setChecked(boolean checked,
+ MouseEventDetails mouseEventDetails) {
+ if (isReadOnly()) {
+ return;
+ }
+
+ final Boolean oldValue = getValue();
+ final Boolean newValue = checked;
+
+ if (!newValue.equals(oldValue)) {
+ // The event is only sent if the switch state is changed
+ setValue(newValue);
+ }
+
+ }
+ };
+
+ FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) {
+ @Override
+ protected void fireEvent(Event event) {
+ CheckBox.this.fireEvent(event);
+ }
+ };
/**
- * Creates a new switch button with a caption and a set initial state.
- *
- * @param caption
- * the caption of the switch button
- * @param initialState
- * the initial state of the switch button
+ * Creates a new checkbox.
*/
- @SuppressWarnings("deprecation")
- public CheckBox(String caption, boolean initialState) {
- super(caption, initialState);
+ public CheckBox() {
+ registerRpc(rpc);
+ registerRpc(focusBlurRpc);
+ setValue(Boolean.FALSE);
}
/**
- * Creates a new switch button with a caption and a click listener.
+ * Creates a new checkbox with a set caption.
*
* @param caption
- * the caption of the switch button
- * @param listener
- * the click listener
+ * the Checkbox caption.
*/
- public CheckBox(String caption, ClickListener listener) {
- super(caption, listener);
- setSwitchMode(true);
+ public CheckBox(String caption) {
+ this();
+ setCaption(caption);
}
/**
- * Convenience method for creating a new switch button with a method
- * listening button clicks. Using this method is discouraged because it
- * cannot be checked during compilation. Use
- * {@link #addListener(Class, Object, Method)} or
- * {@link #addListener(com.vaadin.ui.Component.Listener)} instead. The
- * method must have either no parameters, or only one parameter of
- * Button.ClickEvent type.
+ * Creates a new checkbox with a caption and a set initial state.
*
* @param caption
- * the Button caption.
- * @param target
- * the Object having the method for listening button clicks.
- * @param methodName
- * the name of the method in target object, that receives button
- * click events.
+ * the caption of the checkbox
+ * @param initialState
+ * the initial state of the checkbox
*/
- public CheckBox(String caption, Object target, String methodName) {
- super(caption, target, methodName);
- setSwitchMode(true);
+ public CheckBox(String caption, boolean initialState) {
+ this(caption);
+ setValue(initialState);
}
/**
- * Creates a new switch button that is connected to a boolean property.
+ * Creates a new checkbox that is connected to a boolean property.
*
* @param state
* the Initial state of the switch-button.
* @param dataSource
*/
- @SuppressWarnings("deprecation")
- public CheckBox(String caption, Property dataSource) {
- super(caption, dataSource);
- setSwitchMode(true);
+ public CheckBox(String caption, Property<?> dataSource) {
+ this(caption);
+ setPropertyDataSource(dataSource);
}
- /**
- * Creates a new push button with a set caption.
- *
- * The value of the push button is always false and they are immediate by
- * default.
- *
- * @param caption
- * the Button caption.
- */
+ @Override
+ public Class<Boolean> getType() {
+ return Boolean.class;
+ }
- @SuppressWarnings("deprecation")
- public CheckBox(String caption) {
- super(caption, false);
+ @Override
+ public CheckBoxState getState() {
+ return (CheckBoxState) super.getState();
}
- @Deprecated
@Override
- public void setSwitchMode(boolean switchMode)
- throws UnsupportedOperationException {
- if (this.switchMode && !switchMode) {
- throw new UnsupportedOperationException(
- "CheckBox is always in switch mode (consider using a Button)");
+ protected void setInternalValue(Boolean newValue) {
+ super.setInternalValue(newValue);
+ if (newValue == null) {
+ newValue = false;
}
- super.setSwitchMode(true);
+ getState().setChecked(newValue);
}
- @Override
- public void setDisableOnClick(boolean disableOnClick) {
- throw new UnsupportedOperationException(
- "CheckBox does not support disable on click");
+ public void addListener(BlurListener listener) {
+ addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
+ BlurListener.blurMethod);
+ }
+
+ public void removeListener(BlurListener listener) {
+ removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
+ }
+
+ public void addListener(FocusListener listener) {
+ addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
+ FocusListener.focusMethod);
}
+ public void removeListener(FocusListener listener) {
+ removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
+ }
+
+ /**
+ * Get the boolean value of the button state.
+ *
+ * @return True iff the button is pressed down or checked.
+ *
+ * @deprecated Use {@link #getValue()} instead and, if needed, handle null
+ * values.
+ */
+ @Deprecated
+ public boolean booleanValue() {
+ Boolean value = getValue();
+ return (null == value) ? false : value.booleanValue();
+ }
}
diff --git a/src/com/vaadin/ui/ComboBox.java b/src/com/vaadin/ui/ComboBox.java
index bc7ab6f994..6286dad124 100644
--- a/src/com/vaadin/ui/ComboBox.java
+++ b/src/com/vaadin/ui/ComboBox.java
@@ -9,7 +9,7 @@ import java.util.Collection;
import com.vaadin.data.Container;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
+import com.vaadin.terminal.gwt.client.ui.combobox.VFilterSelect;
/**
* A filtering dropdown single-select. Suitable for newItemsAllowed, but it's
@@ -20,7 +20,6 @@ import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
*
*/
@SuppressWarnings("serial")
-@ClientWidget(VFilterSelect.class)
public class ComboBox extends Select {
private String inputPrompt = null;
@@ -33,36 +32,24 @@ public class ComboBox extends Select {
private boolean textInputAllowed = true;
public ComboBox() {
- setMultiSelect(false);
setNewItemsAllowed(false);
}
public ComboBox(String caption, Collection<?> options) {
super(caption, options);
- setMultiSelect(false);
setNewItemsAllowed(false);
}
public ComboBox(String caption, Container dataSource) {
super(caption, dataSource);
- setMultiSelect(false);
setNewItemsAllowed(false);
}
public ComboBox(String caption) {
super(caption);
- setMultiSelect(false);
setNewItemsAllowed(false);
}
- @Override
- public void setMultiSelect(boolean multiSelect) {
- if (multiSelect && !isMultiSelect()) {
- throw new UnsupportedOperationException("Multiselect not supported");
- }
- super.setMultiSelect(multiSelect);
- }
-
/**
* Gets the current input prompt.
*
diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java
index b32aad2fca..3632c4ca5e 100644
--- a/src/com/vaadin/ui/Component.java
+++ b/src/com/vaadin/ui/Component.java
@@ -5,7 +5,6 @@
package com.vaadin.ui;
import java.io.Serializable;
-import java.util.Collection;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Locale;
@@ -13,10 +12,11 @@ import java.util.Locale;
import com.vaadin.Application;
import com.vaadin.event.FieldEvents;
import com.vaadin.terminal.ErrorMessage;
-import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.server.ClientConnector;
/**
* {@code Component} is the top-level interface that is and must be implemented
@@ -51,8 +51,7 @@ import com.vaadin.terminal.VariableOwner;
* @VERSION@
* @since 3.0
*/
-public interface Component extends Paintable, VariableOwner, Sizeable,
- Serializable {
+public interface Component extends ClientConnector, Sizeable, Serializable {
/**
* Gets all user-defined CSS style names of a component. If the component
@@ -115,9 +114,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* </p>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * This method will trigger a {@link RepaintRequestEvent}.
* </p>
*
* @param style
@@ -159,9 +156,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* </pre>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * This method will trigger a {@link RepaintRequestEvent}.
* </p>
*
* @param style
@@ -183,9 +178,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* style names defined in Vaadin or GWT can not be removed.
* </p>
*
- * * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * * This method will trigger a {@link RepaintRequestEvent}.
*
* @param style
* the style name or style names to be removed
@@ -202,8 +195,14 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* component are also disabled. Components are enabled by default.
*
* <p>
- * As a security feature, all variable change events for disabled components
- * are blocked on the server-side.
+ * As a security feature, all updates for disabled components are blocked on
+ * the server-side.
+ * </p>
+ *
+ * <p>
+ * Note that this method only returns the status of the component and does
+ * not take parents into account. Even though this method returns true the
+ * component can be disabled to the user if a parent is disabled.
* </p>
*
* @return <code>true</code> if the component and its parent are enabled,
@@ -216,9 +215,6 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* Enables or disables the component. The user can not interact disabled
* components, which are shown with a style that indicates the status,
* usually shaded in light gray color. Components are enabled by default.
- * Children of a disabled component are automatically disabled; if a child
- * component is explicitly set as disabled, changes in the disabled status
- * of its parents do not change its status.
*
* <pre>
* Button enabled = new Button(&quot;Enabled&quot;);
@@ -231,10 +227,9 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* </pre>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent} for the component and, if it is a
- * {@link ComponentContainer}, for all its children recursively.
+ * This method will trigger a {@link RepaintRequestEvent} for the component
+ * and, if it is a {@link ComponentContainer}, for all its children
+ * recursively.
* </p>
*
* @param enabled
@@ -248,27 +243,22 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
*
* <p>
* Visible components are drawn in the user interface, while invisible ones
- * are not. The effect is not merely a cosmetic CSS change, but the entire
- * HTML element will be empty. Making a component invisible through this
- * property can alter the positioning of other components.
+ * are not. The effect is not merely a cosmetic CSS change - no information
+ * about an invisible component will be sent to the client. The effect is
+ * thus the same as removing the component from its parent. Making a
+ * component invisible through this property can alter the positioning of
+ * other components.
* </p>
*
* <p>
- * A component is visible only if all its parents are also visible. Notice
- * that if a child component is explicitly set as invisible, changes in the
- * visibility status of its parents do not change its status.
+ * A component is visible only if all its parents are also visible. This is
+ * not checked by this method though, so even if this method returns true,
+ * the component can be hidden from the user because a parent is set to
+ * invisible.
* </p>
*
- * <p>
- * This method does not check whether the component is attached (see
- * {@link #attach()}). The component and all its parents may be considered
- * "visible", but not necessarily attached to application. To test if
- * component will actually be drawn, check both its visibility and that
- * {@link #getApplication()} does not return {@code null}.
- * </p>
- *
- * @return <code>true</code> if the component is visible in the user
- * interface, <code>false</code> if not
+ * @return <code>true</code> if the component has been set to be visible in
+ * the user interface, <code>false</code> if not
* @see #setVisible(boolean)
* @see #attach()
*/
@@ -279,8 +269,9 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
*
* <p>
* Visible components are drawn in the user interface, while invisible ones
- * are not. The effect is not merely a cosmetic CSS change, but the entire
- * HTML element will be empty.
+ * are not. The effect is not merely a cosmetic CSS change - no information
+ * about an invisible component will be sent to the client. The effect is
+ * thus the same as removing the component from its parent.
* </p>
*
* <pre>
@@ -315,7 +306,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* @return the parent component
* @see #setParent(Component)
*/
- public Component getParent();
+ public HasComponents getParent();
/**
* Sets the parent component of the component.
@@ -327,7 +318,6 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* is attached to the application, {@link #detach()} is called for the
* component.
* </p>
- *
* <p>
* This method is rarely called directly. The
* {@link ComponentContainer#addComponent(Component)} method is normally
@@ -346,7 +336,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* if a parent is given even though the component already has a
* parent
*/
- public void setParent(Component parent);
+ public void setParent(HasComponents parent);
/**
* Tests whether the component is in the read-only mode. The user can not
@@ -389,9 +379,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* </p>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * This method will trigger a {@link RepaintRequestEvent}.
* </p>
*
* @param readOnly
@@ -458,10 +446,8 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* </p>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}. A reimplementation should call the superclass
- * implementation.
+ * This method will trigger a {@link RepaintRequestEvent}. A
+ * reimplementation should call the superclass implementation.
* </p>
*
* @param caption
@@ -531,9 +517,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* {@code v-caption} .
* </p>
*
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * This method will trigger a {@link RepaintRequestEvent}.
*
* @param icon
* the icon of the component. If null, no icon is shown and it
@@ -560,7 +544,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* @return the parent window of the component or <code>null</code> if it is
* not attached to a window or is itself a window
*/
- public Window getWindow();
+ public Root getRoot();
/**
* Gets the application object to which the component is attached.
@@ -593,7 +577,7 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* <p>
* Reimplementing the {@code attach()} method is useful for tasks that need
* to get a reference to the parent, window, or application object with the
- * {@link #getParent()}, {@link #getWindow()}, and {@link #getApplication()}
+ * {@link #getParent()}, {@link #getRoot()}, and {@link #getApplication()}
* methods. A component does not yet know these objects in the constructor,
* so in such case, the methods will return {@code null}. For example, the
* following is invalid:
@@ -618,6 +602,11 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* application, the {@code attach()} is called immediately from
* {@link #setParent(Component)}.
* </p>
+ * <p>
+ * This method must call {@link Root#componentAttached(Component)} to let
+ * the Root know that a new Component has been attached.
+ * </p>
+ *
*
* <pre>
* public class AttachExample extends CustomComponent {
@@ -648,11 +637,16 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
* Notifies the component that it is detached from the application.
*
* <p>
- * The {@link #getApplication()} and {@link #getWindow()} methods might
- * return <code>null</code> after this method is called.
+ * The {@link #getApplication()} and {@link #getRoot()} methods might return
+ * <code>null</code> after this method is called.
* </p>
*
* <p>
+ * This method must call {@link Root#componentDetached(Component)} to let
+ * the Root know that a new Component has been attached.
+ * </p>
+ * *
+ * <p>
* The caller of this method is {@link #setParent(Component)} if the parent
* is in the application. When the parent is detached from the application
* it is its response to call {@link #detach()} for all the children and to
@@ -685,31 +679,114 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
public Locale getLocale();
/**
- * The child components of the component must call this method when they
- * need repainting. The call must be made even in the case in which the
- * children sent the repaint request themselves.
+ * Returns the current shared state bean for the component. The state (or
+ * changes to it) is communicated from the server to the client.
*
- * <p>
- * A repaint request is ignored if the component is invisible.
- * </p>
+ * Subclasses can use a more specific return type for this method.
+ *
+ * @return The state object for the component
*
+ * @since 7.0
+ */
+ public ComponentState getState();
+
+ /**
+ * Called before the shared state is sent to the client. Gives the component
+ * an opportunity to set computed/dynamic state values e.g. state values
+ * that depend on other component features.
* <p>
- * This method is called automatically by {@link AbstractComponent}, which
- * also provides a default implementation of it. As this is a somewhat
- * internal feature, it is rarely necessary to reimplement this or call it
- * explicitly.
+ * This method must not alter the component hierarchy in any way.
* </p>
*
- * @param alreadyNotified
- * the collection of repaint request listeners that have been
- * already notified by the child. This component should not
- * re-notify the listed listeners again. The container given as
- * parameter must be modifiable as the component might modify it
- * and pass it forward. A {@code null} parameter is interpreted
- * as an empty collection.
+ * @since 7.0
+ */
+ public void updateState();
+
+ /**
+ * Adds an unique id for component that get's transferred to terminal for
+ * testing purposes. Keeping identifiers unique is the responsibility of the
+ * programmer.
+ *
+ * @param id
+ * An alphanumeric id
+ */
+ public void setDebugId(String id);
+
+ /**
+ * Get's currently set debug identifier
+ *
+ * @return current debug id, null if not set
*/
- public void childRequestedRepaint(
- Collection<RepaintRequestListener> alreadyNotified);
+ public String getDebugId();
+
+ /**
+ * Requests that the component should be repainted as soon as possible.
+ */
+ public void requestRepaint();
+
+ /**
+ * Repaint request event is thrown when the connector needs to be repainted.
+ * This is typically done when the <code>paint</code> method would return
+ * dissimilar UIDL from the previous call of the method.
+ */
+ @SuppressWarnings("serial")
+ public static class RepaintRequestEvent extends EventObject {
+
+ /**
+ * Constructs a new event.
+ *
+ * @param source
+ * the paintable needing repaint.
+ */
+ public RepaintRequestEvent(ClientConnector source) {
+ super(source);
+ }
+
+ /**
+ * Gets the connector needing repainting.
+ *
+ * @return Paintable for which the <code>paint</code> method will return
+ * dissimilar UIDL from the previous call of the method.
+ */
+ public ClientConnector getConnector() {
+ return (ClientConnector) getSource();
+ }
+ }
+
+ /**
+ * Listens repaint requests. The <code>repaintRequested</code> method is
+ * called when the paintable needs to be repainted. This is typically done
+ * when the <code>paint</code> method would return dissimilar UIDL from the
+ * previous call of the method.
+ */
+ public interface RepaintRequestListener extends Serializable {
+
+ /**
+ * Receives repaint request events.
+ *
+ * @param event
+ * the repaint request event specifying the paintable source.
+ */
+ public void repaintRequested(RepaintRequestEvent event);
+ }
+
+ /**
+ * Adds repaint request listener. In order to assure that no repaint
+ * requests are missed, the new repaint listener should paint the paintable
+ * right after adding itself as listener.
+ *
+ * @param listener
+ * the listener to be added.
+ */
+ public void addListener(RepaintRequestListener listener);
+
+ /**
+ * Removes repaint request listener.
+ *
+ * @param listener
+ * the listener to be removed.
+ */
+ public void removeListener(RepaintRequestListener listener);
/* Component event framework */
@@ -1099,4 +1176,5 @@ public interface Component extends Paintable, VariableOwner, Sizeable,
public void setTabIndex(int tabIndex);
}
+
}
diff --git a/src/com/vaadin/ui/ComponentContainer.java b/src/com/vaadin/ui/ComponentContainer.java
index 1e1f0796ca..8182d54b56 100644
--- a/src/com/vaadin/ui/ComponentContainer.java
+++ b/src/com/vaadin/ui/ComponentContainer.java
@@ -5,7 +5,6 @@
package com.vaadin.ui;
import java.io.Serializable;
-import java.util.Iterator;
/**
* Extension to the {@link Component} interface which adds to it the capacity to
@@ -17,7 +16,7 @@ import java.util.Iterator;
* @VERSION@
* @since 3.0
*/
-public interface ComponentContainer extends Component {
+public interface ComponentContainer extends HasComponents {
/**
* Adds the component into this container.
@@ -61,22 +60,13 @@ public interface ComponentContainer extends Component {
public void replaceComponent(Component oldComponent, Component newComponent);
/**
- * Gets an iterator to the collection of contained components. Using this
- * iterator it is possible to step through all components contained in this
- * container.
+ * Gets the number of children this {@link ComponentContainer} has. This
+ * must be symmetric with what {@link #getComponentIterator()} returns.
*
- * @return the component iterator.
+ * @return The number of child components this container has.
+ * @since 7.0.0
*/
- public Iterator<Component> getComponentIterator();
-
- /**
- * Causes a repaint of this component, and all components below it.
- *
- * This should only be used in special cases, e.g when the state of a
- * descendant depends on the state of a ancestor.
- *
- */
- public void requestRepaintAll();
+ public int getComponentCount();
/**
* Moves all components from an another container into this container. The
diff --git a/src/com/vaadin/ui/CssLayout.java b/src/com/vaadin/ui/CssLayout.java
index b9432df6b6..0a2656af31 100644
--- a/src/com/vaadin/ui/CssLayout.java
+++ b/src/com/vaadin/ui/CssLayout.java
@@ -3,18 +3,17 @@
*/
package com.vaadin.ui;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.Paintable;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.ui.VCssLayout;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutServerRpc;
+import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutState;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
/**
* CssLayout is a layout component that can be used in browser environment only.
@@ -57,16 +56,25 @@ import com.vaadin.terminal.gwt.client.ui.VCssLayout;
* @since 6.1 brought in from "FastLayouts" incubator project
*
*/
-@ClientWidget(VCssLayout.class)
public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
- private static final String CLICK_EVENT = EventId.LAYOUT_CLICK;
+ private CssLayoutServerRpc rpc = new CssLayoutServerRpc() {
+ public void layoutClick(MouseEventDetails mouseDetails,
+ Connector clickedConnector) {
+ fireEvent(LayoutClickEvent.createEvent(CssLayout.this,
+ mouseDetails, clickedConnector));
+ }
+ };
/**
* Custom layout slots containing the components.
*/
protected LinkedList<Component> components = new LinkedList<Component>();
+ public CssLayout() {
+ registerRpc(rpc);
+ }
+
/**
* Add a component into this container. The component is added to the right
* or under the previous component.
@@ -173,33 +181,22 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
return components.size();
}
- /**
- * Paints the content of this component.
- *
- * @param target
- * the Paint Event.
- * @throws PaintException
- * if the paint operation failed.
- */
@Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
- HashMap<Paintable, String> componentCss = null;
- // Adds all items in all the locations
- for (Component c : components) {
- // Paint child component UIDL
- c.paint(target);
- String componentCssString = getCss(c);
+ public void updateState() {
+ super.updateState();
+ getState().getChildCss().clear();
+ for (Component child : this) {
+ String componentCssString = getCss(child);
if (componentCssString != null) {
- if (componentCss == null) {
- componentCss = new HashMap<Paintable, String>();
- }
- componentCss.put(c, componentCssString);
+ getState().getChildCss().put(child, componentCssString);
}
+
}
- if (componentCss != null) {
- target.addAttribute("css", componentCss);
- }
+ }
+
+ @Override
+ public CssLayoutState getState() {
+ return (CssLayoutState) super.getState();
}
/**
@@ -267,12 +264,14 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
}
public void addListener(LayoutClickListener listener) {
- addListener(CLICK_EVENT, LayoutClickEvent.class, listener,
+ addListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener,
LayoutClickListener.clickMethod);
}
public void removeListener(LayoutClickListener listener) {
- removeListener(CLICK_EVENT, LayoutClickEvent.class, listener);
+ removeListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener);
}
/**
diff --git a/src/com/vaadin/ui/CustomComponent.java b/src/com/vaadin/ui/CustomComponent.java
index 21eda08909..98d650f6db 100644
--- a/src/com/vaadin/ui/CustomComponent.java
+++ b/src/com/vaadin/ui/CustomComponent.java
@@ -7,11 +7,6 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.util.Iterator;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VCustomComponent;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
/**
* Custom component provides simple implementation of Component interface for
* creation of new UI components by composition of existing components.
@@ -27,7 +22,6 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VCustomComponent.class, loadStyle = LoadStyle.EAGER)
public class CustomComponent extends AbstractComponentContainer {
/**
@@ -36,11 +30,6 @@ public class CustomComponent extends AbstractComponentContainer {
private Component root = null;
/**
- * Type of the component.
- */
- private String componentType = null;
-
- /**
* Constructs a new custom component.
*
* <p>
@@ -107,51 +96,6 @@ public class CustomComponent extends AbstractComponentContainer {
/* Basic component features ------------------------------------------ */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- if (root == null) {
- throw new IllegalStateException("Composition root must be set to"
- + " non-null value before the " + getClass().getName()
- + " can be painted");
- }
-
- if (getComponentType() != null) {
- target.addAttribute("type", getComponentType());
- }
- root.paint(target);
- }
-
- /**
- * Gets the component type.
- *
- * The component type is textual type of the component. This is included in
- * the UIDL as component tag attribute.
- *
- * @deprecated not more useful as the whole tag system has been removed
- *
- * @return the component type.
- */
- @Deprecated
- public String getComponentType() {
- return componentType;
- }
-
- /**
- * Sets the component type.
- *
- * The component type is textual type of the component. This is included in
- * the UIDL as component tag attribute.
- *
- * @deprecated not more useful as the whole tag system has been removed
- *
- * @param componentType
- * the componentType to set.
- */
- @Deprecated
- public void setComponentType(String componentType) {
- this.componentType = componentType;
- }
-
private class ComponentIterator implements Iterator<Component>,
Serializable {
boolean first = getCompositionRoot() != null;
diff --git a/src/com/vaadin/ui/CustomField.java b/src/com/vaadin/ui/CustomField.java
new file mode 100644
index 0000000000..806ee91335
--- /dev/null
+++ b/src/com/vaadin/ui/CustomField.java
@@ -0,0 +1,230 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+
+import com.vaadin.data.Property;
+
+/**
+ * A {@link Field} whose UI content can be constructed by the user, enabling the
+ * creation of e.g. form fields by composing Vaadin components. Customization of
+ * both the visual presentation and the logic of the field is possible.
+ *
+ * Subclasses must implement {@link #getType()} and {@link #initContent()}.
+ *
+ * Most custom fields can simply compose a user interface that calls the methods
+ * {@link #setInternalValue(Object)} and {@link #getInternalValue()} when
+ * necessary.
+ *
+ * It is also possible to override {@link #validate()},
+ * {@link #setInternalValue(Object)}, {@link #commit()},
+ * {@link #setPropertyDataSource(Property)}, {@link #isEmpty()} and other logic
+ * of the field. Methods overriding {@link #setInternalValue(Object)} should
+ * also call the corresponding superclass method.
+ *
+ * @param <T>
+ * field value type
+ *
+ * @since 7.0
+ */
+public abstract class CustomField<T> extends AbstractField<T> implements
+ ComponentContainer {
+
+ /**
+ * The root component implementing the custom component.
+ */
+ private Component root = null;
+
+ /**
+ * Constructs a new custom field.
+ *
+ * <p>
+ * The component is implemented by wrapping the methods of the composition
+ * root component given as parameter. The composition root must be set
+ * before the component can be used.
+ * </p>
+ */
+ public CustomField() {
+ // expand horizontally by default
+ setWidth(100, Unit.PERCENTAGE);
+ }
+
+ /**
+ * Constructs the content and notifies it that the {@link CustomField} is
+ * attached to a window.
+ *
+ * @see com.vaadin.ui.Component#attach()
+ */
+ @Override
+ public void attach() {
+ root = getContent();
+ super.attach();
+ getContent().setParent(this);
+ getContent().attach();
+
+ fireComponentAttachEvent(getContent());
+ }
+
+ /**
+ * Notifies the content that the {@link CustomField} is detached from a
+ * window.
+ *
+ * @see com.vaadin.ui.Component#detach()
+ */
+ @Override
+ public void detach() {
+ super.detach();
+ getContent().detach();
+ }
+
+ /**
+ * Returns the content (UI) of the custom component.
+ *
+ * @return Component
+ */
+ protected Component getContent() {
+ if (null == root) {
+ root = initContent();
+ }
+ return root;
+ }
+
+ /**
+ * Create the content component or layout for the field. Subclasses of
+ * {@link CustomField} should implement this method.
+ *
+ * Note that this method is called when the CustomField is attached to a
+ * layout or when {@link #getContent()} is called explicitly for the first
+ * time. It is only called once for a {@link CustomField}.
+ *
+ * @return {@link Component} representing the UI of the CustomField
+ */
+ protected abstract Component initContent();
+
+ // Size related methods
+ // TODO might not be necessary to override but following the pattern from
+ // AbstractComponentContainer
+
+ @Override
+ public void setHeight(float height, Unit unit) {
+ super.setHeight(height, unit);
+ requestRepaintAll();
+ }
+
+ @Override
+ public void setWidth(float height, Unit unit) {
+ super.setWidth(height, unit);
+ requestRepaintAll();
+ }
+
+ // ComponentContainer methods
+
+ private class ComponentIterator implements Iterator<Component>,
+ Serializable {
+ boolean first = (root != null);
+
+ public boolean hasNext() {
+ return first;
+ }
+
+ public Component next() {
+ first = false;
+ return getContent();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public Iterator<Component> getComponentIterator() {
+ return new ComponentIterator();
+ }
+
+ public Iterator<Component> iterator() {
+ return getComponentIterator();
+ }
+
+ public int getComponentCount() {
+ return (null != getContent()) ? 1 : 0;
+ }
+
+ public void requestRepaintAll() {
+ AbstractComponentContainer.requestRepaintAll(this);
+ }
+
+ /**
+ * Fires the component attached event. This should be called by the
+ * addComponent methods after the component have been added to this
+ * container.
+ *
+ * @param component
+ * the component that has been added to this container.
+ */
+ protected void fireComponentAttachEvent(Component component) {
+ fireEvent(new ComponentAttachEvent(this, component));
+ }
+
+ // TODO remove these methods when ComponentContainer interface is cleaned up
+
+ public void addComponent(Component c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeComponent(Component c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeAllComponents() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void replaceComponent(Component oldComponent, Component newComponent) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void moveComponentsFrom(ComponentContainer source) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static final Method COMPONENT_ATTACHED_METHOD;
+
+ static {
+ try {
+ COMPONENT_ATTACHED_METHOD = ComponentAttachListener.class
+ .getDeclaredMethod("componentAttachedToContainer",
+ new Class[] { ComponentAttachEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error finding methods in CustomField");
+ }
+ }
+
+ public void addListener(ComponentAttachListener listener) {
+ addListener(ComponentContainer.ComponentAttachEvent.class, listener,
+ COMPONENT_ATTACHED_METHOD);
+ }
+
+ public void removeListener(ComponentAttachListener listener) {
+ removeListener(ComponentContainer.ComponentAttachEvent.class, listener,
+ COMPONENT_ATTACHED_METHOD);
+ }
+
+ public void addListener(ComponentDetachListener listener) {
+ // content never detached
+ }
+
+ public void removeListener(ComponentDetachListener listener) {
+ // content never detached
+ }
+
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ }
+}
diff --git a/src/com/vaadin/ui/CustomLayout.java b/src/com/vaadin/ui/CustomLayout.java
index dc473fb549..97cea1c49d 100644
--- a/src/com/vaadin/ui/CustomLayout.java
+++ b/src/com/vaadin/ui/CustomLayout.java
@@ -9,10 +9,14 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VCustomLayout;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.customlayout.CustomLayoutState;
+import com.vaadin.terminal.gwt.server.JsonPaintTarget;
/**
* <p>
@@ -44,8 +48,7 @@ import com.vaadin.terminal.gwt.client.ui.VCustomLayout;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VCustomLayout.class)
-public class CustomLayout extends AbstractLayout {
+public class CustomLayout extends AbstractLayout implements Vaadin6Component {
private static final int BUFFER_SIZE = 10000;
@@ -54,10 +57,6 @@ public class CustomLayout extends AbstractLayout {
*/
private final HashMap<String, Component> slots = new HashMap<String, Component>();
- private String templateContents = null;
-
- private String templateName = null;
-
/**
* Default constructor only used by subclasses. Subclasses are responsible
* for setting the appropriate fields. Either
@@ -90,7 +89,7 @@ public class CustomLayout extends AbstractLayout {
*/
public CustomLayout(String template) {
this();
- templateName = template;
+ setTemplateName(template);
}
protected void initTemplateContentsFromInputStream(
@@ -110,7 +109,12 @@ public class CustomLayout extends AbstractLayout {
}
}
- templateContents = b.toString();
+ setTemplateContents(b.toString());
+ }
+
+ @Override
+ public CustomLayoutState getState() {
+ return (CustomLayoutState) super.getState();
}
/**
@@ -128,6 +132,7 @@ public class CustomLayout extends AbstractLayout {
removeComponent(old);
}
slots.put(location, c);
+ getState().getChildLocations().put(c, location);
c.setParent(this);
fireComponentAttachEvent(c);
requestRepaint();
@@ -159,6 +164,7 @@ public class CustomLayout extends AbstractLayout {
return;
}
slots.values().remove(c);
+ getState().getChildLocations().remove(c);
super.removeComponent(c);
requestRepaint();
}
@@ -205,37 +211,6 @@ public class CustomLayout extends AbstractLayout {
return slots.get(location);
}
- /**
- * Paints the content of this component.
- *
- * @param target
- * @throws PaintException
- * if the paint operation failed.
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- if (templateName != null) {
- target.addAttribute("template", templateName);
- } else {
- target.addAttribute("templateContents", templateContents);
- }
- // Adds all items in all the locations
- for (final Iterator<String> i = slots.keySet().iterator(); i.hasNext();) {
- // Gets the (location,component)
- final String location = i.next();
- final Component c = slots.get(location);
- if (c != null) {
- // Writes the item
- target.startTag("location");
- target.addAttribute("name", location);
- c.paint(target);
- target.endTag("location");
- }
- }
- }
-
/* Documented in superclass */
public void replaceComponent(Component oldComponent, Component newComponent) {
@@ -261,32 +236,20 @@ public class CustomLayout extends AbstractLayout {
} else {
slots.put(newLocation, oldComponent);
slots.put(oldLocation, newComponent);
+ getState().getChildLocations().put(newComponent, oldLocation);
+ getState().getChildLocations().put(oldComponent, newLocation);
requestRepaint();
}
}
- /**
- * CustomLayout's template selecting was previously implemented with
- * setStyle. Overriding to improve backwards compatibility.
- *
- * @param name
- * template name
- * @deprecated Use {@link #setTemplateName(String)} instead
- */
- @Deprecated
- @Override
- public void setStyle(String name) {
- setTemplateName(name);
- }
-
/** Get the name of the template */
public String getTemplateName() {
- return templateName;
+ return getState().getTemplateName();
}
/** Get the contents of the template */
public String getTemplateContents() {
- return templateContents;
+ return getState().getTemplateContents();
}
/**
@@ -299,8 +262,8 @@ public class CustomLayout extends AbstractLayout {
* @param templateName
*/
public void setTemplateName(String templateName) {
- this.templateName = templateName;
- templateContents = null;
+ getState().setTemplateName(templateName);
+ getState().setTemplateContents(null);
requestRepaint();
}
@@ -310,8 +273,8 @@ public class CustomLayout extends AbstractLayout {
* @param templateContents
*/
public void setTemplateContents(String templateContents) {
- this.templateContents = templateContents;
- templateName = null;
+ getState().setTemplateContents(templateContents);
+ getState().setTemplateName(null);
requestRepaint();
}
@@ -342,4 +305,20 @@ public class CustomLayout extends AbstractLayout {
"CustomLayout does not support margins.");
}
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // Nothing to see here
+ }
+
+ public void paintContent(PaintTarget target) throws PaintException {
+ // Workaround to make the CommunicationManager read the template file
+ // and send it to the client
+ String templateName = getState().getTemplateName();
+ if (templateName != null && templateName.length() != 0) {
+ Set<Object> usedResources = ((JsonPaintTarget) target)
+ .getUsedResources();
+ String resourceName = "layouts/" + templateName + ".html";
+ usedResources.add(resourceName);
+ }
+ }
+
}
diff --git a/src/com/vaadin/ui/DateField.java b/src/com/vaadin/ui/DateField.java
index ef67345aab..55ff67229c 100644
--- a/src/com/vaadin/ui/DateField.java
+++ b/src/com/vaadin/ui/DateField.java
@@ -4,11 +4,13 @@
package com.vaadin.ui;
-import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
@@ -16,6 +18,7 @@ import java.util.TimeZone;
import com.vaadin.data.Property;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.util.converter.Converter;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
@@ -23,8 +26,8 @@ import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VDateField;
-import com.vaadin.terminal.gwt.client.ui.VPopupCalendar;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.datefield.VDateField;
/**
* <p>
@@ -47,56 +50,128 @@ import com.vaadin.terminal.gwt.client.ui.VPopupCalendar;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VPopupCalendar.class)
-public class DateField extends AbstractField implements
- FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {
-
- /* Private members */
+public class DateField extends AbstractField<Date> implements
+ FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Vaadin6Component {
/**
- * Resolution identifier: milliseconds.
+ * Resolutions for DateFields
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
*/
- public static final int RESOLUTION_MSEC = 0;
+ public enum Resolution {
+ SECOND(Calendar.SECOND), MINUTE(Calendar.MINUTE), HOUR(
+ Calendar.HOUR_OF_DAY), DAY(Calendar.DAY_OF_MONTH), MONTH(
+ Calendar.MONTH), YEAR(Calendar.YEAR);
+
+ private int calendarField;
+
+ private Resolution(int calendarField) {
+ this.calendarField = calendarField;
+ }
+
+ /**
+ * Returns the field in {@link Calendar} that corresponds to this
+ * resolution.
+ *
+ * @return one of the field numbers used by Calendar
+ */
+ public int getCalendarField() {
+ return calendarField;
+ }
+
+ /**
+ * Returns the resolutions that are higher or equal to the given
+ * resolution, starting from the given resolution. In other words
+ * passing DAY to this methods returns DAY,MONTH,YEAR
+ *
+ * @param r
+ * The resolution to start from
+ * @return An iterable for the resolutions higher or equal to r
+ */
+ public static Iterable<Resolution> getResolutionsHigherOrEqualTo(
+ Resolution r) {
+ List<Resolution> resolutions = new ArrayList<DateField.Resolution>();
+ Resolution[] values = Resolution.values();
+ for (int i = r.ordinal(); i < values.length; i++) {
+ resolutions.add(values[i]);
+ }
+ return resolutions;
+ }
+
+ /**
+ * Returns the resolutions that are lower than the given resolution,
+ * starting from the given resolution. In other words passing DAY to
+ * this methods returns HOUR,MINUTE,SECOND.
+ *
+ * @param r
+ * The resolution to start from
+ * @return An iterable for the resolutions lower than r
+ */
+ public static List<Resolution> getResolutionsLowerThan(Resolution r) {
+ List<Resolution> resolutions = new ArrayList<DateField.Resolution>();
+ Resolution[] values = Resolution.values();
+ for (int i = r.ordinal() - 1; i >= 0; i--) {
+ resolutions.add(values[i]);
+ }
+ return resolutions;
+ }
+ };
/**
* Resolution identifier: seconds.
+ *
+ * @deprecated Use {@link Resolution#SECOND}
*/
- public static final int RESOLUTION_SEC = 1;
+ @Deprecated
+ public static final Resolution RESOLUTION_SEC = Resolution.SECOND;
/**
* Resolution identifier: minutes.
+ *
+ * @deprecated Use {@link Resolution#MINUTE}
*/
- public static final int RESOLUTION_MIN = 2;
+ @Deprecated
+ public static final Resolution RESOLUTION_MIN = Resolution.MINUTE;
/**
* Resolution identifier: hours.
+ *
+ * @deprecated Use {@link Resolution#HOUR}
*/
- public static final int RESOLUTION_HOUR = 3;
+ @Deprecated
+ public static final Resolution RESOLUTION_HOUR = Resolution.HOUR;
/**
* Resolution identifier: days.
+ *
+ * @deprecated Use {@link Resolution#DAY}
*/
- public static final int RESOLUTION_DAY = 4;
+ @Deprecated
+ public static final Resolution RESOLUTION_DAY = Resolution.DAY;
/**
* Resolution identifier: months.
+ *
+ * @deprecated Use {@link Resolution#MONTH}
*/
- public static final int RESOLUTION_MONTH = 5;
+ @Deprecated
+ public static final Resolution RESOLUTION_MONTH = Resolution.MONTH;
/**
* Resolution identifier: years.
+ *
+ * @deprecated Use {@link Resolution#YEAR}
*/
- public static final int RESOLUTION_YEAR = 6;
-
- /**
- * Specified smallest modifiable unit.
- */
- private int resolution = RESOLUTION_MSEC;
+ @Deprecated
+ public static final Resolution RESOLUTION_YEAR = Resolution.YEAR;
/**
- * Specified largest modifiable unit.
+ * Specified smallest modifiable unit for the date field.
*/
- private static final int largestModifiable = RESOLUTION_YEAR;
+ private Resolution resolution = Resolution.DAY;
/**
* The internal calendar to be used in java.utl.Date conversions.
@@ -129,6 +204,16 @@ public class DateField extends AbstractField implements
private TimeZone timeZone = null;
+ private static Map<Resolution, String> variableNameForResolution = new HashMap<DateField.Resolution, String>();
+ {
+ variableNameForResolution.put(Resolution.SECOND, "sec");
+ variableNameForResolution.put(Resolution.MINUTE, "min");
+ variableNameForResolution.put(Resolution.HOUR, "hour");
+ variableNameForResolution.put(Resolution.DAY, "day");
+ variableNameForResolution.put(Resolution.MONTH, "month");
+ variableNameForResolution.put(Resolution.YEAR, "year");
+ }
+
/* Constructors */
/**
@@ -201,9 +286,7 @@ public class DateField extends AbstractField implements
* Paints this component. Don't add a JavaDoc comment here, we use the
* default documentation from implemented interface.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
// Adds the locale as attribute
final Locale l = getLocale();
@@ -228,51 +311,21 @@ public class DateField extends AbstractField implements
// Gets the calendar
final Calendar calendar = getCalendar();
- final Date currentDate = (Date) getValue();
-
- for (int r = resolution; r <= largestModifiable; r++) {
- switch (r) {
- case RESOLUTION_MSEC:
- target.addVariable(
- this,
- "msec",
- currentDate != null ? calendar
- .get(Calendar.MILLISECOND) : -1);
- break;
- case RESOLUTION_SEC:
- target.addVariable(this, "sec",
- currentDate != null ? calendar.get(Calendar.SECOND)
- : -1);
- break;
- case RESOLUTION_MIN:
- target.addVariable(this, "min",
- currentDate != null ? calendar.get(Calendar.MINUTE)
- : -1);
- break;
- case RESOLUTION_HOUR:
- target.addVariable(
- this,
- "hour",
- currentDate != null ? calendar
- .get(Calendar.HOUR_OF_DAY) : -1);
- break;
- case RESOLUTION_DAY:
- target.addVariable(
- this,
- "day",
- currentDate != null ? calendar
- .get(Calendar.DAY_OF_MONTH) : -1);
- break;
- case RESOLUTION_MONTH:
- target.addVariable(this, "month",
- currentDate != null ? calendar.get(Calendar.MONTH) + 1
- : -1);
- break;
- case RESOLUTION_YEAR:
- target.addVariable(this, "year",
- currentDate != null ? calendar.get(Calendar.YEAR) : -1);
- break;
+ final Date currentDate = getValue();
+
+ // Only paint variables for the resolution and up, e.g. Resolution DAY
+ // paints DAY,MONTH,YEAR
+ for (Resolution res : Resolution
+ .getResolutionsHigherOrEqualTo(resolution)) {
+ int value = -1;
+ if (currentDate != null) {
+ value = calendar.get(res.getCalendarField());
+ if (res == Resolution.MONTH) {
+ // Calendar month is zero based
+ value++;
+ }
}
+ target.addVariable(this, variableNameForResolution.get(res), value);
}
}
@@ -286,9 +339,7 @@ public class DateField extends AbstractField implements
* comment here, we use the default documentation from implemented
* interface.
*/
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
if (!isReadOnly()
&& (variables.containsKey("year")
@@ -298,10 +349,10 @@ public class DateField extends AbstractField implements
|| variables.containsKey("min")
|| variables.containsKey("sec")
|| variables.containsKey("msec") || variables
- .containsKey("dateString"))) {
+ .containsKey("dateString"))) {
// Old and new dates
- final Date oldDate = (Date) getValue();
+ final Date oldDate = getValue();
Date newDate = null;
// this enables analyzing invalid input on the server
@@ -309,59 +360,50 @@ public class DateField extends AbstractField implements
dateString = newDateString;
// Gets the new date in parts
- // Null values are converted to negative values.
- int year = variables.containsKey("year") ? (variables.get("year") == null ? -1
- : ((Integer) variables.get("year")).intValue())
- : -1;
- int month = variables.containsKey("month") ? (variables
- .get("month") == null ? -1 : ((Integer) variables
- .get("month")).intValue() - 1) : -1;
- int day = variables.containsKey("day") ? (variables.get("day") == null ? -1
- : ((Integer) variables.get("day")).intValue())
- : -1;
- int hour = variables.containsKey("hour") ? (variables.get("hour") == null ? -1
- : ((Integer) variables.get("hour")).intValue())
- : -1;
- int min = variables.containsKey("min") ? (variables.get("min") == null ? -1
- : ((Integer) variables.get("min")).intValue())
- : -1;
- int sec = variables.containsKey("sec") ? (variables.get("sec") == null ? -1
- : ((Integer) variables.get("sec")).intValue())
- : -1;
- int msec = variables.containsKey("msec") ? (variables.get("msec") == null ? -1
- : ((Integer) variables.get("msec")).intValue())
- : -1;
-
- // If all of the components is < 0 use the previous value
- if (year < 0 && month < 0 && day < 0 && hour < 0 && min < 0
- && sec < 0 && msec < 0) {
+ boolean hasChanges = false;
+ Map<Resolution, Integer> calendarFieldChanges = new HashMap<DateField.Resolution, Integer>();
+
+ for (Resolution r : Resolution
+ .getResolutionsHigherOrEqualTo(resolution)) {
+ // Only handle what the client is allowed to send. The same
+ // resolutions that are painted
+ String variableName = variableNameForResolution.get(r);
+
+ if (variables.containsKey(variableName)) {
+ Integer value = (Integer) variables.get(variableName);
+ if (r == Resolution.MONTH) {
+ // Calendar MONTH is zero based
+ value--;
+ }
+ if (value >= 0) {
+ hasChanges = true;
+ calendarFieldChanges.put(r, value);
+ }
+ }
+ }
+
+ // If no new variable values were received, use the previous value
+ if (!hasChanges) {
newDate = null;
} else {
-
// Clone the calendar for date operation
final Calendar cal = getCalendar();
- // Make sure that meaningful values exists
- // Use the previous value if some of the variables
- // have not been changed.
- year = year < 0 ? cal.get(Calendar.YEAR) : year;
- month = month < 0 ? cal.get(Calendar.MONTH) : month;
- day = day < 0 ? cal.get(Calendar.DAY_OF_MONTH) : day;
- hour = hour < 0 ? cal.get(Calendar.HOUR_OF_DAY) : hour;
- min = min < 0 ? cal.get(Calendar.MINUTE) : min;
- sec = sec < 0 ? cal.get(Calendar.SECOND) : sec;
- msec = msec < 0 ? cal.get(Calendar.MILLISECOND) : msec;
-
- // Sets the calendar fields
- cal.set(Calendar.YEAR, year);
- cal.set(Calendar.MONTH, month);
- cal.set(Calendar.DAY_OF_MONTH, day);
- cal.set(Calendar.HOUR_OF_DAY, hour);
- cal.set(Calendar.MINUTE, min);
- cal.set(Calendar.SECOND, sec);
- cal.set(Calendar.MILLISECOND, msec);
-
- // Assigns the date
+ // Update the value based on the received info
+ // Must set in this order to avoid invalid dates (or wrong
+ // dates if lenient is true) in calendar
+ for (int r = Resolution.YEAR.ordinal(); r >= 0; r--) {
+ Resolution res = Resolution.values()[r];
+ if (calendarFieldChanges.containsKey(res)) {
+
+ // Field resolution should be included. Others are
+ // skipped so that client can not make unexpected
+ // changes (e.g. day change even though resolution is
+ // year).
+ Integer newValue = calendarFieldChanges.get(res);
+ cal.set(res.getCalendarField(), newValue);
+ }
+ }
newDate = cal.getTime();
}
@@ -377,7 +419,7 @@ public class DateField extends AbstractField implements
* this case the invalid text remains in the DateField.
*/
requestRepaint();
- } catch (ConversionException e) {
+ } catch (Converter.ConversionException e) {
/*
* Datefield now contains some text that could't be parsed
@@ -445,7 +487,7 @@ public class DateField extends AbstractField implements
* This method is called to handle a non-empty date string from the client
* if the client could not parse it as a Date.
*
- * By default, a Property.ConversionException is thrown, and the current
+ * By default, a Converter.ConversionException is thrown, and the current
* value is not modified.
*
* This can be overridden to handle conversions, to return null (equivalent
@@ -453,13 +495,13 @@ public class DateField extends AbstractField implements
*
* @param dateString
* @return parsed Date
- * @throws Property.ConversionException
+ * @throws Converter.ConversionException
* to keep the old value and indicate an error
*/
protected Date handleUnparsableDateString(String dateString)
- throws Property.ConversionException {
+ throws Converter.ConversionException {
currentParseErrorMessage = null;
- throw new Property.ConversionException(getParseErrorMessage());
+ throw new Converter.ConversionException(getParseErrorMessage());
}
/* Property features */
@@ -469,7 +511,7 @@ public class DateField extends AbstractField implements
* the default documentation from implemented interface.
*/
@Override
- public Class<?> getType() {
+ public Class<Date> getType() {
return Date.class;
}
@@ -479,8 +521,8 @@ public class DateField extends AbstractField implements
* @see com.vaadin.ui.AbstractField#setValue(java.lang.Object, boolean)
*/
@Override
- protected void setValue(Object newValue, boolean repaintIsNotNeeded)
- throws Property.ReadOnlyException, Property.ConversionException {
+ protected void setValue(Date newValue, boolean repaintIsNotNeeded)
+ throws Property.ReadOnlyException {
/*
* First handle special case when the client side component have a date
@@ -513,23 +555,7 @@ public class DateField extends AbstractField implements
return;
}
- if (newValue == null || newValue instanceof Date) {
- super.setValue(newValue, repaintIsNotNeeded);
- } else {
- // Try to parse the given string value to Date
- try {
- final SimpleDateFormat parser = new SimpleDateFormat();
- final TimeZone currentTimeZone = getTimeZone();
- if (currentTimeZone != null) {
- parser.setTimeZone(currentTimeZone);
- }
- final Date val = parser.parse(newValue.toString());
- super.setValue(val, repaintIsNotNeeded);
- } catch (final ParseException e) {
- uiHasValidDateString = false;
- throw new Property.ConversionException(getParseErrorMessage());
- }
- }
+ super.setValue(newValue, repaintIsNotNeeded);
}
/**
@@ -544,7 +570,7 @@ public class DateField extends AbstractField implements
Form f = (Form) parenOfDateField;
Collection<?> visibleItemProperties = f.getItemPropertyIds();
for (Object fieldId : visibleItemProperties) {
- Field field = f.getField(fieldId);
+ Field<?> field = f.getField(fieldId);
if (field == this) {
/*
* this datefield is logically in a form. Do the same
@@ -564,24 +590,8 @@ public class DateField extends AbstractField implements
}
}
- /**
- * Sets the DateField datasource. Datasource type must assignable to Date.
- *
- * @see com.vaadin.data.Property.Viewer#setPropertyDataSource(Property)
- */
@Override
- public void setPropertyDataSource(Property newDataSource) {
- if (newDataSource == null
- || Date.class.isAssignableFrom(newDataSource.getType())) {
- super.setPropertyDataSource(newDataSource);
- } else {
- throw new IllegalArgumentException(
- "DateField only supports Date properties");
- }
- }
-
- @Override
- protected void setInternalValue(Object newValue) {
+ protected void setInternalValue(Date newValue) {
// Also set the internal dateString
if (newValue != null) {
dateString = newValue.toString();
@@ -604,17 +614,19 @@ public class DateField extends AbstractField implements
*
* @return int
*/
- public int getResolution() {
+ public Resolution getResolution() {
return resolution;
}
/**
* Sets the resolution of the DateField.
*
+ * The default resolution is {@link Resolution#DAY} since Vaadin 7.0.
+ *
* @param resolution
* the resolution to set.
*/
- public void setResolution(int resolution) {
+ public void setResolution(Resolution resolution) {
this.resolution = resolution;
requestRepaint();
}
@@ -636,13 +648,19 @@ public class DateField extends AbstractField implements
// Makes sure we have an calendar instance
if (calendar == null) {
calendar = Calendar.getInstance();
+ // Start by a zeroed calendar to avoid having values for lower
+ // resolution variables e.g. time when resolution is day
+ for (Resolution r : Resolution.getResolutionsLowerThan(resolution)) {
+ calendar.set(r.getCalendarField(), 0);
+ }
+ calendar.set(Calendar.MILLISECOND, 0);
}
// Clone the instance
final Calendar newCal = (Calendar) calendar.clone();
// Assigns the current time tom calendar.
- final Date currentDate = (Date) getValue();
+ final Date currentDate = getValue();
if (currentDate != null) {
newCal.setTime(currentDate);
}
@@ -752,19 +770,14 @@ public class DateField extends AbstractField implements
}
/**
- * Tests the current value against registered validators if the field is not
- * empty. Note that DateField is considered empty (value == null) and
+ * Validates the current value against registered validators if the field is
+ * not empty. Note that DateField is considered empty (value == null) and
* invalid if it contains text typed in by the user that couldn't be parsed
* into a Date value.
*
- * @see com.vaadin.ui.AbstractField#isValid()
+ * @see com.vaadin.ui.AbstractField#validate()
*/
@Override
- public boolean isValid() {
- return uiHasValidDateString && super.isValid();
- }
-
- @Override
public void validate() throws InvalidValueException {
/*
* To work properly in form we must throw exception if there is
diff --git a/src/com/vaadin/ui/DefaultFieldFactory.java b/src/com/vaadin/ui/DefaultFieldFactory.java
index 1e55d2795f..9d096094e3 100644
--- a/src/com/vaadin/ui/DefaultFieldFactory.java
+++ b/src/com/vaadin/ui/DefaultFieldFactory.java
@@ -35,19 +35,20 @@ public class DefaultFieldFactory implements FormFieldFactory, TableFieldFactory
protected DefaultFieldFactory() {
}
- public Field createField(Item item, Object propertyId, Component uiContext) {
+ public Field<?> createField(Item item, Object propertyId,
+ Component uiContext) {
Class<?> type = item.getItemProperty(propertyId).getType();
- Field field = createFieldByPropertyType(type);
+ Field<?> field = createFieldByPropertyType(type);
field.setCaption(createCaptionByPropertyId(propertyId));
return field;
}
- public Field createField(Container container, Object itemId,
+ public Field<?> createField(Container container, Object itemId,
Object propertyId, Component uiContext) {
- Property containerProperty = container.getContainerProperty(itemId,
+ Property<?> containerProperty = container.getContainerProperty(itemId,
propertyId);
Class<?> type = containerProperty.getType();
- Field field = createFieldByPropertyType(type);
+ Field<?> field = createFieldByPropertyType(type);
field.setCaption(createCaptionByPropertyId(propertyId));
return field;
}
@@ -63,6 +64,10 @@ public class DefaultFieldFactory implements FormFieldFactory, TableFieldFactory
String name = propertyId.toString();
if (name.length() > 0) {
+ int dotLocation = name.lastIndexOf('.');
+ if (dotLocation > 0 && dotLocation < name.length() - 1) {
+ name = name.substring(dotLocation + 1);
+ }
if (name.indexOf(' ') < 0
&& name.charAt(0) == Character.toLowerCase(name.charAt(0))
&& name.charAt(0) != Character.toUpperCase(name.charAt(0))) {
@@ -110,7 +115,7 @@ public class DefaultFieldFactory implements FormFieldFactory, TableFieldFactory
* the type of the property
* @return the most suitable generic {@link Field} for given type
*/
- public static Field createFieldByPropertyType(Class<?> type) {
+ public static Field<?> createFieldByPropertyType(Class<?> type) {
// Null typed properties can not be edited
if (type == null) {
return null;
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
new file mode 100644
index 0000000000..84df7e7c7c
--- /dev/null
+++ b/src/com/vaadin/ui/DirtyConnectorTracker.java
@@ -0,0 +1,132 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.terminal.gwt.server.AbstractCommunicationManager;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+import com.vaadin.ui.Component.RepaintRequestEvent;
+import com.vaadin.ui.Component.RepaintRequestListener;
+
+/**
+ * A class that tracks dirty {@link ClientConnector}s. A {@link ClientConnector}
+ * is dirty when an operation has been performed on it on the server and as a
+ * result of this operation new information needs to be sent to its client side
+ * counterpart.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public class DirtyConnectorTracker implements RepaintRequestListener {
+ private Set<Component> dirtyComponents = new HashSet<Component>();
+ private Root root;
+
+ /**
+ * Gets a logger for this class
+ *
+ * @return A logger instance for logging within this class
+ *
+ */
+ public static Logger getLogger() {
+ return Logger.getLogger(DirtyConnectorTracker.class.getName());
+ }
+
+ public DirtyConnectorTracker(Root root) {
+ this.root = root;
+ }
+
+ public void repaintRequested(RepaintRequestEvent event) {
+ markDirty((Component) event.getConnector());
+ }
+
+ public void componentAttached(Component component) {
+ component.addListener(this);
+ markDirty(component);
+ }
+
+ private void markDirty(Component component) {
+ if (getLogger().isLoggable(Level.FINE)) {
+ if (!dirtyComponents.contains(component)) {
+ getLogger()
+ .fine(getDebugInfo(component) + " " + "is now dirty");
+ }
+ }
+
+ dirtyComponents.add(component);
+ }
+
+ private void markClean(Component component) {
+ if (getLogger().isLoggable(Level.FINE)) {
+ if (dirtyComponents.contains(component)) {
+ getLogger().fine(
+ getDebugInfo(component) + " " + "is no longer dirty");
+ }
+ }
+
+ dirtyComponents.remove(component);
+ }
+
+ private String getDebugInfo(Component component) {
+ String message = getObjectString(component);
+ if (component.getParent() != null) {
+ message += " (parent: " + getObjectString(component.getParent())
+ + ")";
+ }
+ return message;
+ }
+
+ private String getObjectString(Object component) {
+ return component.getClass().getName() + "@"
+ + Integer.toHexString(component.hashCode());
+ }
+
+ public void componentDetached(Component component) {
+ component.removeListener(this);
+ markClean(component);
+ }
+
+ public void markAllComponentsDirty() {
+ markComponentsDirtyRecursively(root);
+ getLogger().fine("All components are now dirty");
+ }
+
+ public void markAllComponentsClean() {
+ dirtyComponents.clear();
+ getLogger().fine("All components are now clean");
+ }
+
+ /**
+ * Marks all visible components dirty, starting from the given component and
+ * going downwards in the hierarchy.
+ *
+ * @param c
+ * The component to start iterating downwards from
+ */
+ private void markComponentsDirtyRecursively(Component c) {
+ if (!c.isVisible()) {
+ return;
+ }
+ markDirty(c);
+ if (c instanceof HasComponents) {
+ HasComponents container = (HasComponents) c;
+ for (Component child : AbstractCommunicationManager
+ .getChildComponents(container)) {
+ markComponentsDirtyRecursively(child);
+ }
+ }
+
+ }
+
+ public Collection<Component> getDirtyComponents() {
+ return dirtyComponents;
+ }
+
+}
diff --git a/src/com/vaadin/ui/DragAndDropWrapper.java b/src/com/vaadin/ui/DragAndDropWrapper.java
index c6522f15c7..b623197a4c 100644
--- a/src/com/vaadin/ui/DragAndDropWrapper.java
+++ b/src/com/vaadin/ui/DragAndDropWrapper.java
@@ -20,15 +20,15 @@ import com.vaadin.event.dd.TargetDetailsImpl;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.StreamVariable;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper;
import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper;
@SuppressWarnings("serial")
-@ClientWidget(VDragAndDropWrapper.class)
public class DragAndDropWrapper extends CustomComponent implements DropTarget,
- DragSource {
+ DragSource, Vaadin6Component {
public class WrapperTransferable extends TransferableImpl {
@@ -214,9 +214,11 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
requestRepaint();
}
- @Override
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
+
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
target.addAttribute(VDragAndDropWrapper.DRAG_START_MODE,
dragStartMode.ordinal());
if (getDropHandler() != null) {
diff --git a/src/com/vaadin/ui/Embedded.java b/src/com/vaadin/ui/Embedded.java
index 181cbbfb96..1bcd984666 100644
--- a/src/com/vaadin/ui/Embedded.java
+++ b/src/com/vaadin/ui/Embedded.java
@@ -13,8 +13,11 @@ import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VEmbedded;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.embedded.EmbeddedConnector;
+import com.vaadin.terminal.gwt.client.ui.embedded.EmbeddedServerRpc;
/**
* Component for embedding external objects.
@@ -25,10 +28,7 @@ import com.vaadin.terminal.gwt.client.ui.VEmbedded;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VEmbedded.class)
-public class Embedded extends AbstractComponent {
-
- private static final String CLICK_EVENT = VEmbedded.CLICK_EVENT_IDENTIFIER;
+public class Embedded extends AbstractComponent implements Vaadin6Component {
/**
* General object type.
@@ -80,10 +80,17 @@ public class Embedded extends AbstractComponent {
private String altText;
+ private EmbeddedServerRpc rpc = new EmbeddedServerRpc() {
+ public void click(MouseEventDetails mouseDetails) {
+ fireEvent(new ClickEvent(Embedded.this, mouseDetails));
+ }
+ };
+
/**
* Creates a new empty Embedded object.
*/
public Embedded() {
+ registerRpc(rpc);
}
/**
@@ -92,6 +99,7 @@ public class Embedded extends AbstractComponent {
* @param caption
*/
public Embedded(String caption) {
+ this();
setCaption(caption);
}
@@ -105,14 +113,13 @@ public class Embedded extends AbstractComponent {
* the Source of the embedded object.
*/
public Embedded(String caption, Resource source) {
- setCaption(caption);
+ this(caption);
setSource(source);
}
/**
* Invoked when the component state should be painted.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
switch (type) {
@@ -149,7 +156,7 @@ public class Embedded extends AbstractComponent {
target.addAttribute("archive", archive);
}
if (altText != null && !"".equals(altText)) {
- target.addAttribute(VEmbedded.ALTERNATE_TEXT, altText);
+ target.addAttribute(EmbeddedConnector.ALTERNATE_TEXT, altText);
}
// Params
@@ -498,8 +505,8 @@ public class Embedded extends AbstractComponent {
* The listener to add
*/
public void addListener(ClickListener listener) {
- addListener(CLICK_EVENT, ClickEvent.class, listener,
- ClickListener.clickMethod);
+ addListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
+ listener, ClickListener.clickMethod);
}
/**
@@ -510,35 +517,12 @@ public class Embedded extends AbstractComponent {
* The listener to remove
*/
public void removeListener(ClickListener listener) {
- removeListener(CLICK_EVENT, ClickEvent.class, listener);
+ removeListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER,
+ ClickEvent.class, listener);
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
- * java.util.Map)
- */
- @SuppressWarnings("unchecked")
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
- if (variables.containsKey(CLICK_EVENT)) {
- fireClick((Map<String, Object>) variables.get(CLICK_EVENT));
- }
-
- }
-
- /**
- * Notifies click-listeners that a mouse click event has occurred.
- *
- * @param parameters
- */
- private void fireClick(Map<String, Object> parameters) {
- MouseEventDetails mouseDetails = MouseEventDetails
- .deSerialize((String) parameters.get("mouseDetails"));
-
- fireEvent(new ClickEvent(this, mouseDetails));
+ // TODO Remove once Vaadin6Component is no longer implemented
}
}
diff --git a/src/com/vaadin/ui/ExpandLayout.java b/src/com/vaadin/ui/ExpandLayout.java
deleted file mode 100644
index 55ee2ffdcf..0000000000
--- a/src/com/vaadin/ui/ExpandLayout.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.ui;
-
-/**
- * A layout that will give one of it's components as much space as possible,
- * while still showing the other components in the layout. The other components
- * will in effect be given a fixed sized space, while the space given to the
- * expanded component will grow/shrink to fill the rest of the space available -
- * for instance when re-sizing the window.
- *
- * Note that this layout is 100% in both directions by default ({link
- * {@link #setSizeFull()}). Remember to set the units if you want to specify a
- * fixed size. If the layout fails to show up, check that the parent layout is
- * actually giving some space.
- *
- * @deprecated Deprecated in favor of the new OrderedLayout
- */
-@SuppressWarnings("serial")
-@Deprecated
-public class ExpandLayout extends OrderedLayout {
-
- private Component expanded = null;
-
- public ExpandLayout() {
- this(ORIENTATION_VERTICAL);
- }
-
- public ExpandLayout(int orientation) {
- super(orientation);
-
- setSizeFull();
- }
-
- /**
- * @param c
- * Component which container will be maximized
- */
- public void expand(Component c) {
- if (expanded != null) {
- try {
- setExpandRatio(expanded, 0.0f);
- } catch (IllegalArgumentException e) {
- // Ignore error if component has been removed
- }
- }
-
- expanded = c;
- if (expanded != null) {
- setExpandRatio(expanded, 1.0f);
- }
-
- requestRepaint();
- }
-
- @Override
- public void addComponent(Component c, int index) {
- super.addComponent(c, index);
- if (expanded == null) {
- expand(c);
- }
- }
-
- @Override
- public void addComponent(Component c) {
- super.addComponent(c);
- if (expanded == null) {
- expand(c);
- }
- }
-
- @Override
- public void addComponentAsFirst(Component c) {
- super.addComponentAsFirst(c);
- if (expanded == null) {
- expand(c);
- }
- }
-
- @Override
- public void removeComponent(Component c) {
- super.removeComponent(c);
- if (c == expanded) {
- if (getComponentIterator().hasNext()) {
- expand(getComponentIterator().next());
- } else {
- expand(null);
- }
- }
- }
-
- @Override
- public void replaceComponent(Component oldComponent, Component newComponent) {
- super.replaceComponent(oldComponent, newComponent);
- if (oldComponent == expanded) {
- expand(newComponent);
- }
- }
-}
diff --git a/src/com/vaadin/ui/Field.java b/src/com/vaadin/ui/Field.java
index 0cd6cd2d87..3a66db47b0 100644
--- a/src/com/vaadin/ui/Field.java
+++ b/src/com/vaadin/ui/Field.java
@@ -9,30 +9,21 @@ import com.vaadin.data.Property;
import com.vaadin.ui.Component.Focusable;
/**
+ * TODO document
+ *
* @author Vaadin Ltd.
*
+ * @param T
+ * the type of values in the field, which might not be the same type
+ * as that of the data source if converters are used
+ *
+ * @author IT Mill Ltd.
*/
-public interface Field extends Component, BufferedValidatable, Property,
+public interface Field<T> extends Component, BufferedValidatable, Property<T>,
Property.ValueChangeNotifier, Property.ValueChangeListener,
Property.Editor, Focusable {
/**
- * Sets the Caption.
- *
- * @param caption
- */
- void setCaption(String caption);
-
- String getDescription();
-
- /**
- * Sets the Description.
- *
- * @param caption
- */
- void setDescription(String caption);
-
- /**
* Is this field required.
*
* Required fields must filled by the user.
@@ -80,7 +71,7 @@ public interface Field extends Component, BufferedValidatable, Property,
* @since 3.0
*/
@SuppressWarnings("serial")
- public class ValueChangeEvent extends Component.Event implements
+ public static class ValueChangeEvent extends Component.Event implements
Property.ValueChangeEvent {
/**
diff --git a/src/com/vaadin/ui/FieldFactory.java b/src/com/vaadin/ui/FieldFactory.java
deleted file mode 100644
index d18918640e..0000000000
--- a/src/com/vaadin/ui/FieldFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.ui;
-
-import com.vaadin.data.Property;
-
-/**
- * Factory for creating new Field-instances based on type, datasource and/or
- * context.
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.1
- * @deprecated FieldFactory was split into two lighter interfaces in 6.0 Use
- * FormFieldFactory or TableFieldFactory or both instead.
- */
-@Deprecated
-public interface FieldFactory extends FormFieldFactory, TableFieldFactory {
-
- /**
- * Creates a field based on type of data.
- *
- * @param type
- * the type of data presented in field.
- * @param uiContext
- * the component where the field is presented.
- * @return Field the field suitable for editing the specified data.
- *
- */
- Field createField(Class<?> type, Component uiContext);
-
- /**
- * Creates a field based on the property datasource.
- *
- * @param property
- * the property datasource.
- * @param uiContext
- * the component where the field is presented.
- * @return Field the field suitable for editing the specified data.
- */
- Field createField(Property property, Component uiContext);
-
-} \ No newline at end of file
diff --git a/src/com/vaadin/ui/Form.java b/src/com/vaadin/ui/Form.java
index c3bb725edf..5f5516b21f 100644
--- a/src/com/vaadin/ui/Form.java
+++ b/src/com/vaadin/ui/Form.java
@@ -4,6 +4,7 @@
package com.vaadin.ui;
+import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -17,16 +18,20 @@ import com.vaadin.data.Property;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.util.BeanItem;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.Action.ShortcutNotifier;
import com.vaadin.event.ActionManager;
+import com.vaadin.terminal.AbstractErrorMessage;
import com.vaadin.terminal.CompositeErrorMessage;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VForm;
+import com.vaadin.terminal.UserError;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.form.FormState;
/**
* Form component provides easy way of creating and managing sets fields.
@@ -58,20 +63,17 @@ import com.vaadin.terminal.gwt.client.ui.VForm;
* @version
* @VERSION@
* @since 3.0
+ * @deprecated Use {@link FieldGroup} instead of {@link Form} for more
+ * flexibility.
*/
-@SuppressWarnings("serial")
-@ClientWidget(VForm.class)
-public class Form extends AbstractField implements Item.Editor, Buffered, Item,
- Validatable, Action.Notifier {
+@Deprecated
+public class Form extends AbstractField<Object> implements Item.Editor,
+ Buffered, Item, Validatable, Action.Notifier, HasComponents,
+ Vaadin6Component {
private Object propertyValue;
/**
- * Layout of the form.
- */
- private Layout layout;
-
- /**
* Item connected to this form as datasource.
*/
private Item itemDatasource;
@@ -99,12 +101,12 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
/**
* Mapping from propertyName to corresponding field.
*/
- private final HashMap<Object, Field> fields = new HashMap<Object, Field>();
+ private final HashMap<Object, Field<?>> fields = new HashMap<Object, Field<?>>();
/**
* Form may act as an Item, its own properties are stored here.
*/
- private final HashMap<Object, Property> ownProperties = new HashMap<Object, Property>();
+ private final HashMap<Object, Property<?>> ownProperties = new HashMap<Object, Property<?>>();
/**
* Field factory for this form.
@@ -129,8 +131,6 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
};
- private Layout formFooter;
-
/**
* If this is true, commit implicitly calls setValidationVisible(true).
*/
@@ -182,30 +182,25 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
public Form(Layout formLayout, FormFieldFactory fieldFactory) {
super();
setLayout(formLayout);
+ setFooter(null);
setFormFieldFactory(fieldFactory);
setValidationVisible(false);
setWidth(100, UNITS_PERCENTAGE);
}
- /* Documented in interface */
@Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- layout.paint(target);
- if (formFooter != null) {
- formFooter.paint(target);
- }
+ public FormState getState() {
+ return (FormState) super.getState();
+ }
+ /* Documented in interface */
+ public void paintContent(PaintTarget target) throws PaintException {
if (ownActionManager != null) {
ownActionManager.paintActions(null, target);
}
}
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
-
// Actions
if (ownActionManager != null) {
ownActionManager.handleActions(variables, this);
@@ -238,15 +233,13 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
if (validationError != null) {
// Show caption as error for fields with empty errors
if ("".equals(validationError.toString())) {
- validationError = new Validator.InvalidValueException(
- field.getCaption());
+ validationError = new UserError(field.getCaption());
}
break;
- } else if (f instanceof Field && !((Field) f).isValid()) {
+ } else if (f instanceof Field && !((Field<?>) f).isValid()) {
// Something is wrong with the field, but no proper
// error is given. Generate one.
- validationError = new Validator.InvalidValueException(
- field.getCaption());
+ validationError = new UserError(field.getCaption());
break;
}
}
@@ -260,9 +253,12 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
// Throw combination of the error types
- return new CompositeErrorMessage(new ErrorMessage[] {
- getComponentError(), validationError,
- currentBufferedSourceException });
+ return new CompositeErrorMessage(
+ new ErrorMessage[] {
+ getComponentError(),
+ validationError,
+ AbstractErrorMessage
+ .getErrorMessageForException(currentBufferedSourceException) });
}
/**
@@ -321,7 +317,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
// Try to commit all
for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
try {
- final Field f = (fields.get(i.next()));
+ final Field<?> f = (fields.get(i.next()));
// Commit only non-readonly fields.
if (!f.isReadOnly()) {
f.commit();
@@ -408,7 +404,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
@Override
public boolean isModified() {
for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
- final Field f = fields.get(i.next());
+ final Field<?> f = fields.get(i.next());
if (f != null && f.isModified()) {
return true;
}
@@ -422,6 +418,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* we use the default one from the interface.
*/
@Override
+ @Deprecated
public boolean isReadThrough() {
return readThrough;
}
@@ -431,6 +428,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* we use the default one from the interface.
*/
@Override
+ @Deprecated
public boolean isWriteThrough() {
return writeThrough;
}
@@ -485,7 +483,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
ownProperties.put(id, property);
// Gets suitable field
- final Field field = fieldFactory.createField(this, id, this);
+ final Field<?> field = fieldFactory.createField(this, id, this);
if (field == null) {
return false;
}
@@ -516,7 +514,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* @param field
* the field which should be added to the form.
*/
- public void addField(Object propertyId, Field field) {
+ public void addField(Object propertyId, Field<?> field) {
registerField(propertyId, field);
attachField(propertyId, field);
requestRepaint();
@@ -536,7 +534,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* @param field
* the Field that should be registered
*/
- private void registerField(Object propertyId, Field field) {
+ private void registerField(Object propertyId, Field<?> field) {
if (propertyId == null || field == null) {
return;
}
@@ -580,6 +578,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
return;
}
+ Layout layout = getLayout();
if (layout instanceof CustomLayout) {
((CustomLayout) layout).addComponent(field, propertyId.toString());
} else {
@@ -599,13 +598,13 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
*
* @see com.vaadin.data.Item#getItemProperty(Object)
*/
- public Property getItemProperty(Object id) {
- final Field field = fields.get(id);
+ public Property<?> getItemProperty(Object id) {
+ final Field<?> field = fields.get(id);
if (field == null) {
// field does not exist or it is not (yet) created for this property
return ownProperties.get(id);
}
- final Property property = field.getPropertyDataSource();
+ final Property<?> property = field.getPropertyDataSource();
if (property != null) {
return property;
@@ -620,7 +619,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* @param propertyId
* the id of the property.
*/
- public Field getField(Object propertyId) {
+ public Field<?> getField(Object propertyId) {
return fields.get(propertyId);
}
@@ -637,7 +636,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
public boolean removeItemProperty(Object id) {
ownProperties.remove(id);
- final Field field = fields.get(id);
+ final Field<?> field = fields.get(id);
if (field != null) {
propertyIds.remove(id);
@@ -722,8 +721,8 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
*/
public void setItemDataSource(Item newDataSource, Collection<?> propertyIds) {
- if (layout instanceof GridLayout) {
- GridLayout gl = (GridLayout) layout;
+ if (getLayout() instanceof GridLayout) {
+ GridLayout gl = (GridLayout) getLayout();
if (gridlayoutCursorX == -1) {
// first setItemDataSource, remember initial cursor
gridlayoutCursorX = gl.getCursorX();
@@ -750,9 +749,9 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
// Adds all the properties to this form
for (final Iterator<?> i = propertyIds.iterator(); i.hasNext();) {
final Object id = i.next();
- final Property property = itemDatasource.getItemProperty(id);
+ final Property<?> property = itemDatasource.getItemProperty(id);
if (id != null && property != null) {
- final Field f = fieldFactory.createField(itemDatasource, id,
+ final Field<?> f = fieldFactory.createField(itemDatasource, id,
this);
if (f != null) {
bindPropertyToField(id, property, f);
@@ -799,25 +798,24 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* @return the Layout of the form.
*/
public Layout getLayout() {
- return layout;
+ return (Layout) getState().getLayout();
}
/**
* Sets the layout of the form.
*
* <p>
- * By default form uses <code>OrderedLayout</code> with <code>form</code>
- * -style.
+ * If set to null then Form uses a FormLayout by default.
* </p>
*
- * @param newLayout
- * the Layout of the form.
+ * @param layout
+ * the layout of the form.
*/
- public void setLayout(Layout newLayout) {
+ public void setLayout(Layout layout) {
// Use orderedlayout by default
- if (newLayout == null) {
- newLayout = new FormLayout();
+ if (layout == null) {
+ layout = new FormLayout();
}
// reset cursor memory
@@ -825,26 +823,29 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
gridlayoutCursorY = -1;
// Move fields from previous layout
- if (layout != null) {
+ if (getLayout() != null) {
final Object[] properties = propertyIds.toArray();
for (int i = 0; i < properties.length; i++) {
- Field f = getField(properties[i]);
+ Field<?> f = getField(properties[i]);
detachField(f);
- if (newLayout instanceof CustomLayout) {
- ((CustomLayout) newLayout).addComponent(f,
+ if (layout instanceof CustomLayout) {
+ ((CustomLayout) layout).addComponent(f,
properties[i].toString());
} else {
- newLayout.addComponent(f);
+ layout.addComponent(f);
}
}
- layout.setParent(null);
+ getLayout().setParent(null);
}
// Replace the previous layout
- newLayout.setParent(this);
- layout = newLayout;
+ layout.setParent(this);
+ getState().setLayout(layout);
+ // Hierarchy has changed so we need to repaint (this could be a
+ // hierarchy repaint only)
+ requestRepaint();
}
/**
@@ -856,13 +857,16 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* match. Null values are not supported.
* </p>
*
+ * Note: since Vaadin 7.0, returns an {@link AbstractSelect} instead of a
+ * {@link Select}.
+ *
* @param propertyId
* the id of the property.
* @param values
* @param descriptions
* @return the select property generated
*/
- public Select replaceWithSelect(Object propertyId, Object[] values,
+ public AbstractSelect replaceWithSelect(Object propertyId, Object[] values,
Object[] descriptions) {
// Checks the parameters
@@ -875,7 +879,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
// Gets the old field
- final Field oldField = fields.get(propertyId);
+ final Field<?> oldField = fields.get(propertyId);
if (oldField == null) {
throw new IllegalArgumentException("Field with given propertyid '"
+ propertyId.toString() + "' can not be found.");
@@ -922,10 +926,8 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
// Creates the new field matching to old field parameters
- final Select newField = new Select();
- if (isMultiselect) {
- newField.setMultiSelect(true);
- }
+ final AbstractSelect newField = isMultiselect ? new ListSelect()
+ : new Select();
newField.setCaption(oldField.getCaption());
newField.setReadOnly(oldField.isReadOnly());
newField.setReadThrough(oldField.isReadThrough());
@@ -952,12 +954,12 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
// Sets the property data source
- final Property property = oldField.getPropertyDataSource();
+ final Property<?> property = oldField.getPropertyDataSource();
oldField.setPropertyDataSource(null);
newField.setPropertyDataSource(property);
// Replaces the old field with new one
- layout.replaceComponent(oldField, newField);
+ getLayout().replaceComponent(oldField, newField);
fields.put(propertyId, newField);
newField.addListener(fieldValueChangeListener);
oldField.removeListener(fieldValueChangeListener);
@@ -973,9 +975,9 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
@Override
public void attach() {
super.attach();
- layout.attach();
- if (formFooter != null) {
- formFooter.attach();
+ getLayout().attach();
+ if (getFooter() != null) {
+ getFooter().attach();
}
}
@@ -987,28 +989,14 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
@Override
public void detach() {
super.detach();
- layout.detach();
- if (formFooter != null) {
- formFooter.detach();
+ getLayout().detach();
+ if (getFooter() != null) {
+ getFooter().detach();
}
}
/**
- * Tests the current value of the object against all registered validators
- *
- * @see com.vaadin.data.Validatable#isValid()
- */
- @Override
- public boolean isValid() {
- boolean valid = true;
- for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
- valid &= (fields.get(i.next())).isValid();
- }
- return valid && super.isValid();
- }
-
- /**
- * Checks the validity of the validatable.
+ * Checks the validity of the Form and all of its fields.
*
* @see com.vaadin.data.Validatable#validate()
*/
@@ -1055,23 +1043,6 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
/**
- * Sets the field factory of Form.
- *
- * <code>FieldFactory</code> is used to create fields for form properties.
- * By default the form uses BaseFieldFactory to create Field instances.
- *
- * @param fieldFactory
- * the New factory used to create the fields.
- * @see Field
- * @see FormFieldFactory
- * @deprecated use {@link #setFormFieldFactory(FormFieldFactory)} instead
- */
- @Deprecated
- public void setFieldFactory(FieldFactory fieldFactory) {
- this.fieldFactory = fieldFactory;
- }
-
- /**
* Sets the field factory used by this Form to genarate Fields for
* properties.
*
@@ -1097,23 +1068,6 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
/**
- * Get the field factory of the form.
- *
- * @return the FieldFactory Factory used to create the fields.
- * @deprecated Use {@link #getFormFieldFactory()} instead. Set the
- * FormFieldFactory using
- * {@link #setFormFieldFactory(FormFieldFactory)}.
- */
- @Deprecated
- public FieldFactory getFieldFactory() {
- if (fieldFactory instanceof FieldFactory) {
- return (FieldFactory) fieldFactory;
-
- }
- return null;
- }
-
- /**
* Gets the field type.
*
* @see com.vaadin.ui.AbstractField#getType()
@@ -1155,11 +1109,11 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
*
* @return the Field.
*/
- private Field getFirstFocusableField() {
+ private Field<?> getFirstFocusableField() {
if (getItemPropertyIds() != null) {
for (Object id : getItemPropertyIds()) {
if (id != null) {
- Field field = getField(id);
+ Field<?> field = getField(id);
if (field.isEnabled() && !field.isReadOnly()) {
return field;
}
@@ -1248,7 +1202,7 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
*/
@Override
public void focus() {
- final Field f = getFirstFocusableField();
+ final Field<?> f = getFirstFocusableField();
if (f != null) {
f.focus();
}
@@ -1274,8 +1228,8 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
@Override
public void setImmediate(boolean immediate) {
super.setImmediate(immediate);
- for (Iterator<Field> i = fields.values().iterator(); i.hasNext();) {
- Field f = i.next();
+ for (Iterator<Field<?>> i = fields.values().iterator(); i.hasNext();) {
+ Field<?> f = i.next();
if (f instanceof AbstractComponent) {
((AbstractComponent) f).setImmediate(immediate);
}
@@ -1286,10 +1240,10 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
@Override
protected boolean isEmpty() {
- for (Iterator<Field> i = fields.values().iterator(); i.hasNext();) {
- Field f = i.next();
+ for (Iterator<Field<?>> i = fields.values().iterator(); i.hasNext();) {
+ Field<?> f = i.next();
if (f instanceof AbstractField) {
- if (!((AbstractField) f).isEmpty()) {
+ if (!((AbstractField<?>) f).isEmpty()) {
return false;
}
}
@@ -1315,25 +1269,32 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
* @return layout rendered below normal form contents.
*/
public Layout getFooter() {
- if (formFooter == null) {
- formFooter = new HorizontalLayout();
- formFooter.setParent(this);
- }
- return formFooter;
+ return (Layout) getState().getFooter();
}
/**
- * Sets the layout that is rendered below normal form contens.
+ * Sets the layout that is rendered below normal form contents. Setting this
+ * to null will cause an empty HorizontalLayout to be rendered in the
+ * footer.
*
- * @param newFormFooter
- * the new Layout
+ * @param footer
+ * the new footer layout
*/
- public void setFooter(Layout newFormFooter) {
- if (formFooter != null) {
- formFooter.setParent(null);
+ public void setFooter(Layout footer) {
+ if (getFooter() != null) {
+ getFooter().setParent(null);
}
- formFooter = newFormFooter;
- formFooter.setParent(this);
+ if (footer == null) {
+ footer = new HorizontalLayout();
+ }
+
+ getState().setFooter(footer);
+ footer.setParent(this);
+
+ // Hierarchy has changed so we need to repaint (this could be a
+ // hierarchy repaint only)
+ requestRepaint();
+
}
@Override
@@ -1399,4 +1360,74 @@ public class Form extends AbstractField implements Item.Editor, Buffered, Item,
}
}
+ public Iterator<Component> iterator() {
+ return getComponentIterator();
+ }
+
+ /**
+ * Modifiable and Serializable Iterator for the components, used by
+ * {@link Form#getComponentIterator()}.
+ */
+ private class ComponentIterator implements Iterator<Component>,
+ Serializable {
+
+ int i = 0;
+
+ public boolean hasNext() {
+ if (i < getComponentCount()) {
+ return true;
+ }
+ return false;
+ }
+
+ public Component next() {
+ if (!hasNext()) {
+ return null;
+ }
+ i++;
+ if (i == 1) {
+ return getLayout() != null ? getLayout() : getFooter();
+ } else if (i == 2) {
+ return getFooter();
+ }
+ return null;
+ }
+
+ public void remove() {
+ if (i == 1) {
+ if (getLayout() != null) {
+ setLayout(null);
+ i = 0;
+ } else {
+ setFooter(null);
+ }
+ } else if (i == 2) {
+ setFooter(null);
+ }
+ }
+ }
+
+ public Iterator<Component> getComponentIterator() {
+ return new ComponentIterator();
+ }
+
+ public void requestRepaintAll() {
+ AbstractComponentContainer.requestRepaintAll(this);
+ }
+
+ public int getComponentCount() {
+ int count = 0;
+ if (getLayout() != null) {
+ count++;
+ }
+ if (getFooter() != null) {
+ count++;
+ }
+
+ return count;
+ }
+
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ };
}
diff --git a/src/com/vaadin/ui/FormFieldFactory.java b/src/com/vaadin/ui/FormFieldFactory.java
index 52ecfcd8c2..1efa05c5f5 100644
--- a/src/com/vaadin/ui/FormFieldFactory.java
+++ b/src/com/vaadin/ui/FormFieldFactory.java
@@ -37,5 +37,5 @@ public interface FormFieldFactory extends Serializable {
* creating it.
* @return Field the field suitable for editing the specified data.
*/
- Field createField(Item item, Object propertyId, Component uiContext);
+ Field<?> createField(Item item, Object propertyId, Component uiContext);
}
diff --git a/src/com/vaadin/ui/FormLayout.java b/src/com/vaadin/ui/FormLayout.java
index f61f5d544e..c0be784a7b 100644
--- a/src/com/vaadin/ui/FormLayout.java
+++ b/src/com/vaadin/ui/FormLayout.java
@@ -4,8 +4,6 @@
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VFormLayout;
-
/**
* FormLayout is used by {@link Form} to layout fields. It may also be used
* separately without {@link Form}.
@@ -21,14 +19,13 @@ import com.vaadin.terminal.gwt.client.ui.VFormLayout;
* bottom are by default on.
*
*/
-@SuppressWarnings({ "deprecation", "serial" })
-@ClientWidget(VFormLayout.class)
-public class FormLayout extends OrderedLayout {
+public class FormLayout extends AbstractOrderedLayout {
public FormLayout() {
super();
setSpacing(true);
setMargin(true, false, true, false);
+ setWidth(100, UNITS_PERCENTAGE);
}
}
diff --git a/src/com/vaadin/ui/GridLayout.java b/src/com/vaadin/ui/GridLayout.java
index 90122cddf9..0ab729ce5c 100644
--- a/src/com/vaadin/ui/GridLayout.java
+++ b/src/com/vaadin/ui/GridLayout.java
@@ -15,10 +15,15 @@ import java.util.Map.Entry;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
+import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.EventId;
-import com.vaadin.terminal.gwt.client.ui.VGridLayout;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.gridlayout.GridLayoutServerRpc;
+import com.vaadin.terminal.gwt.client.ui.gridlayout.GridLayoutState;
/**
* A layout where the components are laid out on a grid using cell coordinates.
@@ -47,22 +52,19 @@ import com.vaadin.terminal.gwt.client.ui.VGridLayout;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VGridLayout.class)
public class GridLayout extends AbstractLayout implements
- Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier {
+ Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier,
+ Vaadin6Component {
- private static final String CLICK_EVENT = EventId.LAYOUT_CLICK;
+ private GridLayoutServerRpc rpc = new GridLayoutServerRpc() {
- /**
- * Initial grid columns.
- */
- private int cols = 0;
-
- /**
- * Initial grid rows.
- */
- private int rows = 0;
+ public void layoutClick(MouseEventDetails mouseDetails,
+ Connector clickedConnector) {
+ fireEvent(LayoutClickEvent.createEvent(GridLayout.this,
+ mouseDetails, clickedConnector));
+ }
+ };
/**
* Cursor X position: this is where the next component with unspecified x,y
* is inserted
@@ -91,11 +93,6 @@ public class GridLayout extends AbstractLayout implements
*/
private Map<Component, Alignment> componentToAlignment = new HashMap<Component, Alignment>();
- /**
- * Is spacing between contained components enabled. Defaults to false.
- */
- private boolean spacing = false;
-
private static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT;
/**
@@ -121,6 +118,7 @@ public class GridLayout extends AbstractLayout implements
public GridLayout(int columns, int rows) {
setColumns(columns);
setRows(rows);
+ registerRpc(rpc);
}
/**
@@ -130,6 +128,11 @@ public class GridLayout extends AbstractLayout implements
this(1, 1);
}
+ @Override
+ public GridLayoutState getState() {
+ return (GridLayoutState) super.getState();
+ }
+
/**
* <p>
* Adds a component to the grid in the specified area. The area is defined
@@ -185,7 +188,8 @@ public class GridLayout extends AbstractLayout implements
throw new IllegalArgumentException(
"Illegal coordinates for the component");
}
- if (column1 < 0 || row1 < 0 || column2 >= cols || row2 >= rows) {
+ if (column1 < 0 || row1 < 0 || column2 >= getColumns()
+ || row2 >= getRows()) {
throw new OutOfBoundsException(area);
}
@@ -228,7 +232,7 @@ public class GridLayout extends AbstractLayout implements
&& cursorY <= row2) {
// cursor within area
cursorX = column2 + 1; // one right of area
- if (cursorX >= cols) {
+ if (cursorX >= getColumns()) {
// overflowed columns
cursorX = 0; // first col
// move one row down, or one row under the area
@@ -310,7 +314,7 @@ public class GridLayout extends AbstractLayout implements
*/
public void space() {
cursorX++;
- if (cursorX >= cols) {
+ if (cursorX >= getColumns()) {
cursorX = 0;
cursorY++;
}
@@ -342,8 +346,12 @@ public class GridLayout extends AbstractLayout implements
}
// Extends the grid if needed
- cols = cursorX >= cols ? cursorX + 1 : cols;
- rows = cursorY >= rows ? cursorY + 1 : rows;
+ if (cursorX >= getColumns()) {
+ setColumns(cursorX + 1);
+ }
+ if (cursorY >= getRows()) {
+ setRows(cursorY + 1);
+ }
addComponent(component, cursorX, cursorY);
}
@@ -423,6 +431,10 @@ public class GridLayout extends AbstractLayout implements
return components.size();
}
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
+
/**
* Paints the contents of this component.
*
@@ -431,22 +443,11 @@ public class GridLayout extends AbstractLayout implements
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
-
- super.paintContent(target);
-
// TODO refactor attribute names in future release.
- target.addAttribute("h", rows);
- target.addAttribute("w", cols);
-
target.addAttribute("structuralChange", structuralChange);
structuralChange = false;
- if (spacing) {
- target.addAttribute("spacing", spacing);
- }
-
// Area iterator
final Iterator<Area> areaiterator = areas.iterator();
@@ -460,22 +461,22 @@ public class GridLayout extends AbstractLayout implements
int emptyCells = 0;
final String[] alignmentsArray = new String[components.size()];
- final Integer[] columnExpandRatioArray = new Integer[cols];
- final Integer[] rowExpandRatioArray = new Integer[rows];
+ final Integer[] columnExpandRatioArray = new Integer[getColumns()];
+ final Integer[] rowExpandRatioArray = new Integer[getRows()];
int realColExpandRatioSum = 0;
float colSum = getExpandRatioSum(columnExpandRatio);
if (colSum == 0) {
// no columns has been expanded, all cols have same expand
// rate
- float equalSize = 1 / (float) cols;
+ float equalSize = 1 / (float) getColumns();
int myRatio = Math.round(equalSize * 1000);
- for (int i = 0; i < cols; i++) {
+ for (int i = 0; i < getColumns(); i++) {
columnExpandRatioArray[i] = myRatio;
}
- realColExpandRatioSum = myRatio * cols;
+ realColExpandRatioSum = myRatio * getColumns();
} else {
- for (int i = 0; i < cols; i++) {
+ for (int i = 0; i < getColumns(); i++) {
int myRatio = Math
.round((getColumnExpandRatio(i) / colSum) * 1000);
columnExpandRatioArray[i] = myRatio;
@@ -489,18 +490,18 @@ public class GridLayout extends AbstractLayout implements
if (rowSum == 0) {
// no rows have been expanded
equallyDividedRows = true;
- float equalSize = 1 / (float) rows;
+ float equalSize = 1 / (float) getRows();
int myRatio = Math.round(equalSize * 1000);
- for (int i = 0; i < rows; i++) {
+ for (int i = 0; i < getRows(); i++) {
rowExpandRatioArray[i] = myRatio;
}
- realRowExpandRatioSum = myRatio * rows;
+ realRowExpandRatioSum = myRatio * getRows();
}
int index = 0;
// Iterates every applicable row
- for (int cury = 0; cury < rows; cury++) {
+ for (int cury = 0; cury < getRows(); cury++) {
target.startTag("gr");
if (!equallyDividedRows) {
@@ -511,7 +512,7 @@ public class GridLayout extends AbstractLayout implements
}
// Iterates every applicable column
- for (int curx = 0; curx < cols; curx++) {
+ for (int curx = 0; curx < getColumns(); curx++) {
// Checks if current item is located at curx,cury
if (area != null && (area.row1 == cury)
@@ -543,7 +544,7 @@ public class GridLayout extends AbstractLayout implements
if (rows > 1) {
target.addAttribute("h", rows);
}
- area.getComponent().paint(target);
+ LegacyPaint.paint(area.getComponent(), target);
alignmentsArray[index++] = String
.valueOf(getComponentAlignment(area.getComponent())
@@ -621,7 +622,7 @@ public class GridLayout extends AbstractLayout implements
// Checks if empty cell needs to be rendered
if (emptyCells > 0) {
target.startTag("gc");
- target.addAttribute("x", cols - emptyCells);
+ target.addAttribute("x", getColumns() - emptyCells);
target.addAttribute("y", cury);
if (emptyCells > 1) {
target.addAttribute("w", emptyCells);
@@ -967,12 +968,12 @@ public class GridLayout extends AbstractLayout implements
}
// In case of no change
- if (cols == columns) {
+ if (getColumns() == columns) {
return;
}
// Checks for overlaps
- if (cols > columns) {
+ if (getColumns() > columns) {
for (final Iterator<Area> i = areas.iterator(); i.hasNext();) {
final Area area = i.next();
if (area.column2 >= columns) {
@@ -981,7 +982,7 @@ public class GridLayout extends AbstractLayout implements
}
}
- cols = columns;
+ getState().setColumns(columns);
requestRepaint();
}
@@ -992,7 +993,7 @@ public class GridLayout extends AbstractLayout implements
* @return the number of columns in the grid.
*/
public int getColumns() {
- return cols;
+ return getState().getColumns();
}
/**
@@ -1011,12 +1012,12 @@ public class GridLayout extends AbstractLayout implements
}
// In case of no change
- if (this.rows == rows) {
+ if (getRows() == rows) {
return;
}
// Checks for overlaps
- if (this.rows > rows) {
+ if (getRows() > rows) {
for (final Iterator<Area> i = areas.iterator(); i.hasNext();) {
final Area area = i.next();
if (area.row2 >= rows) {
@@ -1025,7 +1026,7 @@ public class GridLayout extends AbstractLayout implements
}
}
- this.rows = rows;
+ getState().setRows(rows);
requestRepaint();
}
@@ -1036,7 +1037,7 @@ public class GridLayout extends AbstractLayout implements
* @return the number of rows in the grid.
*/
public int getRows() {
- return rows;
+ return getState().getRows();
}
/**
@@ -1160,8 +1161,8 @@ public class GridLayout extends AbstractLayout implements
*
* @see com.vaadin.ui.Layout.SpacingHandler#setSpacing(boolean)
*/
- public void setSpacing(boolean enabled) {
- spacing = enabled;
+ public void setSpacing(boolean spacing) {
+ getState().setSpacing(spacing);
requestRepaint();
}
@@ -1170,18 +1171,8 @@ public class GridLayout extends AbstractLayout implements
*
* @see com.vaadin.ui.Layout.SpacingHandler#isSpacing()
*/
- @Deprecated
- public boolean isSpacingEnabled() {
- return spacing;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.Layout.SpacingHandler#isSpacing()
- */
public boolean isSpacing() {
- return spacing;
+ return getState().isSpacing();
}
/**
@@ -1192,9 +1183,9 @@ public class GridLayout extends AbstractLayout implements
* The leftmost row has index 0.
*/
public void insertRow(int row) {
- if (row > rows) {
+ if (row > getRows()) {
throw new IllegalArgumentException("Cannot insert row at " + row
- + " in a gridlayout with height " + rows);
+ + " in a gridlayout with height " + getRows());
}
for (Iterator<Area> i = areas.iterator(); i.hasNext();) {
@@ -1215,7 +1206,7 @@ public class GridLayout extends AbstractLayout implements
cursorY++;
}
- setRows(rows + 1);
+ setRows(getRows() + 1);
structuralChange = true;
requestRepaint();
}
@@ -1238,9 +1229,9 @@ public class GridLayout extends AbstractLayout implements
* Index of the row to remove. The leftmost row has index 0.
*/
public void removeRow(int row) {
- if (row >= rows) {
+ if (row >= getRows()) {
throw new IllegalArgumentException("Cannot delete row " + row
- + " from a gridlayout with height " + rows);
+ + " from a gridlayout with height " + getRows());
}
// Remove all components in row
@@ -1260,7 +1251,7 @@ public class GridLayout extends AbstractLayout implements
}
}
- if (rows == 1) {
+ if (getRows() == 1) {
/*
* Removing the last row means that the dimensions of the Grid
* layout will be truncated to 1 empty row and the cursor is moved
@@ -1269,7 +1260,7 @@ public class GridLayout extends AbstractLayout implements
cursorX = 0;
cursorY = 0;
} else {
- setRows(rows - 1);
+ setRows(getRows() - 1);
if (cursorY > row) {
cursorY--;
}
@@ -1397,29 +1388,15 @@ public class GridLayout extends AbstractLayout implements
return null;
}
- /**
- * Sets the component alignment using a shorthand string notation.
- *
- * @deprecated Replaced by
- * {@link #setComponentAlignment(Component, Alignment)}
- *
- * @param component
- * A child component in this layout
- * @param alignment
- * A short hand notation described in {@link AlignmentUtils}
- */
- @Deprecated
- public void setComponentAlignment(Component component, String alignment) {
- AlignmentUtils.setComponentAlignment(this, component, alignment);
- }
-
public void addListener(LayoutClickListener listener) {
- addListener(CLICK_EVENT, LayoutClickEvent.class, listener,
+ addListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener,
LayoutClickListener.clickMethod);
}
public void removeListener(LayoutClickListener listener) {
- removeListener(CLICK_EVENT, LayoutClickEvent.class, listener);
+ removeListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
+ LayoutClickEvent.class, listener);
}
}
diff --git a/src/com/vaadin/ui/HasComponents.java b/src/com/vaadin/ui/HasComponents.java
new file mode 100644
index 0000000000..eca89ddcd2
--- /dev/null
+++ b/src/com/vaadin/ui/HasComponents.java
@@ -0,0 +1,54 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import java.util.Iterator;
+
+/**
+ * Interface that must be implemented by all {@link Component}s that contain
+ * other {@link Component}s.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ *
+ */
+public interface HasComponents extends Component, Iterable<Component> {
+ /**
+ * Gets an iterator to the collection of contained components. Using this
+ * iterator it is possible to step through all components contained in this
+ * container.
+ *
+ * @return the component iterator.
+ */
+ public Iterator<Component> getComponentIterator();
+
+ /**
+ * Checks if the child component is visible. This method allows hiding a
+ * child component from updates and communication to and from the client.
+ * This is useful for components that show only a limited number of its
+ * children at any given time and want to allow updates only for the
+ * children that are visible (e.g. TabSheet has one tab open at a time).
+ * <p>
+ * Note that this will prevent updates from reaching the child even though
+ * the child itself is set to visible. Also if a child is set to invisible
+ * this will not force it to be visible.
+ * </p>
+ *
+ * @param childComponent
+ * The child component to check
+ * @return true if the child component is visible to the user, false
+ * otherwise
+ */
+ public boolean isComponentVisible(Component childComponent);
+
+ /**
+ * Causes a repaint of this component, and all components below it.
+ *
+ * This should only be used in special cases, e.g when the state of a
+ * descendant depends on the state of a ancestor.
+ */
+ public void requestRepaintAll();
+
+}
diff --git a/src/com/vaadin/ui/HorizontalLayout.java b/src/com/vaadin/ui/HorizontalLayout.java
index ed1cad8184..b9dc1c13ca 100644
--- a/src/com/vaadin/ui/HorizontalLayout.java
+++ b/src/com/vaadin/ui/HorizontalLayout.java
@@ -3,9 +3,6 @@
*/
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VHorizontalLayout;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
/**
* Horizontal layout
*
@@ -18,7 +15,6 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 5.3
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VHorizontalLayout.class, loadStyle = LoadStyle.EAGER)
public class HorizontalLayout extends AbstractOrderedLayout {
public HorizontalLayout() {
diff --git a/src/com/vaadin/ui/HorizontalSplitPanel.java b/src/com/vaadin/ui/HorizontalSplitPanel.java
index d9368635df..5bd6c8a075 100644
--- a/src/com/vaadin/ui/HorizontalSplitPanel.java
+++ b/src/com/vaadin/ui/HorizontalSplitPanel.java
@@ -3,9 +3,6 @@
*/
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
/**
* A horizontal split panel contains two components and lays them horizontally.
* The first component is on the left side.
@@ -29,7 +26,6 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @VERSION@
* @since 6.5
*/
-@ClientWidget(value = VSplitPanelHorizontal.class, loadStyle = LoadStyle.EAGER)
public class HorizontalSplitPanel extends AbstractSplitPanel {
public HorizontalSplitPanel() {
super();
diff --git a/src/com/vaadin/ui/InlineDateField.java b/src/com/vaadin/ui/InlineDateField.java
index 50e16d803b..cf61703318 100644
--- a/src/com/vaadin/ui/InlineDateField.java
+++ b/src/com/vaadin/ui/InlineDateField.java
@@ -7,7 +7,6 @@ package com.vaadin.ui;
import java.util.Date;
import com.vaadin.data.Property;
-import com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar;
/**
* <p>
@@ -22,7 +21,6 @@ import com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar;
* @VERSION@
* @since 5.0
*/
-@ClientWidget(VDateFieldCalendar.class)
public class InlineDateField extends DateField {
public InlineDateField() {
diff --git a/src/com/vaadin/ui/Label.java b/src/com/vaadin/ui/Label.java
index 2dadf4f5c5..e52090aa5f 100644
--- a/src/com/vaadin/ui/Label.java
+++ b/src/com/vaadin/ui/Label.java
@@ -5,19 +5,18 @@
package com.vaadin.ui;
import java.lang.reflect.Method;
+import java.util.Map;
import com.vaadin.data.Property;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VLabel;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.Vaadin6Component;
/**
* Label component for showing non-editable short texts.
*
- * The label content can be set to the modes specified by the final members
- * CONTENT_*
+ * The label content can be set to the modes specified by {@link ContentMode}
*
* <p>
* The contents of the label may contain simple formatting:
@@ -39,67 +38,165 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VLabel.class, loadStyle = LoadStyle.EAGER)
+// TODO generics for interface Property
public class Label extends AbstractComponent implements Property,
Property.Viewer, Property.ValueChangeListener,
- Property.ValueChangeNotifier, Comparable<Object> {
+ Property.ValueChangeNotifier, Comparable<Object>, Vaadin6Component {
/**
- * Content mode, where the label contains only plain text. The getValue()
- * result is coded to XML when painting.
+ * Content modes defining how the client should interpret a Label's value.
+ *
+ * @sine 7.0
*/
- public static final int CONTENT_TEXT = 0;
+ public enum ContentMode {
+ /**
+ * Content mode, where the label contains only plain text. The
+ * getValue() result is coded to XML when painting.
+ */
+ TEXT(null) {
+ @Override
+ public void paintText(String text, PaintTarget target)
+ throws PaintException {
+ target.addText(text);
+ }
+ },
+
+ /**
+ * Content mode, where the label contains preformatted text.
+ */
+ PREFORMATTED("pre") {
+ @Override
+ public void paintText(String text, PaintTarget target)
+ throws PaintException {
+ target.startTag("pre");
+ target.addText(text);
+ target.endTag("pre");
+ }
+ },
+
+ /**
+ * Content mode, where the label contains XHTML. Contents is then
+ * enclosed in DIV elements having namespace of
+ * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".
+ */
+ XHTML("xhtml") {
+ @Override
+ public void paintText(String text, PaintTarget target)
+ throws PaintException {
+ target.startTag("data");
+ target.addXMLSection("div", text,
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
+ target.endTag("data");
+ }
+ },
+
+ /**
+ * Content mode, where the label contains well-formed or well-balanced
+ * XML. Each of the root elements must have their default namespace
+ * specified.
+ */
+ XML("xml") {
+ @Override
+ public void paintText(String text, PaintTarget target)
+ throws PaintException {
+ target.addXMLSection("data", text, null);
+ }
+ },
+
+ /**
+ * Content mode, where the label contains RAW output. Output is not
+ * required to comply to with XML. In Web Adapter output is inserted
+ * inside the resulting HTML document as-is. This is useful for some
+ * specific purposes where possibly broken HTML content needs to be
+ * shown, but in most cases XHTML mode should be preferred.
+ */
+ RAW("raw") {
+ @Override
+ public void paintText(String text, PaintTarget target)
+ throws PaintException {
+ target.startTag("data");
+ target.addAttribute("escape", false);
+ target.addText(text);
+ target.endTag("data");
+ }
+ };
+
+ private final String uidlName;
+
+ /**
+ * The default content mode is text
+ */
+ public static ContentMode DEFAULT = TEXT;
+
+ private ContentMode(String uidlName) {
+ this.uidlName = uidlName;
+ }
+
+ /**
+ * Gets the name representing this content mode in UIDL messages
+ *
+ * @return the UIDL name of this content mode
+ */
+ public String getUidlName() {
+ return uidlName;
+ }
+
+ /**
+ * Adds the text value to a {@link PaintTarget} according to this
+ * content mode
+ *
+ * @param text
+ * the text to add
+ * @param target
+ * the paint target to add the value to
+ * @throws PaintException
+ * if the paint operation failed
+ */
+ public abstract void paintText(String text, PaintTarget target)
+ throws PaintException;
+ }
/**
- * Content mode, where the label contains preformatted text.
+ * @deprecated From 7.0, use {@link ContentMode#TEXT} instead
*/
- public static final int CONTENT_PREFORMATTED = 1;
+ @Deprecated
+ public static final ContentMode CONTENT_TEXT = ContentMode.TEXT;
/**
- * Formatted content mode, where the contents is XML restricted to the UIDL
- * 1.0 formatting markups.
- *
- * @deprecated Use CONTENT_XML instead.
+ * @deprecated From 7.0, use {@link ContentMode#PREFORMATTED} instead
*/
@Deprecated
- public static final int CONTENT_UIDL = 2;
+ public static final ContentMode CONTENT_PREFORMATTED = ContentMode.PREFORMATTED;
/**
- * Content mode, where the label contains XHTML. Contents is then enclosed
- * in DIV elements having namespace of
- * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".
+ * @deprecated From 7.0, use {@link ContentMode#XHTML} instead
*/
- public static final int CONTENT_XHTML = 3;
+ @Deprecated
+ public static final ContentMode CONTENT_XHTML = ContentMode.XHTML;
/**
- * Content mode, where the label contains well-formed or well-balanced XML.
- * Each of the root elements must have their default namespace specified.
+ * @deprecated From 7.0, use {@link ContentMode#XML} instead
*/
- public static final int CONTENT_XML = 4;
+ @Deprecated
+ public static final ContentMode CONTENT_XML = ContentMode.XML;
/**
- * Content mode, where the label contains RAW output. Output is not required
- * to comply to with XML. In Web Adapter output is inserted inside the
- * resulting HTML document as-is. This is useful for some specific purposes
- * where possibly broken HTML content needs to be shown, but in most cases
- * XHTML mode should be preferred.
+ * @deprecated From 7.0, use {@link ContentMode#RAW} instead
*/
- public static final int CONTENT_RAW = 5;
+ @Deprecated
+ public static final ContentMode CONTENT_RAW = ContentMode.RAW;
/**
- * The default content mode is plain text.
+ * @deprecated From 7.0, use {@link ContentMode#DEFAULT} instead
*/
- public static final int CONTENT_DEFAULT = CONTENT_TEXT;
-
- /** Array of content mode names that are rendered in UIDL as mode attribute. */
- private static final String[] CONTENT_MODE_NAME = { "text", "pre", "uidl",
- "xhtml", "xml", "raw" };
+ @Deprecated
+ public static final ContentMode CONTENT_DEFAULT = ContentMode.DEFAULT;
private static final String DATASOURCE_MUST_BE_SET = "Datasource must be set";
private Property dataSource;
- private int contentMode = CONTENT_DEFAULT;
+ private ContentMode contentMode = ContentMode.DEFAULT;
/**
* Creates an empty Label.
@@ -114,7 +211,7 @@ public class Label extends AbstractComponent implements Property,
* @param content
*/
public Label(String content) {
- this(content, CONTENT_DEFAULT);
+ this(content, ContentMode.DEFAULT);
}
/**
@@ -124,7 +221,7 @@ public class Label extends AbstractComponent implements Property,
* @param contentSource
*/
public Label(Property contentSource) {
- this(contentSource, CONTENT_DEFAULT);
+ this(contentSource, ContentMode.DEFAULT);
}
/**
@@ -133,7 +230,7 @@ public class Label extends AbstractComponent implements Property,
* @param content
* @param contentMode
*/
- public Label(String content, int contentMode) {
+ public Label(String content, ContentMode contentMode) {
this(new ObjectProperty<String>(content, String.class), contentMode);
}
@@ -144,43 +241,15 @@ public class Label extends AbstractComponent implements Property,
* @param contentSource
* @param contentMode
*/
- public Label(Property contentSource, int contentMode) {
+ public Label(Property contentSource, ContentMode contentMode) {
setPropertyDataSource(contentSource);
- if (contentMode != CONTENT_DEFAULT) {
+ if (contentMode != ContentMode.DEFAULT) {
setContentMode(contentMode);
}
setWidth(100, UNITS_PERCENTAGE);
}
/**
- * Set the component to read-only. Readonly is not used in label.
- *
- * @param readOnly
- * True to enable read-only mode, False to disable it.
- */
- @Override
- public void setReadOnly(boolean readOnly) {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
- }
- dataSource.setReadOnly(readOnly);
- }
-
- /**
- * Is the component read-only ? Readonly is not used in label - this returns
- * allways false.
- *
- * @return <code>true</code> if the component is in read only mode.
- */
- @Override
- public boolean isReadOnly() {
- if (dataSource == null) {
- throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
- }
- return dataSource.isReadOnly();
- }
-
- /**
* Paints the content of this component.
*
* @param target
@@ -188,32 +257,12 @@ public class Label extends AbstractComponent implements Property,
* @throws PaintException
* if the Paint Operation fails.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- if (contentMode != CONTENT_TEXT) {
- target.addAttribute("mode", CONTENT_MODE_NAME[contentMode]);
- }
- if (contentMode == CONTENT_TEXT) {
- target.addText(toString());
- } else if (contentMode == CONTENT_UIDL) {
- target.addUIDL(toString());
- } else if (contentMode == CONTENT_XHTML) {
- target.startTag("data");
- target.addXMLSection("div", toString(),
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
- target.endTag("data");
- } else if (contentMode == CONTENT_PREFORMATTED) {
- target.startTag("pre");
- target.addText(toString());
- target.endTag("pre");
- } else if (contentMode == CONTENT_XML) {
- target.addXMLSection("data", toString(), null);
- } else if (contentMode == CONTENT_RAW) {
- target.startTag("data");
- target.addAttribute("escape", false);
- target.addText(toString());
- target.endTag("data");
+ String uidlName = contentMode.getUidlName();
+ if (uidlName != null) {
+ target.addAttribute("mode", uidlName);
}
+ contentMode.paintText(getStringValue(), target);
}
@@ -246,13 +295,33 @@ public class Label extends AbstractComponent implements Property,
/**
* @see java.lang.Object#toString()
+ * @deprecated use the data source value or {@link #getStringValue()}
+ * instead
*/
+ @Deprecated
@Override
public String toString() {
+ throw new UnsupportedOperationException(
+ "Use Property.getValue() instead of Label.toString()");
+ }
+
+ /**
+ * Returns the value of the <code>Property</code> in human readable textual
+ * format.
+ *
+ * This method exists to help migration from previous Vaadin versions by
+ * providing a simple replacement for {@link #toString()}. However, it is
+ * normally better to use the value of the label directly.
+ *
+ * @return String representation of the value stored in the Property
+ * @since 7.0
+ */
+ public String getStringValue() {
if (dataSource == null) {
throw new IllegalStateException(DATASOURCE_MUST_BE_SET);
}
- return dataSource.toString();
+ Object value = dataSource.getValue();
+ return (null != value) ? value.toString() : null;
}
/**
@@ -307,67 +376,27 @@ public class Label extends AbstractComponent implements Property,
/**
* Gets the content mode of the Label.
*
- * <p>
- * Possible content modes include:
- * <ul>
- * <li><b>CONTENT_TEXT</b> Content mode, where the label contains only plain
- * text. The getValue() result is coded to XML when painting.</li>
- * <li><b>CONTENT_PREFORMATTED</b> Content mode, where the label contains
- * preformatted text.</li>
- * <li><b>CONTENT_UIDL</b> Formatted content mode, where the contents is XML
- * restricted to the UIDL 1.0 formatting markups.</li>
- * <li><b>CONTENT_XHTML</b> Content mode, where the label contains XHTML.
- * Contents is then enclosed in DIV elements having namespace of
- * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
- * <li><b>CONTENT_XML</b> Content mode, where the label contains well-formed
- * or well-balanced XML. Each of the root elements must have their default
- * namespace specified.</li>
- * <li><b>CONTENT_RAW</b> Content mode, where the label contains RAW output.
- * Output is not required to comply to with XML. In Web Adapter output is
- * inserted inside the resulting HTML document as-is. This is useful for
- * some specific purposes where possibly broken HTML content needs to be
- * shown, but in most cases XHTML mode should be preferred.</li>
- * </ul>
- * </p>
- *
* @return the Content mode of the label.
+ *
+ * @see ContentMode
*/
- public int getContentMode() {
+ public ContentMode getContentMode() {
return contentMode;
}
/**
* Sets the content mode of the Label.
*
- * <p>
- * Possible content modes include:
- * <ul>
- * <li><b>CONTENT_TEXT</b> Content mode, where the label contains only plain
- * text. The getValue() result is coded to XML when painting.</li>
- * <li><b>CONTENT_PREFORMATTED</b> Content mode, where the label contains
- * preformatted text.</li>
- * <li><b>CONTENT_UIDL</b> Formatted content mode, where the contents is XML
- * restricted to the UIDL 1.0 formatting markups.</li>
- * <li><b>CONTENT_XHTML</b> Content mode, where the label contains XHTML.
- * Contents is then enclosed in DIV elements having namespace of
- * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
- * <li><b>CONTENT_XML</b> Content mode, where the label contains well-formed
- * or well-balanced XML. Each of the root elements must have their default
- * namespace specified.</li>
- * <li><b>CONTENT_RAW</b> Content mode, where the label contains RAW output.
- * Output is not required to comply to with XML. In Web Adapter output is
- * inserted inside the resulting HTML document as-is. This is useful for
- * some specific purposes where possibly broken HTML content needs to be
- * shown, but in most cases XHTML mode should be preferred.</li>
- * </ul>
- * </p>
- *
* @param contentMode
* the New content mode of the label.
+ *
+ * @see ContentMode
*/
- public void setContentMode(int contentMode) {
- if (contentMode != this.contentMode && contentMode >= CONTENT_TEXT
- && contentMode <= CONTENT_RAW) {
+ public void setContentMode(ContentMode contentMode) {
+ if (contentMode == null) {
+ throw new IllegalArgumentException("Content mode can not be null");
+ }
+ if (contentMode != this.contentMode) {
this.contentMode = contentMode;
requestRepaint();
}
@@ -397,7 +426,7 @@ public class Label extends AbstractComponent implements Property,
* @VERSION@
* @since 3.0
*/
- public class ValueChangeEvent extends Component.Event implements
+ public static class ValueChangeEvent extends Component.Event implements
Property.ValueChangeEvent {
/**
@@ -487,19 +516,19 @@ public class Label extends AbstractComponent implements Property,
String thisValue;
String otherValue;
- if (contentMode == CONTENT_XML || contentMode == CONTENT_UIDL
- || contentMode == CONTENT_XHTML) {
- thisValue = stripTags(toString());
+ if (contentMode == ContentMode.XML || contentMode == ContentMode.XHTML) {
+ thisValue = stripTags(getStringValue());
} else {
- thisValue = toString();
+ thisValue = getStringValue();
}
if (other instanceof Label
- && (((Label) other).getContentMode() == CONTENT_XML
- || ((Label) other).getContentMode() == CONTENT_UIDL || ((Label) other)
- .getContentMode() == CONTENT_XHTML)) {
- otherValue = stripTags(other.toString());
+ && (((Label) other).getContentMode() == ContentMode.XML || ((Label) other)
+ .getContentMode() == ContentMode.XHTML)) {
+ otherValue = stripTags(((Label) other).getStringValue());
} else {
+ // TODO not a good idea - and might assume that Field.toString()
+ // returns a string representation of the value
otherValue = other.toString();
}
@@ -537,4 +566,8 @@ public class Label extends AbstractComponent implements Property,
return res.toString();
}
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
+
}
diff --git a/src/com/vaadin/ui/Layout.java b/src/com/vaadin/ui/Layout.java
index a7cd0abdb4..67bfaa75ff 100644
--- a/src/com/vaadin/ui/Layout.java
+++ b/src/com/vaadin/ui/Layout.java
@@ -178,15 +178,6 @@ public interface Layout extends ComponentContainer, Serializable {
*
* @return true if spacing between child components within this layout
* is enabled, false otherwise
- * @deprecated Use {@link #isSpacing()} instead.
- */
- @Deprecated
- public boolean isSpacingEnabled();
-
- /**
- *
- * @return true if spacing between child components within this layout
- * is enabled, false otherwise
*/
public boolean isSpacing();
}
diff --git a/src/com/vaadin/ui/Link.java b/src/com/vaadin/ui/Link.java
index ebea47118a..ed5ffbba3a 100644
--- a/src/com/vaadin/ui/Link.java
+++ b/src/com/vaadin/ui/Link.java
@@ -4,10 +4,12 @@
package com.vaadin.ui;
+import java.util.Map;
+
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VLink;
+import com.vaadin.terminal.Vaadin6Component;
/**
* Link is used to create external or internal URL links.
@@ -18,17 +20,16 @@ import com.vaadin.terminal.gwt.client.ui.VLink;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VLink.class)
-public class Link extends AbstractComponent {
+public class Link extends AbstractComponent implements Vaadin6Component {
/* Target window border type constant: No window border */
- public static final int TARGET_BORDER_NONE = Window.BORDER_NONE;
+ public static final int TARGET_BORDER_NONE = Root.BORDER_NONE;
/* Target window border type constant: Minimal window border */
- public static final int TARGET_BORDER_MINIMAL = Window.BORDER_MINIMAL;
+ public static final int TARGET_BORDER_MINIMAL = Root.BORDER_MINIMAL;
/* Target window border type constant: Default window border */
- public static final int TARGET_BORDER_DEFAULT = Window.BORDER_DEFAULT;
+ public static final int TARGET_BORDER_DEFAULT = Root.BORDER_DEFAULT;
private Resource resource = null;
@@ -94,7 +95,6 @@ public class Link extends AbstractComponent {
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
if (resource != null) {
@@ -232,4 +232,8 @@ public class Link extends AbstractComponent {
this.resource = resource;
requestRepaint();
}
+
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
}
diff --git a/src/com/vaadin/ui/ListSelect.java b/src/com/vaadin/ui/ListSelect.java
index 5c879f00f5..35ccb34b3c 100644
--- a/src/com/vaadin/ui/ListSelect.java
+++ b/src/com/vaadin/ui/ListSelect.java
@@ -9,14 +9,12 @@ import java.util.Collection;
import com.vaadin.data.Container;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VListSelect;
/**
* This is a simple list select without, for instance, support for new items,
* lazyloading, and other advanced features.
*/
@SuppressWarnings("serial")
-@ClientWidget(VListSelect.class)
public class ListSelect extends AbstractSelect {
private int columns = 0;
diff --git a/src/com/vaadin/ui/LoginForm.java b/src/com/vaadin/ui/LoginForm.java
index 80e002435e..1d5203bc6b 100644
--- a/src/com/vaadin/ui/LoginForm.java
+++ b/src/com/vaadin/ui/LoginForm.java
@@ -4,10 +4,10 @@
package com.vaadin.ui;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
-import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -15,8 +15,9 @@ import java.util.Map;
import com.vaadin.Application;
import com.vaadin.terminal.ApplicationResource;
import com.vaadin.terminal.DownloadStream;
-import com.vaadin.terminal.ParameterHandler;
-import com.vaadin.terminal.URIHandler;
+import com.vaadin.terminal.RequestHandler;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
/**
@@ -73,11 +74,20 @@ public class LoginForm extends CustomComponent {
}
};
- private ParameterHandler paramHandler = new ParameterHandler() {
-
- public void handleParameters(Map<String, String[]> parameters) {
- if (parameters.containsKey("username")) {
- getWindow().addURIHandler(uriHandler);
+ private final RequestHandler requestHandler = new RequestHandler() {
+ public boolean handleRequest(Application application,
+ WrappedRequest request, WrappedResponse response)
+ throws IOException {
+ String requestPathInfo = request.getRequestPathInfo();
+ if ("/loginHandler".equals(requestPathInfo)) {
+ response.setCacheTime(-1);
+ response.setContentType("text/html; charset=utf-8");
+ response.getWriter()
+ .write("<html><body>Login form handled."
+ + "<script type='text/javascript'>top.vaadin.forceSync();"
+ + "</script></body></html>");
+
+ Map<String, String[]> parameters = request.getParameterMap();
HashMap<String, String> params = new HashMap<String, String>();
// expecting single params
@@ -89,33 +99,12 @@ public class LoginForm extends CustomComponent {
}
LoginEvent event = new LoginEvent(params);
fireEvent(event);
+ return true;
}
+ return false;
}
};
- private URIHandler uriHandler = new URIHandler() {
- private final String responce = "<html><body>Login form handeled."
- + "<script type='text/javascript'>top.vaadin.forceSync();"
- + "</script></body></html>";
-
- public DownloadStream handleURI(URL context, String relativeUri) {
- if (relativeUri != null && relativeUri.contains("loginHandler")) {
- if (window != null) {
- window.removeURIHandler(this);
- }
- DownloadStream downloadStream = new DownloadStream(
- new ByteArrayInputStream(responce.getBytes()),
- "text/html", "loginSuccesfull");
- downloadStream.setCacheTime(-1);
- return downloadStream;
- } else {
- return null;
- }
- }
- };
-
- private Window window;
-
public LoginForm() {
iframe.setType(Embedded.TYPE_BROWSER);
iframe.setSizeFull();
@@ -132,8 +121,7 @@ public class LoginForm extends CustomComponent {
* @return byte array containing login page html
*/
protected byte[] getLoginHTML() {
- String appUri = getApplication().getURL().toString()
- + getWindow().getName() + "/";
+ String appUri = getApplication().getURL().toString();
try {
return ("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
@@ -170,11 +158,11 @@ public class LoginForm extends CustomComponent {
+ "<div>"
+ usernameCaption
+ "</div><div >"
- + "<input class='v-textfield' style='display:block;' type='text' name='username'></div>"
+ + "<input class='v-textfield v-connector' style='display:block;' type='text' name='username'></div>"
+ "<div>"
+ passwordCaption
+ "</div>"
- + "<div><input class='v-textfield' style='display:block;' type='password' name='password'></div>"
+ + "<div><input class='v-textfield v-connector' style='display:block;' type='password' name='password'></div>"
+ "<div><div onclick=\"document.forms[0].submit();\" tabindex=\"0\" class=\"v-button\" role=\"button\" ><span class=\"v-button-wrap\"><span class=\"v-button-caption\">"
+ loginButtonCaption
+ "</span></span></div></div></form></div>" + "</body></html>")
@@ -188,21 +176,15 @@ public class LoginForm extends CustomComponent {
public void attach() {
super.attach();
getApplication().addResource(loginPage);
- getWindow().addParameterHandler(paramHandler);
+ getApplication().addRequestHandler(requestHandler);
iframe.setSource(loginPage);
}
@Override
public void detach() {
getApplication().removeResource(loginPage);
- getWindow().removeParameterHandler(paramHandler);
- // store window temporary to properly remove uri handler once
- // response is handled. (May happen if login handler removes login
- // form
- window = getWindow();
- if (window.getParent() != null) {
- window = window.getParent();
- }
+ getApplication().removeRequestHandler(requestHandler);
+
super.detach();
}
@@ -281,7 +263,7 @@ public class LoginForm extends CustomComponent {
}
@Override
- public void setWidth(float width, int unit) {
+ public void setWidth(float width, Unit unit) {
super.setWidth(width, unit);
if (iframe != null) {
if (width < 0) {
@@ -293,7 +275,7 @@ public class LoginForm extends CustomComponent {
}
@Override
- public void setHeight(float height, int unit) {
+ public void setHeight(float height, Unit unit) {
super.setHeight(height, unit);
if (iframe != null) {
if (height < 0) {
diff --git a/src/com/vaadin/ui/MenuBar.java b/src/com/vaadin/ui/MenuBar.java
index 3469f77ebb..f94bd7ea64 100644
--- a/src/com/vaadin/ui/MenuBar.java
+++ b/src/com/vaadin/ui/MenuBar.java
@@ -13,8 +13,8 @@ import java.util.Stack;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VMenuBar;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.menubar.VMenuBar;
/**
* <p>
@@ -24,8 +24,7 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* </p>
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VMenuBar.class, loadStyle = LoadStyle.LAZY)
-public class MenuBar extends AbstractComponent {
+public class MenuBar extends AbstractComponent implements Vaadin6Component {
// Items of the top-level menu
private final List<MenuItem> menuItems;
@@ -33,20 +32,6 @@ public class MenuBar extends AbstractComponent {
// Number of items in this menu
private int numberOfItems = 0;
- /**
- * @deprecated
- * @see #setCollapse(boolean)
- */
- @Deprecated
- private boolean collapseItems;
-
- /**
- * @deprecated
- * @see #setSubmenuIcon(Resource)
- */
- @Deprecated
- private Resource submenuIcon;
-
private MenuItem moreItem;
private boolean openRootOnHover;
@@ -54,12 +39,7 @@ public class MenuBar extends AbstractComponent {
private boolean htmlContentAllowed;
/** Paint (serialise) the component for the client. */
- @Override
public void paintContent(PaintTarget target) throws PaintException {
-
- // Superclass writes any common attributes in the paint target.
- super.paintContent(target);
-
target.addAttribute(VMenuBar.OPEN_ROOT_MENU_ON_HOWER, openRootOnHover);
if (isHtmlContentAllowed()) {
@@ -68,10 +48,6 @@ public class MenuBar extends AbstractComponent {
target.startTag("options");
- if (submenuIcon != null) {
- target.addAttribute("submenuIcon", submenuIcon);
- }
-
if (getWidth() > -1) {
target.startTag("moreItem");
target.addAttribute("text", moreItem.getText());
@@ -103,7 +79,8 @@ public class MenuBar extends AbstractComponent {
target.addAttribute("id", item.getId());
if (item.getStyleName() != null) {
- target.addAttribute("style", item.getStyleName());
+ target.addAttribute(VMenuBar.ATTRIBUTE_ITEM_STYLE,
+ item.getStyleName());
}
if (item.isSeparator()) {
@@ -118,16 +95,17 @@ public class MenuBar extends AbstractComponent {
Resource icon = item.getIcon();
if (icon != null) {
- target.addAttribute("icon", icon);
+ target.addAttribute(VMenuBar.ATTRIBUTE_ITEM_ICON, icon);
}
if (!item.isEnabled()) {
- target.addAttribute("disabled", true);
+ target.addAttribute(VMenuBar.ATTRIBUTE_ITEM_DISABLED, true);
}
String description = item.getDescription();
if (description != null && description.length() > 0) {
- target.addAttribute("description", description);
+ target.addAttribute(VMenuBar.ATTRIBUTE_ITEM_DESCRIPTION,
+ description);
}
if (item.isCheckable()) {
// if the "checked" attribute is present (either true or false),
@@ -147,7 +125,6 @@ public class MenuBar extends AbstractComponent {
}
/** Deserialize changes received from client. */
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
Stack<MenuItem> items = new Stack<MenuItem>();
boolean found = false;
@@ -193,7 +170,6 @@ public class MenuBar extends AbstractComponent {
*/
public MenuBar() {
menuItems = new ArrayList<MenuItem>();
- setCollapse(true);
setMoreMenuItem(null);
}
@@ -311,54 +287,6 @@ public class MenuBar extends AbstractComponent {
}
/**
- * Set the icon to be used if a sub-menu has children. Defaults to null;
- *
- * @param icon
- * @deprecated (since 6.2, will be removed in 7.0) Icon is set in theme, no
- * need to worry about the visual representation here.
- */
- @Deprecated
- public void setSubmenuIcon(Resource icon) {
- submenuIcon = icon;
- requestRepaint();
- }
-
- /**
- * @deprecated
- * @see #setSubmenuIcon(Resource)
- */
- @Deprecated
- public Resource getSubmenuIcon() {
- return submenuIcon;
- }
-
- /**
- * Enable or disable collapsing top-level items. Top-level items will
- * collapse together if there is not enough room for them. Items that don't
- * fit will be placed under the "More" menu item.
- *
- * Collapsing is enabled by default.
- *
- * @param collapse
- * @deprecated (since 6.2, will be removed in 7.0) Collapsing is always
- * enabled if the MenuBar has a specified width.
- */
- @Deprecated
- public void setCollapse(boolean collapse) {
- collapseItems = collapse;
- requestRepaint();
- }
-
- /**
- * @see #setCollapse(boolean)
- * @deprecated
- */
- @Deprecated
- public boolean getCollapse() {
- return collapseItems;
- }
-
- /**
* Set the item that is used when collapsing the top level menu. All
* "overflowing" items will be added below this. The item command will be
* ignored. If set to null, the default item with a downwards arrow is used.
@@ -798,8 +726,7 @@ public class MenuBar extends AbstractComponent {
/**
* Sets the items's description. See {@link #getDescription()} for more
* information on what the description is. This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent}.
+ * {@link RepaintRequestEvent}.
*
* @param description
* the new description string for the component.
diff --git a/src/com/vaadin/ui/NativeButton.java b/src/com/vaadin/ui/NativeButton.java
index 369b40b93a..6eb4379261 100644
--- a/src/com/vaadin/ui/NativeButton.java
+++ b/src/com/vaadin/ui/NativeButton.java
@@ -3,11 +3,7 @@
*/
package com.vaadin.ui;
-import com.vaadin.data.Property;
-import com.vaadin.terminal.gwt.client.ui.VNativeButton;
-
@SuppressWarnings("serial")
-@ClientWidget(VNativeButton.class)
public class NativeButton extends Button {
public NativeButton() {
@@ -22,34 +18,4 @@ public class NativeButton extends Button {
super(caption, listener);
}
- public NativeButton(String caption, Object target, String methodName) {
- super(caption, target, methodName);
- }
-
- /**
- * Creates a new switch button with initial value.
- *
- * @param state
- * the Initial state of the switch-button.
- * @param initialState
- * @deprecated use the {@link CheckBox} component instead
- */
- @Deprecated
- public NativeButton(String caption, boolean initialState) {
- super(caption, initialState);
- }
-
- /**
- * Creates a new switch button that is connected to a boolean property.
- *
- * @param state
- * the Initial state of the switch-button.
- * @param dataSource
- * @deprecated use the {@link CheckBox} component instead
- */
- @Deprecated
- public NativeButton(String caption, Property dataSource) {
- super(caption, dataSource);
- }
-
-} \ No newline at end of file
+}
diff --git a/src/com/vaadin/ui/NativeSelect.java b/src/com/vaadin/ui/NativeSelect.java
index e701d212b4..1f85f57c97 100644
--- a/src/com/vaadin/ui/NativeSelect.java
+++ b/src/com/vaadin/ui/NativeSelect.java
@@ -9,7 +9,6 @@ import java.util.Collection;
import com.vaadin.data.Container;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VNativeSelect;
/**
* This is a simple drop-down select without, for instance, support for
@@ -18,7 +17,6 @@ import com.vaadin.terminal.gwt.client.ui.VNativeSelect;
* better choice.
*/
@SuppressWarnings("serial")
-@ClientWidget(VNativeSelect.class)
public class NativeSelect extends AbstractSelect {
// width in characters, mimics TextField
diff --git a/src/com/vaadin/ui/Notification.java b/src/com/vaadin/ui/Notification.java
new file mode 100644
index 0000000000..bb1f874635
--- /dev/null
+++ b/src/com/vaadin/ui/Notification.java
@@ -0,0 +1,321 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.Resource;
+
+/**
+ * A notification message, used to display temporary messages to the user - for
+ * example "Document saved", or "Save failed".
+ * <p>
+ * The notification message can consist of several parts: caption, description
+ * and icon. It is usually used with only caption - one should be wary of
+ * filling the notification with too much information.
+ * </p>
+ * <p>
+ * The notification message tries to be as unobtrusive as possible, while still
+ * drawing needed attention. There are several basic types of messages that can
+ * be used in different situations:
+ * <ul>
+ * <li>TYPE_HUMANIZED_MESSAGE fades away quickly as soon as the user uses the
+ * mouse or types something. It can be used to show fairly unimportant messages,
+ * such as feedback that an operation succeeded ("Document Saved") - the kind of
+ * messages the user ignores once the application is familiar.</li>
+ * <li>TYPE_WARNING_MESSAGE is shown for a short while after the user uses the
+ * mouse or types something. It's default style is also more noticeable than the
+ * humanized message. It can be used for messages that do not contain a lot of
+ * important information, but should be noticed by the user. Despite the name,
+ * it does not have to be a warning, but can be used instead of the humanized
+ * message whenever you want to make the message a little more noticeable.</li>
+ * <li>TYPE_ERROR_MESSAGE requires to user to click it before disappearing, and
+ * can be used for critical messages.</li>
+ * <li>TYPE_TRAY_NOTIFICATION is shown for a while in the lower left corner of
+ * the window, and can be used for "convenience notifications" that do not have
+ * to be noticed immediately, and should not interfere with the current task -
+ * for instance to show "You have a new message in your inbox" while the user is
+ * working in some other area of the application.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * In addition to the basic pre-configured types, a Notification can also be
+ * configured to show up in a custom position, for a specified time (or until
+ * clicked), and with a custom stylename. An icon can also be added.
+ * </p>
+ *
+ */
+public class Notification implements Serializable {
+ public static final int TYPE_HUMANIZED_MESSAGE = 1;
+ public static final int TYPE_WARNING_MESSAGE = 2;
+ public static final int TYPE_ERROR_MESSAGE = 3;
+ public static final int TYPE_TRAY_NOTIFICATION = 4;
+
+ public static final int POSITION_CENTERED = 1;
+ public static final int POSITION_CENTERED_TOP = 2;
+ public static final int POSITION_CENTERED_BOTTOM = 3;
+ public static final int POSITION_TOP_LEFT = 4;
+ public static final int POSITION_TOP_RIGHT = 5;
+ public static final int POSITION_BOTTOM_LEFT = 6;
+ public static final int POSITION_BOTTOM_RIGHT = 7;
+
+ public static final int DELAY_FOREVER = -1;
+ public static final int DELAY_NONE = 0;
+
+ private String caption;
+ private String description;
+ private Resource icon;
+ private int position = POSITION_CENTERED;
+ private int delayMsec = 0;
+ private String styleName;
+ private boolean htmlContentAllowed;
+
+ /**
+ * Creates a "humanized" notification message.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is by
+ * default rendered as html.
+ *
+ * @param caption
+ * The message to show
+ */
+ public Notification(String caption) {
+ this(caption, null, TYPE_HUMANIZED_MESSAGE);
+ }
+
+ /**
+ * Creates a notification message of the specified type.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is by
+ * default rendered as html.
+ *
+ * @param caption
+ * The message to show
+ * @param type
+ * The type of message
+ */
+ public Notification(String caption, int type) {
+ this(caption, null, type);
+ }
+
+ /**
+ * Creates a "humanized" notification message with a bigger caption and
+ * smaller description.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are by default rendered as html.
+ *
+ * @param caption
+ * The message caption
+ * @param description
+ * The message description
+ */
+ public Notification(String caption, String description) {
+ this(caption, description, TYPE_HUMANIZED_MESSAGE);
+ }
+
+ /**
+ * Creates a notification message of the specified type, with a bigger
+ * caption and smaller description.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are by default rendered as html.
+ *
+ * @param caption
+ * The message caption
+ * @param description
+ * The message description
+ * @param type
+ * The type of message
+ */
+ public Notification(String caption, String description, int type) {
+ this(caption, description, type, true);
+ }
+
+ /**
+ * Creates a notification message of the specified type, with a bigger
+ * caption and smaller description.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities if html is allowed.
+ *
+ * @param caption
+ * The message caption
+ * @param description
+ * The message description
+ * @param type
+ * The type of message
+ * @param htmlContentAllowed
+ * Whether html in the caption and description should be
+ * displayed as html or as plain text
+ */
+ public Notification(String caption, String description, int type,
+ boolean htmlContentAllowed) {
+ this.caption = caption;
+ this.description = description;
+ this.htmlContentAllowed = htmlContentAllowed;
+ setType(type);
+ }
+
+ private void setType(int type) {
+ switch (type) {
+ case TYPE_WARNING_MESSAGE:
+ delayMsec = 1500;
+ styleName = "warning";
+ break;
+ case TYPE_ERROR_MESSAGE:
+ delayMsec = -1;
+ styleName = "error";
+ break;
+ case TYPE_TRAY_NOTIFICATION:
+ delayMsec = 3000;
+ position = POSITION_BOTTOM_RIGHT;
+ styleName = "tray";
+
+ case TYPE_HUMANIZED_MESSAGE:
+ default:
+ break;
+ }
+
+ }
+
+ /**
+ * Gets the caption part of the notification message.
+ *
+ * @return The message caption
+ */
+ public String getCaption() {
+ return caption;
+ }
+
+ /**
+ * Sets the caption part of the notification message
+ *
+ * @param caption
+ * The message caption
+ */
+ public void setCaption(String caption) {
+ this.caption = caption;
+ }
+
+ /**
+ * Gets the description part of the notification message.
+ *
+ * @return The message description.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the description part of the notification message.
+ *
+ * @param description
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Gets the position of the notification message.
+ *
+ * @return The position
+ */
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * Sets the position of the notification message.
+ *
+ * @param position
+ * The desired notification position
+ */
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ /**
+ * Gets the icon part of the notification message.
+ *
+ * @return The message icon
+ */
+ public Resource getIcon() {
+ return icon;
+ }
+
+ /**
+ * Sets the icon part of the notification message.
+ *
+ * @param icon
+ * The desired message icon
+ */
+ public void setIcon(Resource icon) {
+ this.icon = icon;
+ }
+
+ /**
+ * Gets the delay before the notification disappears.
+ *
+ * @return the delay in msec, -1 indicates the message has to be clicked.
+ */
+ public int getDelayMsec() {
+ return delayMsec;
+ }
+
+ /**
+ * Sets the delay before the notification disappears.
+ *
+ * @param delayMsec
+ * the desired delay in msec, -1 to require the user to click the
+ * message
+ */
+ public void setDelayMsec(int delayMsec) {
+ this.delayMsec = delayMsec;
+ }
+
+ /**
+ * Sets the style name for the notification message.
+ *
+ * @param styleName
+ * The desired style name.
+ */
+ public void setStyleName(String styleName) {
+ this.styleName = styleName;
+ }
+
+ /**
+ * Gets the style name for the notification message.
+ *
+ * @return
+ */
+ public String getStyleName() {
+ return styleName;
+ }
+
+ /**
+ * Sets whether html is allowed in the caption and description. If set to
+ * true, the texts are passed to the browser as html and the developer is
+ * responsible for ensuring no harmful html is used. If set to false, the
+ * texts are passed to the browser as plain text.
+ *
+ * @param htmlContentAllowed
+ * true if the texts are used as html, false if used as plain
+ * text
+ */
+ public void setHtmlContentAllowed(boolean htmlContentAllowed) {
+ this.htmlContentAllowed = htmlContentAllowed;
+ }
+
+ /**
+ * Checks whether caption and description are interpreted as html or plain
+ * text.
+ *
+ * @return true if the texts are used as html, false if used as plain text
+ * @see #setHtmlContentAllowed(boolean)
+ */
+ public boolean isHtmlContentAllowed() {
+ return htmlContentAllowed;
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/ui/OptionGroup.java b/src/com/vaadin/ui/OptionGroup.java
index 884e58824a..a4aaf7ec99 100644
--- a/src/com/vaadin/ui/OptionGroup.java
+++ b/src/com/vaadin/ui/OptionGroup.java
@@ -17,13 +17,12 @@ import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VOptionGroup;
+import com.vaadin.terminal.gwt.client.ui.optiongroup.VOptionGroup;
/**
* Configures select to be used as an option group.
*/
@SuppressWarnings("serial")
-@ClientWidget(VOptionGroup.class)
public class OptionGroup extends AbstractSelect implements
FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {
@@ -60,7 +59,7 @@ public class OptionGroup extends AbstractSelect implements
throws PaintException {
super.paintItem(target, itemId);
if (!isItemEnabled(itemId)) {
- target.addAttribute("disabled", true);
+ target.addAttribute(VOptionGroup.ATTRIBUTE_OPTION_DISABLED, true);
}
}
diff --git a/src/com/vaadin/ui/OrderedLayout.java b/src/com/vaadin/ui/OrderedLayout.java
deleted file mode 100644
index 474fc89867..0000000000
--- a/src/com/vaadin/ui/OrderedLayout.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VOrderedLayout;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
-/**
- * Ordered layout.
- *
- * <code>OrderedLayout</code> is a component container, which shows the
- * subcomponents in the order of their addition in specified orientation.
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 3.0
- * @deprecated Replaced by VerticalLayout/HorizontalLayout. For type checking
- * please not that VerticalLayout/HorizontalLayout do not extend
- * OrderedLayout but AbstractOrderedLayout (which also OrderedLayout
- * extends).
- */
-@SuppressWarnings("serial")
-@Deprecated
-@ClientWidget(value = VOrderedLayout.class, loadStyle = LoadStyle.EAGER)
-public class OrderedLayout extends AbstractOrderedLayout {
- /* Predefined orientations */
-
- /**
- * Components are to be laid out vertically.
- */
- public static final int ORIENTATION_VERTICAL = 0;
-
- /**
- * Components are to be laid out horizontally.
- */
- public static final int ORIENTATION_HORIZONTAL = 1;
-
- /**
- * Orientation of the layout.
- */
- private int orientation;
-
- /**
- * Creates a new ordered layout. The order of the layout is
- * <code>ORIENTATION_VERTICAL</code>.
- *
- * @deprecated Use VerticalLayout instead.
- */
- @Deprecated
- public OrderedLayout() {
- this(ORIENTATION_VERTICAL);
- }
-
- /**
- * Create a new ordered layout. The orientation of the layout is given as
- * parameters.
- *
- * @param orientation
- * the Orientation of the layout.
- *
- * @deprecated Use VerticalLayout/HorizontalLayout instead.
- */
- @Deprecated
- public OrderedLayout(int orientation) {
- this.orientation = orientation;
- if (orientation == ORIENTATION_VERTICAL) {
- setWidth(100, UNITS_PERCENTAGE);
- }
- }
-
- /**
- * Gets the orientation of the container.
- *
- * @return the Value of property orientation.
- */
- public int getOrientation() {
- return orientation;
- }
-
- /**
- * Sets the orientation of this OrderedLayout. This method should only be
- * used before initial paint.
- *
- * @param orientation
- * the New value of property orientation.
- * @deprecated Use VerticalLayout/HorizontalLayout or define orientation in
- * constructor instead
- */
- @Deprecated
- public void setOrientation(int orientation) {
- setOrientation(orientation, true);
- }
-
- /**
- * Internal method to change orientation of layout. This method should only
- * be used before initial paint.
- *
- * @param orientation
- */
- protected void setOrientation(int orientation, boolean needsRepaint) {
- // Checks the validity of the argument
- if (orientation < ORIENTATION_VERTICAL
- || orientation > ORIENTATION_HORIZONTAL) {
- throw new IllegalArgumentException();
- }
-
- this.orientation = orientation;
- if (needsRepaint) {
- requestRepaint();
- }
- }
-
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- // Adds the orientation attributes (the default is vertical)
- if (orientation == ORIENTATION_HORIZONTAL) {
- target.addAttribute("orientation", "horizontal");
- }
-
- }
-
-}
diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java
index a69413c28b..b2916f78c7 100644
--- a/src/com/vaadin/ui/Panel.java
+++ b/src/com/vaadin/ui/Panel.java
@@ -15,11 +15,12 @@ import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Scrollable;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VPanel;
+import com.vaadin.terminal.gwt.client.ui.ClickEventHandler;
+import com.vaadin.terminal.gwt.client.ui.panel.PanelServerRpc;
+import com.vaadin.terminal.gwt.client.ui.panel.PanelState;
import com.vaadin.ui.Component.Focusable;
-import com.vaadin.ui.themes.Reindeer;
-import com.vaadin.ui.themes.Runo;
/**
* Panel - a simple single component container.
@@ -30,24 +31,10 @@ import com.vaadin.ui.themes.Runo;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VPanel.class)
public class Panel extends AbstractComponentContainer implements Scrollable,
ComponentContainer.ComponentAttachListener,
- ComponentContainer.ComponentDetachListener, Action.Notifier, Focusable {
-
- private static final String CLICK_EVENT = VPanel.CLICK_EVENT_IDENTIFIER;
-
- /**
- * Removes extra decorations from the Panel.
- *
- * @deprecated this style is no longer part of the core framework and this
- * component, even though most built-in themes implement this
- * style. Use the constant specified in the theme class file
- * that you're using, if it provides one, e.g.
- * {@link Reindeer#PANEL_LIGHT} or {@link Runo#PANEL_LIGHT} .
- */
- @Deprecated
- public static final String STYLE_LIGHT = "light";
+ ComponentContainer.ComponentDetachListener, Action.Notifier, Focusable,
+ Vaadin6Component {
/**
* Content of the panel.
@@ -55,32 +42,16 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
private ComponentContainer content;
/**
- * Scroll X position.
- */
- private int scrollOffsetX = 0;
-
- /**
- * Scroll Y position.
- */
- private int scrollOffsetY = 0;
-
- /**
- * Scrolling mode.
- */
- private boolean scrollable = false;
-
- /**
* Keeps track of the Actions added to this component, and manages the
* painting and handling as well.
*/
protected ActionManager actionManager;
- /**
- * By default the Panel is not in the normal document focus flow and can
- * only be focused by using the focus()-method. Change this to 0 if you want
- * to have the Panel in the normal focus flow.
- */
- private int tabIndex = -1;
+ private PanelServerRpc rpc = new PanelServerRpc() {
+ public void click(MouseEventDetails mouseDetails) {
+ fireEvent(new ClickEvent(Panel.this, mouseDetails));
+ }
+ };
/**
* Creates a new empty panel. A VerticalLayout is used as content.
@@ -97,8 +68,10 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* the content for the panel.
*/
public Panel(ComponentContainer content) {
+ registerRpc(rpc);
setContent(content);
- setWidth(100, UNITS_PERCENTAGE);
+ setWidth(100, Unit.PERCENTAGE);
+ getState().setTabIndex(-1);
}
/**
@@ -139,45 +112,6 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
}
/**
- * Gets the current layout of the panel.
- *
- * @return the Current layout of the panel.
- * @deprecated A Panel can now contain a ComponentContainer which is not
- * necessarily a Layout. Use {@link #getContent()} instead.
- */
- @Deprecated
- public Layout getLayout() {
- if (content instanceof Layout) {
- return (Layout) content;
- } else if (content == null) {
- return null;
- }
-
- throw new IllegalStateException(
- "Panel does not contain a Layout. Use getContent() instead of getLayout().");
- }
-
- /**
- * Sets the layout of the panel.
- *
- * If given layout is null, a VerticalLayout with margins set is used as a
- * default.
- *
- * Components from old layout are not moved to new layout by default
- * (changed in 5.2.2). Use function in Layout interface manually.
- *
- * @param newLayout
- * the New layout of the panel.
- * @deprecated A Panel can now contain a ComponentContainer which is not
- * necessarily a Layout. Use
- * {@link #setContent(ComponentContainer)} instead.
- */
- @Deprecated
- public void setLayout(Layout newLayout) {
- setContent(newLayout);
- }
-
- /**
* Returns the content of the Panel.
*
* @return
@@ -249,20 +183,10 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* (non-Javadoc)
*
* @see
- * com.vaadin.ui.AbstractComponent#paintContent(com.vaadin.terminal.PaintTarget
- * )
+ * com.vaadin.terminal.Vaadin6Component#paintContent(com.vaadin.terminal
+ * .PaintTarget)
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- content.paint(target);
-
- target.addVariable(this, "tabindex", getTabIndex());
-
- if (isScrollable()) {
- target.addVariable(this, "scrollLeft", getScrollLeft());
- target.addVariable(this, "scrollTop", getScrollTop());
- }
-
if (actionManager != null) {
actionManager.paintActions(null, target);
}
@@ -323,14 +247,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* @see com.vaadin.terminal.VariableOwner#changeVariables(Object, Map)
*/
@SuppressWarnings("unchecked")
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
-
- if (variables.containsKey(CLICK_EVENT)) {
- fireClick((Map<String, Object>) variables.get(CLICK_EVENT));
- }
-
// Get new size
final Integer newWidth = (Integer) variables.get("width");
final Integer newHeight = (Integer) variables.get("height");
@@ -346,11 +263,11 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
final Integer newScrollY = (Integer) variables.get("scrollTop");
if (newScrollX != null && newScrollX.intValue() != getScrollLeft()) {
// set internally, not to fire request repaint
- scrollOffsetX = newScrollX.intValue();
+ getState().setScrollLeft(newScrollX.intValue());
}
if (newScrollY != null && newScrollY.intValue() != getScrollTop()) {
// set internally, not to fire request repaint
- scrollOffsetY = newScrollY.intValue();
+ getState().setScrollTop(newScrollY.intValue());
}
// Actions
@@ -368,15 +285,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
*/
public int getScrollLeft() {
- return scrollOffsetX;
- }
-
- /**
- * @deprecated use {@link #getScrollLeft()} instead
- */
- @Deprecated
- public int getScrollOffsetX() {
- return getScrollLeft();
+ return getState().getScrollLeft();
}
/*
@@ -385,110 +294,35 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
*/
public int getScrollTop() {
- return scrollOffsetY;
- }
-
- /**
- * @deprecated use {@link #getScrollTop()} instead
- */
- @Deprecated
- public int getScrollOffsetY() {
- return getScrollTop();
+ return getState().getScrollTop();
}
/*
* (non-Javadoc)
*
- * @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
- */
- public boolean isScrollable() {
- return scrollable;
- }
-
- /**
- * Sets the panel as programmatically scrollable.
- *
- * <p>
- * Panel is by default not scrollable programmatically with
- * {@link #setScrollLeft(int)} and {@link #setScrollTop(int)}, so if you use
- * those methods, you need to enable scrolling with this method. Components
- * that extend Panel may have a different default for the programmatic
- * scrollability.
- * </p>
- *
- * @see com.vaadin.terminal.Scrollable#setScrollable(boolean)
- */
- public void setScrollable(boolean isScrollingEnabled) {
- if (scrollable != isScrollingEnabled) {
- scrollable = isScrollingEnabled;
- requestRepaint();
- }
- }
-
- /**
- * Sets the horizontal scroll position.
- *
- * <p>
- * Setting the horizontal scroll position with this method requires that
- * programmatic scrolling of the component has been enabled. For Panel it is
- * disabled by default, so you have to call {@link #setScrollable(boolean)}.
- * Components extending Panel may have a different default for programmatic
- * scrollability.
- * </p>
- *
* @see com.vaadin.terminal.Scrollable#setScrollLeft(int)
- * @see #setScrollable(boolean)
*/
- public void setScrollLeft(int pixelsScrolled) {
- if (pixelsScrolled < 0) {
+ public void setScrollLeft(int scrollLeft) {
+ if (scrollLeft < 0) {
throw new IllegalArgumentException(
"Scroll offset must be at least 0");
}
- if (scrollOffsetX != pixelsScrolled) {
- scrollOffsetX = pixelsScrolled;
- requestRepaint();
- }
- }
-
- /**
- * @deprecated use setScrollLeft() method instead
- */
- @Deprecated
- public void setScrollOffsetX(int pixels) {
- setScrollLeft(pixels);
+ getState().setScrollLeft(scrollLeft);
+ requestRepaint();
}
- /**
- * Sets the vertical scroll position.
- *
- * <p>
- * Setting the vertical scroll position with this method requires that
- * programmatic scrolling of the component has been enabled. For Panel it is
- * disabled by default, so you have to call {@link #setScrollable(boolean)}.
- * Components extending Panel may have a different default for programmatic
- * scrollability.
- * </p>
+ /*
+ * (non-Javadoc)
*
* @see com.vaadin.terminal.Scrollable#setScrollTop(int)
- * @see #setScrollable(boolean)
*/
- public void setScrollTop(int pixelsScrolledDown) {
- if (pixelsScrolledDown < 0) {
+ public void setScrollTop(int scrollTop) {
+ if (scrollTop < 0) {
throw new IllegalArgumentException(
"Scroll offset must be at least 0");
}
- if (scrollOffsetY != pixelsScrolledDown) {
- scrollOffsetY = pixelsScrolledDown;
- requestRepaint();
- }
- }
-
- /**
- * @deprecated use setScrollTop() method instead
- */
- @Deprecated
- public void setScrollOffsetY(int pixels) {
- setScrollTop(pixels);
+ getState().setScrollTop(scrollTop);
+ requestRepaint();
}
/* Documented in superclass */
@@ -526,6 +360,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
*/
@Override
public void attach() {
+ getRoot().componentAttached(this);
// can't call parent here as this is Panels hierarchy is a hack
requestRepaint();
if (content != null) {
@@ -544,6 +379,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
if (content != null) {
content.detach();
}
+ getRoot().componentDetached(this);
}
/**
@@ -559,6 +395,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
/*
* ACTIONS
*/
+ @Override
protected ActionManager getActionManager() {
if (actionManager == null) {
actionManager = new ActionManager(this);
@@ -609,8 +446,8 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* The listener to add
*/
public void addListener(ClickListener listener) {
- addListener(CLICK_EVENT, ClickEvent.class, listener,
- ClickListener.clickMethod);
+ addListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
+ listener, ClickListener.clickMethod);
}
/**
@@ -621,33 +458,22 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
* The listener to remove
*/
public void removeListener(ClickListener listener) {
- removeListener(CLICK_EVENT, ClickEvent.class, listener);
- }
-
- /**
- * Fire a click event to all click listeners.
- *
- * @param object
- * The raw "value" of the variable change from the client side.
- */
- private void fireClick(Map<String, Object> parameters) {
- MouseEventDetails mouseDetails = MouseEventDetails
- .deSerialize((String) parameters.get("mouseDetails"));
- fireEvent(new ClickEvent(this, mouseDetails));
+ removeListener(ClickEventHandler.CLICK_EVENT_IDENTIFIER,
+ ClickEvent.class, listener);
}
/**
* {@inheritDoc}
*/
public int getTabIndex() {
- return tabIndex;
+ return getState().getTabIndex();
}
/**
* {@inheritDoc}
*/
public void setTabIndex(int tabIndex) {
- this.tabIndex = tabIndex;
+ getState().setTabIndex(tabIndex);
requestRepaint();
}
@@ -660,4 +486,19 @@ public class Panel extends AbstractComponentContainer implements Scrollable,
super.focus();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.ComponentContainer#getComponentCount()
+ */
+ public int getComponentCount() {
+ // This is so wrong... (#2924)
+ return content.getComponentCount();
+ }
+
+ @Override
+ public PanelState getState() {
+ return (PanelState) super.getState();
+ }
+
}
diff --git a/src/com/vaadin/ui/PasswordField.java b/src/com/vaadin/ui/PasswordField.java
index 99874147d5..c1fccebbfe 100644
--- a/src/com/vaadin/ui/PasswordField.java
+++ b/src/com/vaadin/ui/PasswordField.java
@@ -4,13 +4,11 @@
package com.vaadin.ui;
import com.vaadin.data.Property;
-import com.vaadin.terminal.gwt.client.ui.VPasswordField;
/**
* A field that is used to enter secret text information like passwords. The
* entered text is not displayed on the screen.
*/
-@ClientWidget(VPasswordField.class)
public class PasswordField extends AbstractTextField {
/**
diff --git a/src/com/vaadin/ui/PopupView.java b/src/com/vaadin/ui/PopupView.java
index fcad727510..911d926053 100644
--- a/src/com/vaadin/ui/PopupView.java
+++ b/src/com/vaadin/ui/PopupView.java
@@ -8,9 +8,10 @@ import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
+import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VPopupView;
+import com.vaadin.terminal.Vaadin6Component;
/**
*
@@ -22,8 +23,8 @@ import com.vaadin.terminal.gwt.client.ui.VPopupView;
* @author Vaadin Ltd.
*/
@SuppressWarnings("serial")
-@ClientWidget(VPopupView.class)
-public class PopupView extends AbstractComponentContainer {
+public class PopupView extends AbstractComponentContainer implements
+ Vaadin6Component {
private Content content;
private boolean hideOnMouseOut;
@@ -306,11 +307,7 @@ public class PopupView extends AbstractComponentContainer {
*
* @see com.vaadin.ui.AbstractComponent#paintContent(com.vaadin.terminal.PaintTarget)
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- // Superclass writes any common attributes in the paint target.
- super.paintContent(target);
-
String html = content.getMinimizedValueAsHTML();
if (html == null) {
html = "";
@@ -321,7 +318,7 @@ public class PopupView extends AbstractComponentContainer {
// Only paint component to client if we know that the popup is showing
if (isPopupVisible()) {
target.startTag("popupComponent");
- visibleComponent.paint(target);
+ LegacyPaint.paint(visibleComponent, target);
target.endTag("popupComponent");
}
@@ -334,7 +331,6 @@ public class PopupView extends AbstractComponentContainer {
* @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
* java.util.Map)
*/
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
if (variables.containsKey("popupVisibility")) {
setPopupVisible(((Boolean) variables.get("popupVisibility"))
diff --git a/src/com/vaadin/ui/ProgressIndicator.java b/src/com/vaadin/ui/ProgressIndicator.java
index 405ff2b52f..4d585cfdd7 100644
--- a/src/com/vaadin/ui/ProgressIndicator.java
+++ b/src/com/vaadin/ui/ProgressIndicator.java
@@ -4,11 +4,13 @@
package com.vaadin.ui;
+import java.util.Map;
+
import com.vaadin.data.Property;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VProgressIndicator;
+import com.vaadin.terminal.Vaadin6Component;
/**
* <code>ProgressIndicator</code> is component that shows user state of a
@@ -25,9 +27,8 @@ import com.vaadin.terminal.gwt.client.ui.VProgressIndicator;
* @since 4
*/
@SuppressWarnings("serial")
-@ClientWidget(VProgressIndicator.class)
-public class ProgressIndicator extends AbstractField implements Property,
- Property.Viewer, Property.ValueChangeListener {
+public class ProgressIndicator extends AbstractField<Number> implements
+ Property.Viewer, Property.ValueChangeListener, Vaadin6Component {
/**
* Content mode, where the label contains only plain text. The getValue()
@@ -110,7 +111,6 @@ public class ProgressIndicator extends AbstractField implements Property,
* @throws PaintException
* if the Paint Operation fails.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
target.addAttribute("indeterminate", indeterminate);
target.addAttribute("pollinginterval", pollingInterval);
@@ -125,11 +125,12 @@ public class ProgressIndicator extends AbstractField implements Property,
* @see com.vaadin.ui.AbstractField#getValue()
*/
@Override
- public Object getValue() {
+ public Number getValue() {
if (dataSource == null) {
throw new IllegalStateException("Datasource must be set");
}
- return dataSource.getValue();
+ // TODO conversions to eliminate cast
+ return (Number) dataSource.getValue();
}
/**
@@ -138,7 +139,7 @@ public class ProgressIndicator extends AbstractField implements Property,
*
* @param newValue
* the New value of the ProgressIndicator.
- * @see com.vaadin.ui.AbstractField#setValue(java.lang.Object)
+ * @see com.vaadin.ui.AbstractField#setValue()
*/
@Override
public void setValue(Object newValue) {
@@ -149,21 +150,10 @@ public class ProgressIndicator extends AbstractField implements Property,
}
/**
- * @see com.vaadin.ui.AbstractField#toString()
- */
- @Override
- public String toString() {
- if (dataSource == null) {
- throw new IllegalStateException("Datasource must be set");
- }
- return dataSource.toString();
- }
-
- /**
* @see com.vaadin.ui.AbstractField#getType()
*/
@Override
- public Class<?> getType() {
+ public Class<? extends Number> getType() {
if (dataSource == null) {
throw new IllegalStateException("Datasource must be set");
}
@@ -257,4 +247,9 @@ public class ProgressIndicator extends AbstractField implements Property,
return pollingInterval;
}
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+
+ }
+
}
diff --git a/src/com/vaadin/ui/RichTextArea.java b/src/com/vaadin/ui/RichTextArea.java
index f4d88edc78..16d4761b40 100644
--- a/src/com/vaadin/ui/RichTextArea.java
+++ b/src/com/vaadin/ui/RichTextArea.java
@@ -10,8 +10,7 @@ import java.util.Map;
import com.vaadin.data.Property;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea;
-import com.vaadin.ui.ClientWidget.LoadStyle;
+import com.vaadin.terminal.Vaadin6Component;
/**
* A simple RichTextArea to edit HTML format text.
@@ -20,8 +19,8 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* {@link RichTextArea} may produce unexpected results as formatting is counted
* into length of field.
*/
-@ClientWidget(value = VRichTextArea.class, loadStyle = LoadStyle.LAZY)
-public class RichTextArea extends AbstractField {
+public class RichTextArea extends AbstractField<String> implements
+ Vaadin6Component {
/**
* Value formatter used to format the string contents.
@@ -104,7 +103,6 @@ public class RichTextArea extends AbstractField {
setCaption(caption);
}
- @Override
public void paintContent(PaintTarget target) throws PaintException {
if (selectAll) {
target.addAttribute("selectAll", true);
@@ -122,13 +120,13 @@ public class RichTextArea extends AbstractField {
}
target.addVariable(this, "text", value);
- super.paintContent(target);
}
@Override
public void setReadOnly(boolean readOnly) {
super.setReadOnly(readOnly);
// IE6 cannot support multi-classname selectors properly
+ // TODO Can be optimized now that support for I6 is dropped
if (readOnly) {
addStyleName("v-richtextarea-readonly");
} else {
@@ -175,8 +173,8 @@ public class RichTextArea extends AbstractField {
}
@Override
- public Object getValue() {
- Object v = super.getValue();
+ public String getValue() {
+ String v = super.getValue();
if (format == null || v == null) {
return v;
}
@@ -187,11 +185,7 @@ public class RichTextArea extends AbstractField {
}
}
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
-
- super.changeVariables(source, variables);
-
// Sets the text
if (variables.containsKey("text") && !isReadOnly()) {
@@ -221,7 +215,7 @@ public class RichTextArea extends AbstractField {
}
@Override
- public Class getType() {
+ public Class<String> getType() {
return String.class;
}
@@ -342,7 +336,7 @@ public class RichTextArea extends AbstractField {
@Override
protected boolean isEmpty() {
- return super.isEmpty() || toString().length() == 0;
+ return super.isEmpty() || getValue().length() == 0;
}
}
diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java
new file mode 100644
index 0000000000..405ae8da93
--- /dev/null
+++ b/src/com/vaadin/ui/Root.java
@@ -0,0 +1,1590 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.vaadin.Application;
+import com.vaadin.annotations.EagerInit;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.event.ActionManager;
+import com.vaadin.event.MouseEvents.ClickEvent;
+import com.vaadin.event.MouseEvents.ClickListener;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.terminal.WrappedRequest.BrowserDetails;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
+import com.vaadin.terminal.gwt.client.ui.root.RootServerRpc;
+import com.vaadin.terminal.gwt.client.ui.root.RootState;
+import com.vaadin.terminal.gwt.client.ui.root.VRoot;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.Window.CloseListener;
+
+/**
+ * The topmost component in any component hierarchy. There is one root for every
+ * Vaadin instance in a browser window. A root may either represent an entire
+ * browser window (or tab) or some part of a html page where a Vaadin
+ * application is embedded.
+ * <p>
+ * The root is the server side entry point for various client side features that
+ * are not represented as components added to a layout, e.g notifications, sub
+ * windows, and executing javascript in the browser.
+ * </p>
+ * <p>
+ * When a new application instance is needed, typically because the user opens
+ * the application in a browser window,
+ * {@link Application#gerRoot(WrappedRequest)} is invoked to get a root. That
+ * method does by default create a root according to the
+ * {@value Application#ROOT_PARAMETER} parameter from web.xml.
+ * </p>
+ * <p>
+ * After a root has been created by the application, it is initialized using
+ * {@link #init(WrappedRequest)}. This method is intended to be overridden by
+ * the developer to add components to the user interface and initialize
+ * non-component functionality. The component hierarchy is initialized by
+ * passing a {@link ComponentContainer} with the main layout of the view to
+ * {@link #setContent(ComponentContainer)}.
+ * </p>
+ * <p>
+ * If a {@link EagerInit} annotation is present on a class extending
+ * <code>Root</code>, the framework will use a faster initialization method
+ * which will not ensure that {@link BrowserDetails} are present in the
+ * {@link WrappedRequest} passed to the init method.
+ * </p>
+ *
+ * @see #init(WrappedRequest)
+ * @see Application#getRoot(WrappedRequest)
+ *
+ * @since 7.0
+ */
+public abstract class Root extends AbstractComponentContainer implements
+ Action.Container, Action.Notifier, Vaadin6Component {
+
+ /**
+ * Listener that gets notified when the size of the browser window
+ * containing the root has changed.
+ *
+ * @see Root#addListener(BrowserWindowResizeListener)
+ */
+ public interface BrowserWindowResizeListener extends Serializable {
+ /**
+ * Invoked when the browser window containing a Root has been resized.
+ *
+ * @param event
+ * a browser window resize event
+ */
+ public void browserWindowResized(BrowserWindowResizeEvent event);
+ }
+
+ /**
+ * Event that is fired when a browser window containing a root is resized.
+ */
+ public class BrowserWindowResizeEvent extends Component.Event {
+
+ private final int width;
+ private final int height;
+
+ /**
+ * Creates a new event
+ *
+ * @param source
+ * the root for which the browser window has been resized
+ * @param width
+ * the new width of the browser window
+ * @param height
+ * the new height of the browser window
+ */
+ public BrowserWindowResizeEvent(Root source, int width, int height) {
+ super(source);
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public Root getSource() {
+ return (Root) super.getSource();
+ }
+
+ /**
+ * Gets the new browser window height
+ *
+ * @return an integer with the new pixel height of the browser window
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Gets the new browser window width
+ *
+ * @return an integer with the new pixel width of the browser window
+ */
+ public int getWidth() {
+ return width;
+ }
+ }
+
+ private static final Method BROWSWER_RESIZE_METHOD = ReflectTools
+ .findMethod(BrowserWindowResizeListener.class,
+ "browserWindowResized", BrowserWindowResizeEvent.class);
+
+ /**
+ * Listener that listens changes in URI fragment.
+ */
+ public interface FragmentChangedListener extends Serializable {
+ public void fragmentChanged(FragmentChangedEvent event);
+ }
+
+ /**
+ * Event fired when uri fragment changes.
+ */
+ public class FragmentChangedEvent extends Component.Event {
+
+ /**
+ * The new uri fragment
+ */
+ private final String fragment;
+
+ /**
+ * Creates a new instance of UriFragmentReader change event.
+ *
+ * @param source
+ * the Source of the event.
+ */
+ public FragmentChangedEvent(Root source, String fragment) {
+ super(source);
+ this.fragment = fragment;
+ }
+
+ /**
+ * Gets the root in which the fragment has changed.
+ *
+ * @return the root in which the fragment has changed
+ */
+ public Root getRoot() {
+ return (Root) getComponent();
+ }
+
+ /**
+ * Get the new fragment
+ *
+ * @return the new fragment
+ */
+ public String getFragment() {
+ return fragment;
+ }
+ }
+
+ /**
+ * Helper class to emulate the main window from Vaadin 6 using roots. This
+ * class should be used in the same way as Window used as a browser level
+ * window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication}
+ */
+ @Deprecated
+ @EagerInit
+ public static class LegacyWindow extends Root {
+ private String name;
+
+ /**
+ * Create a new legacy window
+ */
+ public LegacyWindow() {
+ super();
+ }
+
+ /**
+ * Creates a new legacy window with the given caption
+ *
+ * @param caption
+ * the caption of the window
+ */
+ public LegacyWindow(String caption) {
+ super(caption);
+ }
+
+ /**
+ * Creates a legacy window with the given caption and content layout
+ *
+ * @param caption
+ * @param content
+ */
+ public LegacyWindow(String caption, ComponentContainer content) {
+ super(caption, content);
+ }
+
+ @Override
+ protected void init(WrappedRequest request) {
+ // Just empty
+ }
+
+ /**
+ * Gets the unique name of the window. The name of the window is used to
+ * uniquely identify it.
+ * <p>
+ * The name also determines the URL that can be used for direct access
+ * to a window. All windows can be accessed through
+ * {@code http://host:port/app/win} where {@code http://host:port/app}
+ * is the application URL (as returned by {@link Application#getURL()}
+ * and {@code win} is the window name.
+ * </p>
+ * <p>
+ * Note! Portlets do not support direct window access through URLs.
+ * </p>
+ *
+ * @return the Name of the Window.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the unique name of the window. The name of the window is used to
+ * uniquely identify it inside the application.
+ * <p>
+ * The name also determines the URL that can be used for direct access
+ * to a window. All windows can be accessed through
+ * {@code http://host:port/app/win} where {@code http://host:port/app}
+ * is the application URL (as returned by {@link Application#getURL()}
+ * and {@code win} is the window name.
+ * </p>
+ * <p>
+ * This method can only be called before the window is added to an
+ * application.
+ * <p>
+ * Note! Portlets do not support direct window access through URLs.
+ * </p>
+ *
+ * @param name
+ * the new name for the window or null if the application
+ * should automatically assign a name to it
+ * @throws IllegalStateException
+ * if the window is attached to an application
+ */
+ public void setName(String name) {
+ this.name = name;
+ // The name can not be changed in application
+ if (getApplication() != null) {
+ throw new IllegalStateException(
+ "Window name can not be changed while "
+ + "the window is in application");
+ }
+
+ }
+
+ /**
+ * Gets the full URL of the window. The returned URL is window specific
+ * and can be used to directly refer to the window.
+ * <p>
+ * Note! This method can not be used for portlets.
+ * </p>
+ *
+ * @return the URL of the window or null if the window is not attached
+ * to an application
+ */
+ public URL getURL() {
+ Application application = getApplication();
+ if (application == null) {
+ return null;
+ }
+
+ try {
+ return new URL(application.getURL(), getName() + "/");
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(
+ "Internal problem getting window URL, please report");
+ }
+ }
+ }
+
+ private static final Method FRAGMENT_CHANGED_METHOD;
+
+ static {
+ try {
+ FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
+ .getDeclaredMethod("fragmentChanged",
+ new Class[] { FragmentChangedEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error finding methods in FragmentChangedListener");
+ }
+ }
+
+ /**
+ * A border style used for opening resources in a window without a border.
+ */
+ public static final int BORDER_NONE = 0;
+
+ /**
+ * A border style used for opening resources in a window with a minimal
+ * border.
+ */
+ public static final int BORDER_MINIMAL = 1;
+
+ /**
+ * A border style that indicates that the default border style should be
+ * used when opening resources.
+ */
+ public static final int BORDER_DEFAULT = 2;
+
+ /**
+ * The application to which this root belongs
+ */
+ private Application application;
+
+ /**
+ * A list of notifications that are waiting to be sent to the client.
+ * Cleared (set to null) when the notifications have been sent.
+ */
+ private List<Notification> notifications;
+
+ /**
+ * A list of javascript commands that are waiting to be sent to the client.
+ * Cleared (set to null) when the commands have been sent.
+ */
+ private List<String> jsExecQueue = null;
+
+ /**
+ * List of windows in this root.
+ */
+ private final LinkedHashSet<Window> windows = new LinkedHashSet<Window>();
+
+ /**
+ * Resources to be opened automatically on next repaint. The list is
+ * automatically cleared when it has been sent to the client.
+ */
+ private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>();
+
+ /**
+ * The component that should be scrolled into view after the next repaint.
+ * Null if nothing should be scrolled into view.
+ */
+ private Component scrollIntoView;
+
+ /**
+ * The id of this root, used to find the server side instance of the root
+ * form which a request originates. A negative value indicates that the root
+ * id has not yet been assigned by the Application.
+ *
+ * @see Application#nextRootId
+ */
+ private int rootId = -1;
+
+ /**
+ * Keeps track of the Actions added to this component, and manages the
+ * painting and handling as well.
+ */
+ protected ActionManager actionManager;
+
+ /**
+ * Thread local for keeping track of the current root.
+ */
+ private static final ThreadLocal<Root> currentRoot = new ThreadLocal<Root>();
+
+ private int browserWindowWidth = -1;
+ private int browserWindowHeight = -1;
+
+ /** Identifies the click event */
+ private static final String CLICK_EVENT_ID = VRoot.CLICK_EVENT_ID;
+
+ private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
+ this);
+
+ private RootServerRpc rpc = new RootServerRpc() {
+ public void click(MouseEventDetails mouseDetails) {
+ fireEvent(new ClickEvent(Root.this, mouseDetails));
+ }
+ };
+
+ /**
+ * Creates a new empty root without a caption. This root will have a
+ * {@link VerticalLayout} with margins enabled as its content.
+ */
+ public Root() {
+ this((ComponentContainer) null);
+ }
+
+ /**
+ * Creates a new root with the given component container as its content.
+ *
+ * @param content
+ * the content container to use as this roots content.
+ *
+ * @see #setContent(ComponentContainer)
+ */
+ public Root(ComponentContainer content) {
+ registerRpc(rpc);
+ setSizeFull();
+ setContent(content);
+ }
+
+ /**
+ * Creates a new empty root with the given caption. This root will have a
+ * {@link VerticalLayout} with margins enabled as its content.
+ *
+ * @param caption
+ * the caption of the root, used as the page title if there's
+ * nothing but the application on the web page
+ *
+ * @see #setCaption(String)
+ */
+ public Root(String caption) {
+ this((ComponentContainer) null);
+ setCaption(caption);
+ }
+
+ /**
+ * Creates a new root with the given caption and content.
+ *
+ * @param caption
+ * the caption of the root, used as the page title if there's
+ * nothing but the application on the web page
+ * @param content
+ * the content container to use as this roots content.
+ *
+ * @see #setContent(ComponentContainer)
+ * @see #setCaption(String)
+ */
+ public Root(String caption, ComponentContainer content) {
+ this(content);
+ setCaption(caption);
+ }
+
+ @Override
+ public RootState getState() {
+ return (RootState) super.getState();
+ }
+
+ @Override
+ protected ComponentState createState() {
+ // This is a workaround for a problem with creating the correct state
+ // object during build
+ return new RootState();
+ }
+
+ /**
+ * Overridden to return a value instead of referring to the parent.
+ *
+ * @return this root
+ *
+ * @see com.vaadin.ui.AbstractComponent#getRoot()
+ */
+ @Override
+ public Root getRoot() {
+ return this;
+ }
+
+ public void replaceComponent(Component oldComponent, Component newComponent) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Application getApplication() {
+ return application;
+ }
+
+ public void paintContent(PaintTarget target) throws PaintException {
+ // Open requested resource
+ synchronized (openList) {
+ if (!openList.isEmpty()) {
+ for (final Iterator<OpenResource> i = openList.iterator(); i
+ .hasNext();) {
+ (i.next()).paintContent(target);
+ }
+ openList.clear();
+ }
+ }
+
+ // Paint notifications
+ if (notifications != null) {
+ target.startTag("notifications");
+ for (final Iterator<Notification> it = notifications.iterator(); it
+ .hasNext();) {
+ final Notification n = it.next();
+ target.startTag("notification");
+ if (n.getCaption() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_CAPTION,
+ n.getCaption());
+ }
+ if (n.getDescription() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_MESSAGE,
+ n.getDescription());
+ }
+ if (n.getIcon() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_ICON,
+ n.getIcon());
+ }
+ if (!n.isHtmlContentAllowed()) {
+ target.addAttribute(
+ VRoot.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, true);
+ }
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_POSITION,
+ n.getPosition());
+ target.addAttribute(VNotification.ATTRIBUTE_NOTIFICATION_DELAY,
+ n.getDelayMsec());
+ if (n.getStyleName() != null) {
+ target.addAttribute(
+ VNotification.ATTRIBUTE_NOTIFICATION_STYLE,
+ n.getStyleName());
+ }
+ target.endTag("notification");
+ }
+ target.endTag("notifications");
+ notifications = null;
+ }
+
+ // Add executable javascripts if needed
+ if (jsExecQueue != null) {
+ for (String script : jsExecQueue) {
+ target.startTag("execJS");
+ target.addAttribute("script", script);
+ target.endTag("execJS");
+ }
+ jsExecQueue = null;
+ }
+
+ if (scrollIntoView != null) {
+ target.addAttribute("scrollTo", scrollIntoView);
+ scrollIntoView = null;
+ }
+
+ if (pendingFocus != null) {
+ // ensure focused component is still attached to this main window
+ if (pendingFocus.getRoot() == this
+ || (pendingFocus.getRoot() != null && pendingFocus
+ .getRoot().getParent() == this)) {
+ target.addAttribute("focused", pendingFocus);
+ }
+ pendingFocus = null;
+ }
+
+ if (actionManager != null) {
+ actionManager.paintActions(null, target);
+ }
+
+ if (fragment != null) {
+ target.addAttribute(VRoot.FRAGMENT_VARIABLE, fragment);
+ }
+
+ if (isResizeLazy()) {
+ target.addAttribute(VRoot.RESIZE_LAZY, true);
+ }
+ }
+
+ /**
+ * Fire a click event to all click listeners.
+ *
+ * @param object
+ * The raw "value" of the variable change from the client side.
+ */
+ private void fireClick(Map<String, Object> parameters) {
+ MouseEventDetails mouseDetails = MouseEventDetails
+ .deSerialize((String) parameters.get("mouseDetails"));
+ fireEvent(new ClickEvent(this, mouseDetails));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ if (variables.containsKey(CLICK_EVENT_ID)) {
+ fireClick((Map<String, Object>) variables.get(CLICK_EVENT_ID));
+ }
+
+ // Actions
+ if (actionManager != null) {
+ actionManager.handleActions(variables, this);
+ }
+
+ if (variables.containsKey(VRoot.FRAGMENT_VARIABLE)) {
+ String fragment = (String) variables.get(VRoot.FRAGMENT_VARIABLE);
+ setFragment(fragment, true);
+ }
+
+ boolean sendResizeEvent = false;
+ if (variables.containsKey("height")) {
+ browserWindowHeight = ((Integer) variables.get("height"))
+ .intValue();
+ sendResizeEvent = true;
+ }
+ if (variables.containsKey("width")) {
+ browserWindowWidth = ((Integer) variables.get("width")).intValue();
+ sendResizeEvent = true;
+ }
+ if (sendResizeEvent) {
+ fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
+ browserWindowHeight));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
+ */
+ public Iterator<Component> getComponentIterator() {
+ return Collections.singleton((Component) getContent()).iterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.ComponentContainer#getComponentCount()
+ */
+ public int getComponentCount() {
+ return getContent() == null ? 0 : 1;
+ }
+
+ /**
+ * Sets the application to which this root is assigned. It is not legal to
+ * change the application once it has been set nor to set a
+ * <code>null</code> application.
+ * <p>
+ * This method is mainly intended for internal use by the framework.
+ * </p>
+ *
+ * @param application
+ * the application to set
+ *
+ * @throws IllegalStateException
+ * if the application has already been set
+ *
+ * @see #getApplication()
+ */
+ public void setApplication(Application application) {
+ if ((application == null) == (this.application == null)) {
+ throw new IllegalStateException("Application has already been set");
+ } else {
+ this.application = application;
+ }
+
+ if (application != null) {
+ attach();
+ } else {
+ detach();
+ }
+ }
+
+ /**
+ * Sets the id of this root within its application. The root id is used to
+ * route requests to the right root.
+ * <p>
+ * This method is mainly intended for internal use by the framework.
+ * </p>
+ *
+ * @param rootId
+ * the id of this root
+ *
+ * @throws IllegalStateException
+ * if the root id has already been set
+ *
+ * @see #getRootId()
+ */
+ public void setRootId(int rootId) {
+ if (this.rootId != -1) {
+ throw new IllegalStateException("Root id has already been defined");
+ }
+ this.rootId = rootId;
+ }
+
+ /**
+ * Gets the id of the root, used to identify this root within its
+ * application when processing requests. The root id should be present in
+ * every request to the server that originates from this root.
+ * {@link Application#getRootForRequest(WrappedRequest)} uses this id to
+ * find the route to which the request belongs.
+ *
+ * @return
+ */
+ public int getRootId() {
+ return rootId;
+ }
+
+ /**
+ * Adds a window as a subwindow inside this root. To open a new browser
+ * window or tab, you should instead use {@link open(Resource)} with an url
+ * pointing to this application and ensure
+ * {@link Application#getRoot(WrappedRequest)} returns an appropriate root
+ * for the request.
+ *
+ * @param window
+ * @throws IllegalArgumentException
+ * if the window is already added to an application
+ * @throws NullPointerException
+ * if the given <code>Window</code> is <code>null</code>.
+ */
+ public void addWindow(Window window) throws IllegalArgumentException,
+ NullPointerException {
+
+ if (window == null) {
+ throw new NullPointerException("Argument must not be null");
+ }
+
+ if (window.getApplication() != null) {
+ throw new IllegalArgumentException(
+ "Window is already attached to an application.");
+ }
+
+ attachWindow(window);
+ }
+
+ /**
+ * Helper method to attach a window.
+ *
+ * @param w
+ * the window to add
+ */
+ private void attachWindow(Window w) {
+ windows.add(w);
+ w.setParent(this);
+ requestRepaint();
+ }
+
+ /**
+ * Remove the given subwindow from this root.
+ *
+ * Since Vaadin 6.5, {@link CloseListener}s are called also when explicitly
+ * removing a window by calling this method.
+ *
+ * Since Vaadin 6.5, returns a boolean indicating if the window was removed
+ * or not.
+ *
+ * @param window
+ * Window to be removed.
+ * @return true if the subwindow was removed, false otherwise
+ */
+ public boolean removeWindow(Window window) {
+ if (!windows.remove(window)) {
+ // Window window is not a subwindow of this root.
+ return false;
+ }
+ window.setParent(null);
+ window.fireClose();
+ requestRepaint();
+
+ return true;
+ }
+
+ /**
+ * Gets all the windows added to this root.
+ *
+ * @return an unmodifiable collection of windows
+ */
+ public Collection<Window> getWindows() {
+ return Collections.unmodifiableCollection(windows);
+ }
+
+ @Override
+ public void focus() {
+ super.focus();
+ }
+
+ /**
+ * Component that should be focused after the next repaint. Null if no focus
+ * change should take place.
+ */
+ private Focusable pendingFocus;
+
+ /**
+ * The current URI fragment.
+ */
+ private String fragment;
+
+ private boolean resizeLazy = false;
+
+ /**
+ * This method is used by Component.Focusable objects to request focus to
+ * themselves. Focus renders must be handled at window level (instead of
+ * Component.Focusable) due we want the last focused component to be focused
+ * in client too. Not the one that is rendered last (the case we'd get if
+ * implemented in Focusable only).
+ *
+ * To focus component from Vaadin application, use Focusable.focus(). See
+ * {@link Focusable}.
+ *
+ * @param focusable
+ * to be focused on next paint
+ */
+ public void setFocusedComponent(Focusable focusable) {
+ pendingFocus = focusable;
+ requestRepaint();
+ }
+
+ /**
+ * Shows a notification message on the middle of the root. The message
+ * automatically disappears ("humanized message").
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The message
+ */
+ public void showNotification(String caption) {
+ addNotification(new Notification(caption));
+ }
+
+ /**
+ * Shows a notification message the root. The position and behavior of the
+ * message depends on the type, which is one of the basic types defined in
+ * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption is
+ * rendered as html.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The message
+ * @param type
+ * The message type
+ */
+ public void showNotification(String caption, int type) {
+ addNotification(new Notification(caption, type));
+ }
+
+ /**
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description on the middle of the root. The message automatically
+ * disappears ("humanized message").
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are rendered as html.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The caption of the message
+ * @param description
+ * The message description
+ *
+ */
+ public void showNotification(String caption, String description) {
+ addNotification(new Notification(caption, description));
+ }
+
+ /**
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description. The position and behavior of the message depends on the
+ * type, which is one of the basic types defined in {@link Notification},
+ * for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to to avoid XSS vulnerabilities as the caption and
+ * description are rendered as html.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The caption of the message
+ * @param description
+ * The message description
+ * @param type
+ * The message type
+ */
+ public void showNotification(String caption, String description, int type) {
+ addNotification(new Notification(caption, description, type));
+ }
+
+ /**
+ * Shows a notification consisting of a bigger caption and a smaller
+ * description. The position and behavior of the message depends on the
+ * type, which is one of the basic types defined in {@link Notification},
+ * for instance Notification.TYPE_WARNING_MESSAGE.
+ *
+ * Care should be taken to avoid XSS vulnerabilities if html content is
+ * allowed.
+ *
+ * @see #showNotification(Notification)
+ * @see Notification
+ *
+ * @param caption
+ * The message caption
+ * @param description
+ * The message description
+ * @param type
+ * The type of message
+ * @param htmlContentAllowed
+ * Whether html in the caption and description should be
+ * displayed as html or as plain text
+ */
+ public void showNotification(String caption, String description, int type,
+ boolean htmlContentAllowed) {
+ addNotification(new Notification(caption, description, type,
+ htmlContentAllowed));
+ }
+
+ /**
+ * Shows a notification message.
+ *
+ * @see Notification
+ * @see #showNotification(String)
+ * @see #showNotification(String, int)
+ * @see #showNotification(String, String)
+ * @see #showNotification(String, String, int)
+ *
+ * @param notification
+ * The notification message to show
+ */
+ public void showNotification(Notification notification) {
+ addNotification(notification);
+ }
+
+ /**
+ * Internal helper method to actually add a notification.
+ *
+ * @param notification
+ * the notification to add
+ */
+ private void addNotification(Notification notification) {
+ if (notifications == null) {
+ notifications = new LinkedList<Notification>();
+ }
+ notifications.add(notification);
+ requestRepaint();
+ }
+
+ /**
+ * Executes JavaScript in this root.
+ *
+ * <p>
+ * This method allows one to inject javascript from the server to client. A
+ * client implementation is not required to implement this functionality,
+ * but currently all web-based clients do implement this.
+ * </p>
+ *
+ * <p>
+ * Executing javascript this way often leads to cross-browser compatibility
+ * issues and regressions that are hard to resolve. Use of this method
+ * should be avoided and instead it is recommended to create new widgets
+ * with GWT. For more info on creating own, reusable client-side widgets in
+ * Java, read the corresponding chapter in Book of Vaadin.
+ * </p>
+ *
+ * @param script
+ * JavaScript snippet that will be executed.
+ */
+ public void executeJavaScript(String script) {
+ if (jsExecQueue == null) {
+ jsExecQueue = new ArrayList<String>();
+ }
+
+ jsExecQueue.add(script);
+
+ requestRepaint();
+ }
+
+ /**
+ * Scrolls any component between the component and root to a suitable
+ * position so the component is visible to the user. The given component
+ * must belong to this root.
+ *
+ * @param component
+ * the component to be scrolled into view
+ * @throws IllegalArgumentException
+ * if {@code component} does not belong to this root
+ */
+ public void scrollIntoView(Component component)
+ throws IllegalArgumentException {
+ if (component.getRoot() != this) {
+ throw new IllegalArgumentException(
+ "The component where to scroll must belong to this root.");
+ }
+ scrollIntoView = component;
+ requestRepaint();
+ }
+
+ /**
+ * Gets the content of this root. The content is a component container that
+ * serves as the outermost item of the visual contents of this root.
+ *
+ * @return a component container to use as content
+ *
+ * @see #setContent(ComponentContainer)
+ * @see #createDefaultLayout()
+ */
+ public ComponentContainer getContent() {
+ return (ComponentContainer) getState().getContent();
+ }
+
+ /**
+ * Helper method to create the default content layout that is used if no
+ * content has not been explicitly defined.
+ *
+ * @return a newly created layout
+ */
+ private static VerticalLayout createDefaultLayout() {
+ VerticalLayout layout = new VerticalLayout();
+ layout.setMargin(true);
+ return layout;
+ }
+
+ /**
+ * Sets the content of this root. The content is a component container that
+ * serves as the outermost item of the visual contents of this root. If no
+ * content has been set, a {@link VerticalLayout} with margins enabled will
+ * be used by default - see {@link #createDefaultLayout()}. The content can
+ * also be set in a constructor.
+ *
+ * @return a component container to use as content
+ *
+ * @see #Root(ComponentContainer)
+ * @see #createDefaultLayout()
+ */
+ public void setContent(ComponentContainer content) {
+ if (content == null) {
+ content = createDefaultLayout();
+ }
+
+ if (getState().getContent() != null) {
+ super.removeComponent((Component) getState().getContent());
+ }
+ getState().setContent(content);
+ if (content != null) {
+ super.addComponent(content);
+ }
+ }
+
+ /**
+ * Adds a component to this root. The component is not added directly to the
+ * root, but instead to the content container ({@link #getContent()}).
+ *
+ * @param component
+ * the component to add to this root
+ *
+ * @see #getContent()
+ */
+ @Override
+ public void addComponent(Component component) {
+ getContent().addComponent(component);
+ }
+
+ /**
+ * This implementation removes the component from the content container (
+ * {@link #getContent()}) instead of from the actual root.
+ */
+ @Override
+ public void removeComponent(Component component) {
+ getContent().removeComponent(component);
+ }
+
+ /**
+ * This implementation removes the components from the content container (
+ * {@link #getContent()}) instead of from the actual root.
+ */
+ @Override
+ public void removeAllComponents() {
+ getContent().removeAllComponents();
+ }
+
+ /**
+ * Internal initialization method, should not be overridden. This method is
+ * not declared as final because that would break compatibility with e.g.
+ * CDI.
+ *
+ * @param request
+ * the initialization request
+ */
+ public void doInit(WrappedRequest request) {
+ BrowserDetails browserDetails = request.getBrowserDetails();
+ if (browserDetails != null) {
+ fragment = browserDetails.getUriFragment();
+ }
+
+ // Call the init overridden by the application developer
+ init(request);
+ }
+
+ /**
+ * Initializes this root. This method is intended to be overridden by
+ * subclasses to build the view and configure non-component functionality.
+ * Performing the initialization in a constructor is not suggested as the
+ * state of the root is not properly set up when the constructor is invoked.
+ * <p>
+ * The {@link WrappedRequest} can be used to get information about the
+ * request that caused this root to be created. By default, the
+ * {@link BrowserDetails} will be available in the request. If the browser
+ * details are not required, loading the application in the browser can take
+ * some shortcuts giving a faster initial rendering. This can be indicated
+ * by adding the {@link EagerInit} annotation to the Root class.
+ * </p>
+ *
+ * @param request
+ * the wrapped request that caused this root to be created
+ */
+ protected abstract void init(WrappedRequest request);
+
+ /**
+ * Sets the thread local for the current root. This method is used by the
+ * framework to set the current application whenever a new request is
+ * processed and it is cleared when the request has been processed.
+ * <p>
+ * The application developer can also use this method to define the current
+ * root outside the normal request handling, e.g. when initiating custom
+ * background threads.
+ * </p>
+ *
+ * @param root
+ * the root to register as the current root
+ *
+ * @see #getCurrentRoot()
+ * @see ThreadLocal
+ */
+ public static void setCurrentRoot(Root root) {
+ currentRoot.set(root);
+ }
+
+ /**
+ * Gets the currently used root. The current root is automatically defined
+ * when processing requests to the server. In other cases, (e.g. from
+ * background threads), the current root is not automatically defined.
+ *
+ * @return the current root instance if available, otherwise
+ * <code>null</code>
+ *
+ * @see #setCurrentRoot(Root)
+ */
+ public static Root getCurrentRoot() {
+ return currentRoot.get();
+ }
+
+ /**
+ * Opens the given resource in this root. The contents of this Root is
+ * replaced by the {@code Resource}.
+ *
+ * @param resource
+ * the resource to show in this root
+ */
+ public void open(Resource resource) {
+ synchronized (openList) {
+ if (!openList.contains(resource)) {
+ openList.add(new OpenResource(resource, null, -1, -1,
+ BORDER_DEFAULT));
+ }
+ }
+ requestRepaint();
+ }
+
+ /* ********************************************************************* */
+
+ /**
+ * Opens the given resource in a window with the given name.
+ * <p>
+ * The supplied {@code windowName} is used as the target name in a
+ * window.open call in the client. This means that special values such as
+ * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
+ * <code>null</code> window name is also a special case.
+ * </p>
+ * <p>
+ * "", null and "_self" as {@code windowName} all causes the resource to be
+ * opened in the current window, replacing any old contents. For
+ * downloadable content you should avoid "_self" as "_self" causes the
+ * client to skip rendering of any other changes as it considers them
+ * irrelevant (the page will be replaced by the resource). This can speed up
+ * the opening of a resource, but it might also put the client side into an
+ * inconsistent state if the window content is not completely replaced e.g.,
+ * if the resource is downloaded instead of displayed in the browser.
+ * </p>
+ * <p>
+ * "_blank" as {@code windowName} causes the resource to always be opened in
+ * a new window or tab (depends on the browser and browser settings).
+ * </p>
+ * <p>
+ * "_top" and "_parent" as {@code windowName} works as specified by the HTML
+ * standard.
+ * </p>
+ * <p>
+ * Any other {@code windowName} will open the resource in a window with that
+ * name, either by opening a new window/tab in the browser or by replacing
+ * the contents of an existing window with that name.
+ * </p>
+ *
+ * @param resource
+ * the resource.
+ * @param windowName
+ * the name of the window.
+ */
+ public void open(Resource resource, String windowName) {
+ synchronized (openList) {
+ if (!openList.contains(resource)) {
+ openList.add(new OpenResource(resource, windowName, -1, -1,
+ BORDER_DEFAULT));
+ }
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Opens the given resource in a window with the given size, border and
+ * name. For more information on the meaning of {@code windowName}, see
+ * {@link #open(Resource, String)}.
+ *
+ * @param resource
+ * the resource.
+ * @param windowName
+ * the name of the window.
+ * @param width
+ * the width of the window in pixels
+ * @param height
+ * the height of the window in pixels
+ * @param border
+ * the border style of the window. See {@link #BORDER_NONE
+ * Window.BORDER_* constants}
+ */
+ public void open(Resource resource, String windowName, int width,
+ int height, int border) {
+ synchronized (openList) {
+ if (!openList.contains(resource)) {
+ openList.add(new OpenResource(resource, windowName, width,
+ height, border));
+ }
+ }
+ requestRepaint();
+ }
+
+ /**
+ * Private class for storing properties related to opening resources.
+ */
+ private class OpenResource implements Serializable {
+
+ /**
+ * The resource to open
+ */
+ private final Resource resource;
+
+ /**
+ * The name of the target window
+ */
+ private final String name;
+
+ /**
+ * The width of the target window
+ */
+ private final int width;
+
+ /**
+ * The height of the target window
+ */
+ private final int height;
+
+ /**
+ * The border style of the target window
+ */
+ private final int border;
+
+ /**
+ * Creates a new open resource.
+ *
+ * @param resource
+ * The resource to open
+ * @param name
+ * The name of the target window
+ * @param width
+ * The width of the target window
+ * @param height
+ * The height of the target window
+ * @param border
+ * The border style of the target window
+ */
+ private OpenResource(Resource resource, String name, int width,
+ int height, int border) {
+ this.resource = resource;
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ this.border = border;
+ }
+
+ /**
+ * Paints the open request. Should be painted inside the window.
+ *
+ * @param target
+ * the paint target
+ * @throws PaintException
+ * if the paint operation fails
+ */
+ private void paintContent(PaintTarget target) throws PaintException {
+ target.startTag("open");
+ target.addAttribute("src", resource);
+ if (name != null && name.length() > 0) {
+ target.addAttribute("name", name);
+ }
+ if (width >= 0) {
+ target.addAttribute("width", width);
+ }
+ if (height >= 0) {
+ target.addAttribute("height", height);
+ }
+ switch (border) {
+ case BORDER_MINIMAL:
+ target.addAttribute("border", "minimal");
+ break;
+ case BORDER_NONE:
+ target.addAttribute("border", "none");
+ break;
+ }
+
+ target.endTag("open");
+ }
+ }
+
+ public void setScrollTop(int scrollTop) {
+ throw new RuntimeException("Not yet implemented");
+ }
+
+ @Override
+ protected ActionManager getActionManager() {
+ if (actionManager == null) {
+ actionManager = new ActionManager(this);
+ }
+ return actionManager;
+ }
+
+ public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
+ T action) {
+ getActionManager().addAction(action);
+ }
+
+ public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
+ T action) {
+ if (actionManager != null) {
+ actionManager.removeAction(action);
+ }
+ }
+
+ public void addActionHandler(Handler actionHandler) {
+ getActionManager().addActionHandler(actionHandler);
+ }
+
+ public void removeActionHandler(Handler actionHandler) {
+ if (actionManager != null) {
+ actionManager.removeActionHandler(actionHandler);
+ }
+ }
+
+ /**
+ * Should resize operations be lazy, i.e. should there be a delay before
+ * layout sizes are recalculated. Speeds up resize operations in slow UIs
+ * with the penalty of slightly decreased usability.
+ * <p>
+ * Default value: <code>false</code>
+ *
+ * @param resizeLazy
+ * true to use a delay before recalculating sizes, false to
+ * calculate immediately.
+ */
+ public void setResizeLazy(boolean resizeLazy) {
+ this.resizeLazy = resizeLazy;
+ requestRepaint();
+ }
+
+ /**
+ * Checks whether lazy resize is enabled.
+ *
+ * @return <code>true</code> if lazy resize is enabled, <code>false</code>
+ * if lazy resize is not enabled
+ */
+ public boolean isResizeLazy() {
+ return resizeLazy;
+ }
+
+ /**
+ * Add a click listener to the Root. The listener is called whenever the
+ * user clicks inside the Root. Also when the click targets a component
+ * inside the Root, provided the targeted component does not prevent the
+ * click event from propagating.
+ *
+ * Use {@link #removeListener(ClickListener)} to remove the listener.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void addListener(ClickListener listener) {
+ addListener(CLICK_EVENT_ID, ClickEvent.class, listener,
+ ClickListener.clickMethod);
+ }
+
+ /**
+ * Remove a click listener from the Root. The listener should earlier have
+ * been added using {@link #addListener(ClickListener)}.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public void removeListener(ClickListener listener) {
+ removeListener(CLICK_EVENT_ID, ClickEvent.class, listener);
+ }
+
+ public void addListener(FragmentChangedListener listener) {
+ addListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ public void removeListener(FragmentChangedListener listener) {
+ removeListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ /**
+ * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @param fireEvent
+ * true to fire event
+ * @see FragmentChangedEvent
+ * @see FragmentChangedListener
+ */
+ public void setFragment(String newFragment, boolean fireEvents) {
+ if (newFragment == null) {
+ throw new NullPointerException("The fragment may not be null");
+ }
+ if (!newFragment.equals(fragment)) {
+ fragment = newFragment;
+ if (fireEvents) {
+ fireEvent(new FragmentChangedEvent(this, newFragment));
+ }
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @see FragmentChangedEvent
+ * @see FragmentChangedListener
+ */
+ public void setFragment(String newFragment) {
+ setFragment(newFragment, true);
+ }
+
+ /**
+ * Gets currently set URI fragment.
+ * <p>
+ * To listen changes in fragment, hook a {@link FragmentChangedListener}.
+ *
+ * @return the current fragment in browser uri or null if not known
+ */
+ public String getFragment() {
+ return fragment;
+ }
+
+ /**
+ * Adds a new {@link BrowserWindowResizeListener} to this root. The listener
+ * will be notified whenever the browser window within which this root
+ * resides is resized.
+ *
+ * @param resizeListener
+ * the listener to add
+ *
+ * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
+ * @see #setResizeLazy(boolean)
+ */
+ public void addListener(BrowserWindowResizeListener resizeListener) {
+ addListener(BrowserWindowResizeEvent.class, resizeListener,
+ BROWSWER_RESIZE_METHOD);
+ }
+
+ /**
+ * Removes a {@link BrowserWindowResizeListener} from this root. The
+ * listener will no longer be notified when the browser window is resized.
+ *
+ * @param resizeListener
+ * the listener to remove
+ */
+ public void removeListener(BrowserWindowResizeListener resizeListener) {
+ removeListener(BrowserWindowResizeEvent.class, resizeListener,
+ BROWSWER_RESIZE_METHOD);
+ }
+
+ /**
+ * Gets the last known height of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window height in pixels
+ */
+ public int getBrowserWindowHeight() {
+ return browserWindowHeight;
+ }
+
+ /**
+ * Gets the last known width of the browser window in which this root
+ * resides.
+ *
+ * @return the browser window width in pixels
+ */
+ public int getBrowserWindowWidth() {
+ return browserWindowWidth;
+ }
+
+ /**
+ * Notifies the child components and windows that the root is attached to
+ * the application.
+ */
+ @Override
+ public void attach() {
+ super.attach();
+ for (Window w : windows) {
+ w.attach();
+ }
+ }
+
+ /**
+ * Notifies the child components and windows that the root is detached from
+ * the application.
+ */
+ @Override
+ public void detach() {
+ super.detach();
+ for (Window w : windows) {
+ w.detach();
+ }
+ }
+
+ @Override
+ public boolean isConnectorEnabled() {
+ // TODO How can a Root be invisible? What does it mean?
+ return isVisible() && isEnabled();
+ }
+
+ public DirtyConnectorTracker getDirtyConnectorTracker() {
+ return dirtyConnectorTracker;
+ }
+
+ public void componentAttached(Component component) {
+ getDirtyConnectorTracker().componentAttached(component);
+ }
+
+ public void componentDetached(Component component) {
+ getDirtyConnectorTracker().componentDetached(component);
+ }
+
+}
diff --git a/src/com/vaadin/ui/Select.java b/src/com/vaadin/ui/Select.java
index 0ea331dc40..5398f11391 100644
--- a/src/com/vaadin/ui/Select.java
+++ b/src/com/vaadin/ui/Select.java
@@ -23,7 +23,6 @@ import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
/**
* <p>
@@ -44,7 +43,6 @@ import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VFilterSelect.class)
public class Select extends AbstractSelect implements AbstractSelect.Filtering,
FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {
@@ -147,11 +145,6 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering,
target.addAttribute("modified", true);
}
- // Adds the required attribute
- if (!isReadOnly() && isRequired()) {
- target.addAttribute("required", true);
- }
-
if (isNewItemsAllowed()) {
target.addAttribute("allownewitem", true);
}
@@ -271,11 +264,6 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering,
currentPage = -1; // current page is always set by client
optionRequest = true;
-
- // Hide the error indicator if needed
- if (shouldHideErrors()) {
- target.addAttribute("hideErrors", true);
- }
}
/**
@@ -754,11 +742,15 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering,
* @deprecated use {@link ListSelect}, {@link OptionGroup} or
* {@link TwinColSelect} instead
* @see com.vaadin.ui.AbstractSelect#setMultiSelect(boolean)
+ * @throws UnsupportedOperationException
+ * if trying to activate multiselect mode
*/
@Deprecated
@Override
public void setMultiSelect(boolean multiSelect) {
- super.setMultiSelect(multiSelect);
+ if (multiSelect) {
+ throw new UnsupportedOperationException("Multiselect not supported");
+ }
}
/**
diff --git a/src/com/vaadin/ui/Slider.java b/src/com/vaadin/ui/Slider.java
index 1d67a6b12c..dc5dc0be98 100644
--- a/src/com/vaadin/ui/Slider.java
+++ b/src/com/vaadin/ui/Slider.java
@@ -8,7 +8,7 @@ import java.util.Map;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VSlider;
+import com.vaadin.terminal.Vaadin6Component;
/**
* A component for selecting a numerical value within a range.
@@ -46,22 +46,12 @@ import com.vaadin.terminal.gwt.client.ui.VSlider;
*
* @author Vaadin Ltd.
*/
-@ClientWidget(VSlider.class)
-public class Slider extends AbstractField {
+public class Slider extends AbstractField<Double> implements Vaadin6Component {
public static final int ORIENTATION_HORIZONTAL = 0;
public static final int ORIENTATION_VERTICAL = 1;
- /**
- * Style constant representing a scrollbar styled slider. Use this with
- * {@link #addStyleName(String)}. Default styling usually represents a
- * common slider found e.g. in Adobe Photoshop. The client side
- * implementation dictates how different styles will look.
- */
- @Deprecated
- public static final String STYLE_SCROLLBAR = "scrollbar";
-
/** Minimum value of slider */
private double min = 0;
@@ -80,35 +70,6 @@ public class Slider extends AbstractField {
private int orientation = ORIENTATION_HORIZONTAL;
/**
- * Slider size in pixels. In horizontal mode, if set to -1, allow 100% width
- * of container. In vertical mode, if set to -1, default height is
- * determined by the client-side implementation.
- *
- * @deprecated
- */
- @Deprecated
- private int size = -1;
-
- /**
- * Handle (draggable control element) size in percents relative to base
- * size. Must be a value between 1-99. Other values are converted to nearest
- * bound. A negative value sets the width to auto (client-side
- * implementation calculates).
- *
- * @deprecated The size is dictated by the current theme.
- */
- @Deprecated
- private int handleSize = -1;
-
- /**
- * Show arrows that can be pressed to slide the handle in some increments
- * (client-side implementation decides the increment, usually somewhere
- * between 5-10% of slide range).
- */
- @Deprecated
- private final boolean arrows = false;
-
- /**
* Default slider constructor. Sets all values to defaults and the slide
* handle at minimum value.
*
@@ -198,17 +159,8 @@ public class Slider extends AbstractField {
*/
public void setMax(double max) {
this.max = max;
- try {
- if ((new Double(getValue().toString())).doubleValue() > max) {
- super.setValue(new Double(max));
- }
- } catch (final ClassCastException e) {
- // FIXME: Handle exception
- /*
- * Where does ClassCastException come from? Can't see any casts
- * above
- */
- super.setValue(new Double(max));
+ if (getValue() > max) {
+ setValue(max);
}
requestRepaint();
}
@@ -231,17 +183,8 @@ public class Slider extends AbstractField {
*/
public void setMin(double min) {
this.min = min;
- try {
- if ((new Double(getValue().toString())).doubleValue() < min) {
- super.setValue(new Double(min));
- }
- } catch (final ClassCastException e) {
- // FIXME: Handle exception
- /*
- * Where does ClassCastException come from? Can't see any casts
- * above
- */
- super.setValue(new Double(min));
+ if (getValue() < min) {
+ setValue(min);
}
requestRepaint();
}
@@ -303,8 +246,8 @@ public class Slider extends AbstractField {
* If the given value is not inside the range of the slider.
* @see #setMin(double) {@link #setMax(double)}
*/
- public void setValue(Double value, boolean repaintIsNotNeeded)
- throws ValueOutOfBoundsException {
+ @Override
+ protected void setValue(Double value, boolean repaintIsNotNeeded) {
final double v = value.doubleValue();
double newValue;
if (resolution > 0) {
@@ -320,102 +263,22 @@ public class Slider extends AbstractField {
throw new ValueOutOfBoundsException(value);
}
}
- super.setValue(new Double(newValue), repaintIsNotNeeded);
- }
-
- /**
- * Sets the value of the slider.
- *
- * @param value
- * The new value of the slider.
- * @throws ValueOutOfBoundsException
- * If the given value is not inside the range of the slider.
- * @see #setMin(double) {@link #setMax(double)}
- */
- public void setValue(Double value) throws ValueOutOfBoundsException {
- setValue(value, false);
- }
-
- /**
- * Sets the value of the slider.
- *
- * @param value
- * The new value of the slider.
- * @throws ValueOutOfBoundsException
- * If the given value is not inside the range of the slider.
- * @see #setMin(double) {@link #setMax(double)}
- */
- public void setValue(double value) throws ValueOutOfBoundsException {
- setValue(new Double(value), false);
- }
-
- /**
- * Get the current slider size.
- *
- * @return size in pixels or -1 for auto sizing.
- * @deprecated use standard getWidth/getHeight instead
- */
- @Deprecated
- public int getSize() {
- return size;
+ super.setValue(newValue, repaintIsNotNeeded);
}
- /**
- * Set the size for this slider.
- *
- * @param size
- * in pixels, or -1 auto sizing.
- * @deprecated use standard setWidth/setHeight instead
- */
- @Deprecated
- public void setSize(int size) {
- this.size = size;
- switch (orientation) {
- case ORIENTATION_HORIZONTAL:
- setWidth(size, UNITS_PIXELS);
- break;
- default:
- setHeight(size, UNITS_PIXELS);
- break;
+ @Override
+ public void setValue(Object newFieldValue)
+ throws com.vaadin.data.Property.ReadOnlyException {
+ if (newFieldValue != null && newFieldValue instanceof Number
+ && !(newFieldValue instanceof Double)) {
+ // Support setting all types of Numbers
+ newFieldValue = ((Number) newFieldValue).doubleValue();
}
- requestRepaint();
- }
- /**
- * Get the handle size of this slider.
- *
- * @return handle size in percentages.
- * @deprecated The size is dictated by the current theme.
- */
- @Deprecated
- public int getHandleSize() {
- return handleSize;
+ super.setValue(newFieldValue);
}
- /**
- * Set the handle size of this slider.
- *
- * @param handleSize
- * in percentages relative to slider base size.
- * @deprecated The size is dictated by the current theme.
- */
- @Deprecated
- public void setHandleSize(int handleSize) {
- if (handleSize < 0) {
- this.handleSize = -1;
- } else if (handleSize > 99) {
- this.handleSize = 99;
- } else if (handleSize < 1) {
- this.handleSize = 1;
- } else {
- this.handleSize = handleSize;
- }
- requestRepaint();
- }
-
- @Override
public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
target.addAttribute("min", min);
if (max > min) {
@@ -426,30 +289,15 @@ public class Slider extends AbstractField {
target.addAttribute("resolution", resolution);
if (resolution > 0) {
- target.addVariable(this, "value",
- ((Double) getValue()).doubleValue());
+ target.addVariable(this, "value", getValue().doubleValue());
} else {
- target.addVariable(this, "value", ((Double) getValue()).intValue());
+ target.addVariable(this, "value", getValue().intValue());
}
if (orientation == ORIENTATION_VERTICAL) {
target.addAttribute("vertical", true);
}
- if (arrows) {
- target.addAttribute("arrows", true);
- }
-
- if (size > -1) {
- target.addAttribute("size", size);
- }
-
- if (min != max && min < max) {
- target.addAttribute("hsize", handleSize);
- } else {
- target.addAttribute("hsize", 100);
- }
-
}
/**
@@ -459,9 +307,7 @@ public class Slider extends AbstractField {
* @param source
* @param variables
*/
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
if (variables.containsKey("value")) {
final Object value = variables.get("value");
final Double newValue = new Double(value.toString());
@@ -491,7 +337,7 @@ public class Slider extends AbstractField {
* @author Vaadin Ltd.
*
*/
- public class ValueOutOfBoundsException extends Exception {
+ public class ValueOutOfBoundsException extends RuntimeException {
private final Double value;
@@ -517,7 +363,7 @@ public class Slider extends AbstractField {
}
@Override
- public Class getType() {
+ public Class<Double> getType() {
return Double.class;
}
diff --git a/src/com/vaadin/ui/SplitPanel.java b/src/com/vaadin/ui/SplitPanel.java
deleted file mode 100644
index bae1bf7ce0..0000000000
--- a/src/com/vaadin/ui/SplitPanel.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-
-package com.vaadin.ui;
-
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
-/**
- * SplitPanel.
- *
- * <code>SplitPanel</code> is a component container, that can contain two
- * components (possibly containers) which are split by divider element.
- *
- * @author Vaadin Ltd.
- * @version
- * @VERSION@
- * @since 5.0
- * @deprecated in 6.5. Use {@link HorizontalSplitPanel} or
- * {@link VerticalSplitPanel} instead.
- */
-@Deprecated
-@ClientWidget(value = VSplitPanelHorizontal.class, loadStyle = LoadStyle.EAGER)
-public class SplitPanel extends AbstractSplitPanel {
-
- /* Predefined orientations */
-
- /**
- * Components are to be laid out vertically.
- */
- public static final int ORIENTATION_VERTICAL = 0;
-
- /**
- * Components are to be laid out horizontally.
- */
- public static final int ORIENTATION_HORIZONTAL = 1;
-
- /**
- * Orientation of the layout.
- */
- private int orientation;
-
- /**
- * Creates a new split panel. The orientation of the panels is
- * <code>ORIENTATION_VERTICAL</code>.
- */
- public SplitPanel() {
- super();
- orientation = ORIENTATION_VERTICAL;
- setSizeFull();
- }
-
- /**
- * Create a new split panels. The orientation of the panel is given as
- * parameters.
- *
- * @param orientation
- * the Orientation of the layout.
- */
- public SplitPanel(int orientation) {
- this();
- setOrientation(orientation);
- }
-
- /**
- * Paints the content of this component.
- *
- * @param target
- * the Paint Event.
- * @throws PaintException
- * if the paint operation failed.
- */
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
-
- if (orientation == ORIENTATION_VERTICAL) {
- target.addAttribute("vertical", true);
- }
-
- }
-
- /**
- * Gets the orientation of the split panel.
- *
- * @return the Value of property orientation.
- *
- */
- public int getOrientation() {
- return orientation;
- }
-
- /**
- * Sets the orientation of the split panel.
- *
- * @param orientation
- * the New value of property orientation.
- */
- public void setOrientation(int orientation) {
-
- // Checks the validity of the argument
- if (orientation < ORIENTATION_VERTICAL
- || orientation > ORIENTATION_HORIZONTAL) {
- throw new IllegalArgumentException();
- }
-
- this.orientation = orientation;
- requestRepaint();
- }
-
-}
diff --git a/src/com/vaadin/ui/TabSheet.java b/src/com/vaadin/ui/TabSheet.java
index 09d1002b48..23dee15359 100644
--- a/src/com/vaadin/ui/TabSheet.java
+++ b/src/com/vaadin/ui/TabSheet.java
@@ -7,10 +7,8 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@@ -22,11 +20,13 @@ import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.KeyMapper;
+import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VTabsheet;
-import com.vaadin.terminal.gwt.server.CommunicationManager;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheet;
import com.vaadin.ui.Component.Focusable;
import com.vaadin.ui.themes.Reindeer;
import com.vaadin.ui.themes.Runo;
@@ -60,10 +60,8 @@ import com.vaadin.ui.themes.Runo;
* @VERSION@
* @since 3.0
*/
-@SuppressWarnings("serial")
-@ClientWidget(VTabsheet.class)
public class TabSheet extends AbstractComponentContainer implements Focusable,
- FocusNotifier, BlurNotifier {
+ FocusNotifier, BlurNotifier, Vaadin6Component {
/**
* List of component tabs (tab contents). In addition to being on this list,
@@ -86,7 +84,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* Mapper between server-side component instances (tab contents) and keys
* given to the client that identify tabs.
*/
- private final KeyMapper keyMapper = new KeyMapper();
+ private final KeyMapper<Component> keyMapper = new KeyMapper<Component>();
/**
* When true, the tab selection area is not displayed to the user.
@@ -94,11 +92,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
private boolean tabsHidden;
/**
- * Tabs that have been shown to the user (have been painted as selected).
- */
- private HashSet<Component> paintedTabs = new HashSet<Component>();
-
- /**
* Handler to be called when a tab is closed.
*/
private CloseHandler closeHandler;
@@ -159,7 +152,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
tabs.remove(c);
if (c.equals(selected)) {
if (components.isEmpty()) {
- selected = null;
+ setSelected(null);
} else {
// select the first enabled and visible tab, if any
updateSelection();
@@ -286,7 +279,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
tabs.put(c, tab);
if (selected == null) {
- selected = c;
+ setSelected(c);
fireSelectedTabChange();
}
super.addComponent(c);
@@ -366,7 +359,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
if (areTabsHidden()) {
@@ -379,18 +371,15 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
target.startTag("tabs");
- Collection<Component> orphaned = new HashSet<Component>(paintedTabs);
-
for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
final Component component = i.next();
- orphaned.remove(component);
-
Tab tab = tabs.get(component);
target.startTag("tab");
if (!tab.isEnabled() && tab.isVisible()) {
- target.addAttribute("disabled", true);
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_DISABLED, true);
}
if (!tab.isVisible()) {
@@ -401,23 +390,29 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
target.addAttribute("closable", true);
}
+ // tab icon, caption and description, but used via
+ // VCaption.updateCaption(uidl)
final Resource icon = tab.getIcon();
if (icon != null) {
- target.addAttribute("icon", icon);
+ target.addAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ICON,
+ icon);
}
final String caption = tab.getCaption();
if (caption != null && caption.length() > 0) {
- target.addAttribute("caption", caption);
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_CAPTION, caption);
+ }
+ ErrorMessage tabError = tab.getComponentError();
+ if (tabError != null) {
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE,
+ tabError.getFormattedHtmlMessage());
}
-
final String description = tab.getDescription();
if (description != null) {
- target.addAttribute("description", description);
- }
-
- final ErrorMessage componentError = tab.getComponentError();
- if (componentError != null) {
- componentError.paint(target);
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION,
+ description);
}
final String styleName = tab.getStyleName();
@@ -428,17 +423,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
target.addAttribute("key", keyMapper.key(component));
if (component.equals(selected)) {
target.addAttribute("selected", true);
- if (!paintedTabs.contains(component)) {
- // Ensure the component is painted if it hasn't already been
- // painted in this tabsheet
- component.requestRepaint();
- paintedTabs.add(component);
- }
- component.paint(target);
- } else if (paintedTabs.contains(component)) {
- component.paint(target);
- } else {
- component.requestRepaintRequests();
+ LegacyPaint.paint(component, target);
}
target.endTag("tab");
}
@@ -449,10 +434,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
target.addVariable(this, "selected", keyMapper.key(selected));
}
- // clean possibly orphaned entries in paintedTabs
- for (Component component2 : orphaned) {
- paintedTabs.remove(component2);
- }
}
/**
@@ -589,7 +570,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
*/
public void setSelectedTab(Component c) {
if (c != null && components.contains(c) && !c.equals(selected)) {
- selected = c;
+ setSelected(c);
updateSelection();
fireSelectedTabChange();
requestRepaint();
@@ -597,6 +578,31 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
}
/**
+ * Sets the selected tab in the TabSheet. Ensures that the selected tab is
+ * repainted if needed.
+ *
+ * @param c
+ * The new selection or null for no selection
+ */
+ private void setSelected(Component c) {
+ selected = c;
+ // Repaint of the selected component is needed as only the selected
+ // component is communicated to the client. Otherwise this will be a
+ // "cached" update even though the client knows nothing about the
+ // connector
+ if (selected instanceof ComponentContainer) {
+ ((ComponentContainer) selected).requestRepaintAll();
+ } else if (selected instanceof Table) {
+ // Workaround until there's a generic way of telling a component
+ // that there is no client side state to rely on. See #8642
+ ((Table) selected).refreshRowCache();
+ } else if (selected != null) {
+ selected.requestRepaint();
+ }
+
+ }
+
+ /**
* Sets the selected tab. The tab is identified by the corresponding
* {@link Tab Tab} instance. Does nothing if the tabsheet doesn't contain
* the given tab.
@@ -653,14 +659,14 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
// The current selection is not valid so we need to change
// it
if (tab.isEnabled() && tab.isVisible()) {
- selected = component;
+ setSelected(component);
break;
} else {
/*
* The current selection is not valid but this tab cannot be
* selected either.
*/
- selected = null;
+ setSelected(null);
}
}
}
@@ -677,15 +683,13 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
}
// inherits javadoc
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
if (variables.containsKey("selected")) {
- setSelectedTab((Component) keyMapper.get((String) variables
- .get("selected")));
+ setSelectedTab(keyMapper.get((String) variables.get("selected")));
}
if (variables.containsKey("close")) {
- final Component tab = (Component) keyMapper.get((String) variables
- .get("close"));
+ final Component tab = keyMapper
+ .get((String) variables.get("close"));
if (tab != null) {
closeHandler.onTabClose(this, tab);
}
@@ -719,7 +723,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
if (selected == oldComponent) {
// keep selection w/o selectedTabChange event
- selected = newComponent;
+ setSelected(newComponent);
}
Tab newTab = tabs.get(newComponent);
@@ -897,16 +901,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
fireEvent(new SelectedTabChangeEvent(this));
}
- @Override
- public void removeListener(RepaintRequestListener listener) {
- super.removeListener(listener);
- if (listener instanceof CommunicationManager) {
- // clean the paintedTabs here instead of detach to avoid subtree
- // caching issues when detached-attached without render
- paintedTabs.clear();
- }
- }
-
/**
* Tab meta-data for a component in a {@link TabSheet}.
*
@@ -1033,12 +1027,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public void setComponentError(ErrorMessage componentError);
/**
- * Gets the curent error message shown for the tab.
+ * Gets the current error message shown for the tab.
*
- * @see AbstractComponent#setComponentError(ErrorMessage)
+ * TODO currently not sent to the client
*
- * @param error
- * message or null if none
+ * @see AbstractComponent#setComponentError(ErrorMessage)
*/
public ErrorMessage getComponentError();
@@ -1068,9 +1061,8 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* </pre>
*
* <p>
- * This method will trigger a
- * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
- * RepaintRequestEvent} on the TabSheet to which the Tab belongs.
+ * This method will trigger a {@link RepaintRequestEvent} on the
+ * TabSheet to which the Tab belongs.
* </p>
*
* @param styleName
@@ -1303,4 +1295,9 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
}
+
+ @Override
+ public boolean isComponentVisible(Component childComponent) {
+ return childComponent == getSelectedTab();
+ }
}
diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java
index e605ec4f6b..2fffedd9d6 100644
--- a/src/com/vaadin/ui/Table.java
+++ b/src/com/vaadin/ui/Table.java
@@ -8,6 +8,7 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -20,18 +21,19 @@ import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
+import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.ContainerOrderedWrapper;
import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.data.util.converter.Converter;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.DataBoundTransferable;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
-import com.vaadin.event.ItemClickEvent.ItemClickSource;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DragSource;
@@ -40,12 +42,13 @@ import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
import com.vaadin.terminal.KeyMapper;
+import com.vaadin.terminal.LegacyPaint;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VScrollTable;
import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
+import com.vaadin.terminal.gwt.client.ui.table.VScrollTable;
/**
* <p>
@@ -70,11 +73,10 @@ import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
* @VERSION@
* @since 3.0
*/
-@SuppressWarnings({ "serial", "deprecation" })
-@ClientWidget(VScrollTable.class)
+@SuppressWarnings({ "deprecation" })
public class Table extends AbstractSelect implements Action.Container,
- Container.Ordered, Container.Sortable, ItemClickSource,
- ItemClickNotifier, DragSource, DropTarget {
+ Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource,
+ DropTarget, HasComponents {
private static final Logger logger = Logger
.getLogger(Table.class.getName());
@@ -113,91 +115,215 @@ public class Table extends AbstractSelect implements Action.Container,
protected static final int CELL_FIRSTCOL = 5;
+ public enum Align {
+ /**
+ * Left column alignment. <b>This is the default behaviour. </b>
+ */
+ LEFT("b"),
+
+ /**
+ * Center column alignment.
+ */
+ CENTER("c"),
+
+ /**
+ * Right column alignment.
+ */
+ RIGHT("e");
+
+ private String alignment;
+
+ private Align(String alignment) {
+ this.alignment = alignment;
+ }
+
+ @Override
+ public String toString() {
+ return alignment;
+ }
+
+ public Align convertStringToAlign(String string) {
+ if (string == null) {
+ return null;
+ }
+ if (string.equals("b")) {
+ return Align.LEFT;
+ } else if (string.equals("c")) {
+ return Align.CENTER;
+ } else if (string.equals("e")) {
+ return Align.RIGHT;
+ } else {
+ return null;
+ }
+ }
+ }
+
/**
- * Left column alignment. <b>This is the default behaviour. </b>
+ * @deprecated from 7.0, use {@link Align#LEFT} instead
*/
- public static final String ALIGN_LEFT = "b";
+ @Deprecated
+ public static final Align ALIGN_LEFT = Align.LEFT;
/**
- * Center column alignment.
+ * @deprecated from 7.0, use {@link Align#CENTER} instead
*/
- public static final String ALIGN_CENTER = "c";
+ @Deprecated
+ public static final Align ALIGN_CENTER = Align.CENTER;
/**
- * Right column alignment.
+ * @deprecated from 7.0, use {@link Align#RIGHT} instead
*/
- public static final String ALIGN_RIGHT = "e";
+ @Deprecated
+ public static final Align ALIGN_RIGHT = Align.RIGHT;
+
+ public enum ColumnHeaderMode {
+ /**
+ * Column headers are hidden.
+ */
+ HIDDEN,
+ /**
+ * Property ID:s are used as column headers.
+ */
+ ID,
+ /**
+ * Column headers are explicitly specified with
+ * {@link #setColumnHeaders(String[])}.
+ */
+ EXPLICIT,
+ /**
+ * Column headers are explicitly specified with
+ * {@link #setColumnHeaders(String[])}. If a header is not specified for
+ * a given property, its property id is used instead.
+ * <p>
+ * <b>This is the default behavior. </b>
+ */
+ EXPLICIT_DEFAULTS_ID
+ }
/**
- * Column header mode: Column headers are hidden.
+ * @deprecated from 7.0, use {@link ColumnHeaderMode#HIDDEN} instead
*/
- public static final int COLUMN_HEADER_MODE_HIDDEN = -1;
+ @Deprecated
+ public static final ColumnHeaderMode COLUMN_HEADER_MODE_HIDDEN = ColumnHeaderMode.HIDDEN;
/**
- * Column header mode: Property ID:s are used as column headers.
+ * @deprecated from 7.0, use {@link ColumnHeaderMode#ID} instead
*/
- public static final int COLUMN_HEADER_MODE_ID = 0;
+ @Deprecated
+ public static final ColumnHeaderMode COLUMN_HEADER_MODE_ID = ColumnHeaderMode.ID;
/**
- * Column header mode: Column headers are explicitly specified with
- * {@link #setColumnHeaders(String[])}.
+ * @deprecated from 7.0, use {@link ColumnHeaderMode#EXPLICIT} instead
*/
- public static final int COLUMN_HEADER_MODE_EXPLICIT = 1;
+ @Deprecated
+ public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT = ColumnHeaderMode.EXPLICIT;
/**
- * Column header mode: Column headers are explicitly specified with
- * {@link #setColumnHeaders(String[])}. If a header is not specified for a
- * given property, its property id is used instead.
- * <p>
- * <b>This is the default behavior. </b>
+ * @deprecated from 7.0, use {@link ColumnHeaderMode#EXPLICIT_DEFAULTS_ID}
+ * instead
*/
- public static final int COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = 2;
+ @Deprecated
+ public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
+
+ public enum RowHeaderMode {
+ /**
+ * Row caption mode: The row headers are hidden. <b>This is the default
+ * mode. </b>
+ */
+ HIDDEN(null),
+ /**
+ * Row caption mode: Items Id-objects toString is used as row caption.
+ */
+ ID(ItemCaptionMode.ID),
+ /**
+ * Row caption mode: Item-objects toString is used as row caption.
+ */
+ ITEM(ItemCaptionMode.ITEM),
+ /**
+ * Row caption mode: Index of the item is used as item caption. The
+ * index mode can only be used with the containers implementing the
+ * {@link com.vaadin.data.Container.Indexed} interface.
+ */
+ INDEX(ItemCaptionMode.INDEX),
+ /**
+ * Row caption mode: Item captions are explicitly specified, but if the
+ * caption is missing, the item id objects <code>toString()</code> is
+ * used instead.
+ */
+ EXPLICIT_DEFAULTS_ID(ItemCaptionMode.EXPLICIT_DEFAULTS_ID),
+ /**
+ * Row caption mode: Item captions are explicitly specified.
+ */
+ EXPLICIT(ItemCaptionMode.EXPLICIT),
+ /**
+ * Row caption mode: Only icons are shown, the captions are hidden.
+ */
+ ICON_ONLY(ItemCaptionMode.ICON_ONLY),
+ /**
+ * Row caption mode: Item captions are read from property specified with
+ * {@link #setItemCaptionPropertyId(Object)}.
+ */
+ PROPERTY(ItemCaptionMode.PROPERTY);
+
+ ItemCaptionMode mode;
+
+ private RowHeaderMode(ItemCaptionMode mode) {
+ this.mode = mode;
+ }
+
+ public ItemCaptionMode getItemCaptionMode() {
+ return mode;
+ }
+ }
/**
- * Row caption mode: The row headers are hidden. <b>This is the default
- * mode. </b>
+ * @deprecated from 7.0, use {@link RowHeaderMode#HIDDEN} instead
*/
- public static final int ROW_HEADER_MODE_HIDDEN = -1;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_HIDDEN = RowHeaderMode.HIDDEN;
/**
- * Row caption mode: Items Id-objects toString is used as row caption.
+ * @deprecated from 7.0, use {@link RowHeaderMode#ID} instead
*/
- public static final int ROW_HEADER_MODE_ID = AbstractSelect.ITEM_CAPTION_MODE_ID;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_ID = RowHeaderMode.ID;
/**
- * Row caption mode: Item-objects toString is used as row caption.
+ * @deprecated from 7.0, use {@link RowHeaderMode#ITEM} instead
*/
- public static final int ROW_HEADER_MODE_ITEM = AbstractSelect.ITEM_CAPTION_MODE_ITEM;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_ITEM = RowHeaderMode.ITEM;
/**
- * Row caption mode: Index of the item is used as item caption. The index
- * mode can only be used with the containers implementing Container.Indexed
- * interface.
+ * @deprecated from 7.0, use {@link RowHeaderMode#INDEX} instead
*/
- public static final int ROW_HEADER_MODE_INDEX = AbstractSelect.ITEM_CAPTION_MODE_INDEX;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_INDEX = RowHeaderMode.INDEX;
/**
- * Row caption mode: Item captions are explicitly specified.
+ * @deprecated from 7.0, use {@link RowHeaderMode#EXPLICIT_DEFAULTS_ID}
+ * instead
*/
- public static final int ROW_HEADER_MODE_EXPLICIT = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
/**
- * Row caption mode: Item captions are read from property specified with
- * {@link #setItemCaptionPropertyId(Object)}.
+ * @deprecated from 7.0, use {@link RowHeaderMode#EXPLICIT} instead
*/
- public static final int ROW_HEADER_MODE_PROPERTY = AbstractSelect.ITEM_CAPTION_MODE_PROPERTY;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT = RowHeaderMode.EXPLICIT;
/**
- * Row caption mode: Only icons are shown, the captions are hidden.
+ * @deprecated from 7.0, use {@link RowHeaderMode#ICON_ONLY} instead
*/
- public static final int ROW_HEADER_MODE_ICON_ONLY = AbstractSelect.ITEM_CAPTION_MODE_ICON_ONLY;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_ICON_ONLY = RowHeaderMode.ICON_ONLY;
/**
- * Row caption mode: Item captions are explicitly specified, but if the
- * caption is missing, the item id objects <code>toString()</code> is used
- * instead.
+ * @deprecated from 7.0, use {@link RowHeaderMode#PROPERTY} instead
*/
- public static final int ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
+ @Deprecated
+ public static final RowHeaderMode ROW_HEADER_MODE_PROPERTY = RowHeaderMode.PROPERTY;
/**
* The default rate that table caches rows for smooth scrolling.
@@ -222,7 +348,7 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Keymapper for column ids.
*/
- private final KeyMapper columnIdMap = new KeyMapper();
+ private final KeyMapper<Object> columnIdMap = new KeyMapper<Object>();
/**
* Holds visible column propertyIds - in order.
@@ -252,7 +378,7 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Holds alignments for visible columns (by propertyId).
*/
- private HashMap<Object, String> columnAlignments = new HashMap<Object, String>();
+ private HashMap<Object, Align> columnAlignments = new HashMap<Object, Align>();
/**
* Holds column widths in pixels (Integer) or expand ratios (Float) for
@@ -288,17 +414,17 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Holds value of property columnHeaderMode.
*/
- private int columnHeaderMode = COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID;
+ private ColumnHeaderMode columnHeaderMode = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
/**
- * Should the Table footer be visible?
+ * Holds value of property rowHeaderMode.
*/
- private boolean columnFootersVisible = false;
+ private RowHeaderMode rowHeaderMode = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
/**
- * True iff the row captions are hidden.
+ * Should the Table footer be visible?
*/
- private boolean rowCaptionsAreHidden = true;
+ private boolean columnFootersVisible = false;
/**
* Page contents buffer used in buffered mode.
@@ -309,7 +435,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Set of properties listened - the list is kept to release the listeners
* later.
*/
- private HashSet<Property> listenedProperties = null;
+ private HashSet<Property<?>> listenedProperties = null;
/**
* Set of visible components - the is used for needsRepaint calculation.
@@ -324,7 +450,7 @@ public class Table extends AbstractSelect implements Action.Container,
/**
* Action mapper.
*/
- private KeyMapper actionMapper = null;
+ private KeyMapper<Action> actionMapper = null;
/**
* Table cell editor factory.
@@ -404,10 +530,12 @@ public class Table extends AbstractSelect implements Action.Container,
private RowGenerator rowGenerator = null;
- private final Map<Field, Property> associatedProperties = new HashMap<Field, Property>();
+ private final Map<Field<?>, Property<?>> associatedProperties = new HashMap<Field<?>, Property<?>>();
private boolean painted = false;
+ private HashMap<Object, Converter<String, Object>> propertyValueConverters = new HashMap<Object, Converter<String, Object>>();
+
/* Table constructors */
/**
@@ -508,7 +636,7 @@ public class Table extends AbstractSelect implements Action.Container,
final Object col = i.next();
if (!newVC.contains(col)) {
setColumnHeader(col, null);
- setColumnAlignment(col, null);
+ setColumnAlignment(col, (Align) null);
setColumnIcon(col, null);
}
}
@@ -652,21 +780,21 @@ public class Table extends AbstractSelect implements Action.Container,
* {@link #getVisibleColumns()}. The possible values for the alignments
* include:
* <ul>
- * <li>{@link #ALIGN_LEFT}: Left alignment</li>
- * <li>{@link #ALIGN_CENTER}: Centered</li>
- * <li>{@link #ALIGN_RIGHT}: Right alignment</li>
+ * <li>{@link Align#LEFT}: Left alignment</li>
+ * <li>{@link Align#CENTER}: Centered</li>
+ * <li>{@link Align#RIGHT}: Right alignment</li>
* </ul>
- * The alignments default to {@link #ALIGN_LEFT}: any null values are
+ * The alignments default to {@link Align#LEFT}: any null values are
* rendered as align lefts.
* </p>
*
* @return the Column alignments array.
*/
- public String[] getColumnAlignments() {
+ public Align[] getColumnAlignments() {
if (columnAlignments == null) {
return null;
}
- final String[] alignments = new String[visibleColumns.size()];
+ final Align[] alignments = new Align[visibleColumns.size()];
int i = 0;
for (final Iterator<Object> it = visibleColumns.iterator(); it
.hasNext(); i++) {
@@ -680,39 +808,29 @@ public class Table extends AbstractSelect implements Action.Container,
* Sets the column alignments.
*
* <p>
- * The items in the array must match the properties identified by
- * {@link #getVisibleColumns()}. The possible values for the alignments
- * include:
+ * The amount of items in the array must match the amount of properties
+ * identified by {@link #getVisibleColumns()}. The possible values for the
+ * alignments include:
* <ul>
- * <li>{@link #ALIGN_LEFT}: Left alignment</li>
- * <li>{@link #ALIGN_CENTER}: Centered</li>
- * <li>{@link #ALIGN_RIGHT}: Right alignment</li>
+ * <li>{@link Align#LEFT}: Left alignment</li>
+ * <li>{@link Align#CENTER}: Centered</li>
+ * <li>{@link Align#RIGHT}: Right alignment</li>
* </ul>
- * The alignments default to {@link #ALIGN_LEFT}
+ * The alignments default to {@link Align#LEFT}
* </p>
*
* @param columnAlignments
* the Column alignments array.
*/
- public void setColumnAlignments(String[] columnAlignments) {
+ public void setColumnAlignments(Align... columnAlignments) {
if (columnAlignments.length != visibleColumns.size()) {
throw new IllegalArgumentException(
"The length of the alignments array must match the number of visible columns");
}
- // Checks all alignments
- for (int i = 0; i < columnAlignments.length; i++) {
- final String a = columnAlignments[i];
- if (a != null && !a.equals(ALIGN_LEFT) && !a.equals(ALIGN_CENTER)
- && !a.equals(ALIGN_RIGHT)) {
- throw new IllegalArgumentException("Column " + i
- + " aligment '" + a + "' is invalid");
- }
- }
-
// Resets the alignments
- final HashMap<Object, String> newCA = new HashMap<Object, String>();
+ final HashMap<Object, Align> newCA = new HashMap<Object, Align>();
int i = 0;
for (final Iterator<Object> it = visibleColumns.iterator(); it
.hasNext() && i < columnAlignments.length; i++) {
@@ -1032,13 +1150,13 @@ public class Table extends AbstractSelect implements Action.Container,
* @return the header for the specified column if it has one.
*/
public String getColumnHeader(Object propertyId) {
- if (getColumnHeaderMode() == COLUMN_HEADER_MODE_HIDDEN) {
+ if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) {
return null;
}
String header = columnHeaders.get(propertyId);
- if ((header == null && getColumnHeaderMode() == COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID)
- || getColumnHeaderMode() == COLUMN_HEADER_MODE_ID) {
+ if ((header == null && getColumnHeaderMode() == ColumnHeaderMode.EXPLICIT_DEFAULTS_ID)
+ || getColumnHeaderMode() == ColumnHeaderMode.ID) {
header = propertyId.toString();
}
@@ -1071,9 +1189,9 @@ public class Table extends AbstractSelect implements Action.Container,
* the propertyID identifying the column.
* @return the specified column's alignment if it as one; null otherwise.
*/
- public String getColumnAlignment(Object propertyId) {
- final String a = columnAlignments.get(propertyId);
- return a == null ? ALIGN_LEFT : a;
+ public Align getColumnAlignment(Object propertyId) {
+ final Align a = columnAlignments.get(propertyId);
+ return a == null ? Align.LEFT : a;
}
/**
@@ -1081,8 +1199,8 @@ public class Table extends AbstractSelect implements Action.Container,
*
* <p>
* Throws IllegalArgumentException if the alignment is not one of the
- * following: {@link #ALIGN_LEFT}, {@link #ALIGN_CENTER} or
- * {@link #ALIGN_RIGHT}
+ * following: {@link Align#LEFT}, {@link Align#CENTER} or
+ * {@link Align#RIGHT}
* </p>
*
* @param propertyId
@@ -1090,17 +1208,8 @@ public class Table extends AbstractSelect implements Action.Container,
* @param alignment
* the desired alignment.
*/
- public void setColumnAlignment(Object propertyId, String alignment) {
-
- // Checks for valid alignments
- if (alignment != null && !alignment.equals(ALIGN_LEFT)
- && !alignment.equals(ALIGN_CENTER)
- && !alignment.equals(ALIGN_RIGHT)) {
- throw new IllegalArgumentException("Column alignment '" + alignment
- + "' is not supported.");
- }
-
- if (alignment == null || alignment.equals(ALIGN_LEFT)) {
+ public void setColumnAlignment(Object propertyId, Align alignment) {
+ if (alignment == null || alignment == Align.LEFT) {
columnAlignments.remove(propertyId);
} else {
columnAlignments.put(propertyId, alignment);
@@ -1400,7 +1509,7 @@ public class Table extends AbstractSelect implements Action.Container,
*
* @return the Value of property columnHeaderMode.
*/
- public int getColumnHeaderMode() {
+ public ColumnHeaderMode getColumnHeaderMode() {
return columnHeaderMode;
}
@@ -1410,10 +1519,12 @@ public class Table extends AbstractSelect implements Action.Container,
* @param columnHeaderMode
* the New value of property columnHeaderMode.
*/
- public void setColumnHeaderMode(int columnHeaderMode) {
- if (columnHeaderMode != this.columnHeaderMode
- && columnHeaderMode >= COLUMN_HEADER_MODE_HIDDEN
- && columnHeaderMode <= COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID) {
+ public void setColumnHeaderMode(ColumnHeaderMode columnHeaderMode) {
+ if (columnHeaderMode == null) {
+ throw new IllegalArgumentException(
+ "Column header mode can not be null");
+ }
+ if (columnHeaderMode != this.columnHeaderMode) {
this.columnHeaderMode = columnHeaderMode;
requestRepaint();
}
@@ -1725,13 +1836,13 @@ public class Table extends AbstractSelect implements Action.Container,
final Object[] colids = getVisibleColumns();
final int cols = colids.length;
- HashSet<Property> oldListenedProperties = listenedProperties;
+ HashSet<Property<?>> oldListenedProperties = listenedProperties;
HashSet<Component> oldVisibleComponents = visibleComponents;
if (replaceListeners) {
// initialize the listener collections, this should only be done if
// the entire cache is refreshed (through refreshRenderedCells)
- listenedProperties = new HashSet<Property>();
+ listenedProperties = new HashSet<Property<?>>();
visibleComponents = new HashSet<Component>();
}
@@ -1753,7 +1864,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
- final int headmode = getRowHeaderMode();
+ final RowHeaderMode headmode = getRowHeaderMode();
final boolean[] iscomponent = new boolean[cols];
for (int i = 0; i < cols; i++) {
iscomponent[i] = columnGenerators.containsKey(colids[i])
@@ -1774,7 +1885,7 @@ public class Table extends AbstractSelect implements Action.Container,
cells[CELL_KEY][i] = itemIdMapper.key(id);
if (headmode != ROW_HEADER_MODE_HIDDEN) {
switch (headmode) {
- case ROW_HEADER_MODE_INDEX:
+ case INDEX:
cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1);
break;
default:
@@ -1791,7 +1902,7 @@ public class Table extends AbstractSelect implements Action.Container,
if (isColumnCollapsed(colids[j])) {
continue;
}
- Property p = null;
+ Property<?> p = null;
Object value = "";
boolean isGeneratedRow = generatedRow != null;
boolean isGeneratedColumn = columnGenerators
@@ -1911,8 +2022,8 @@ public class Table extends AbstractSelect implements Action.Container,
visibleComponents.add(component);
}
- private void listenProperty(Property p,
- HashSet<Property> oldListenedProperties) {
+ private void listenProperty(Property<?> p,
+ HashSet<Property<?>> oldListenedProperties) {
if (p instanceof Property.ValueChangeNotifier) {
if (oldListenedProperties == null
|| !oldListenedProperties.contains(p)) {
@@ -1952,7 +2063,7 @@ public class Table extends AbstractSelect implements Action.Container,
visibleComponents.remove(cellVal);
unregisterComponent((Component) cellVal);
} else {
- Property p = getContainerProperty(
+ Property<?> p = getContainerProperty(
pageBuffer[CELL_ITEMID][i + ix], colids[c]);
if (p instanceof ValueChangeNotifier
&& listenedProperties.contains(p)) {
@@ -1977,7 +2088,7 @@ public class Table extends AbstractSelect implements Action.Container,
* set of components that where attached in last render
*/
private void unregisterPropertiesAndComponents(
- HashSet<Property> oldListenedProperties,
+ HashSet<Property<?>> oldListenedProperties,
HashSet<Component> oldVisibleComponents) {
if (oldVisibleComponents != null) {
for (final Iterator<Component> i = oldVisibleComponents.iterator(); i
@@ -1990,8 +2101,8 @@ public class Table extends AbstractSelect implements Action.Container,
}
if (oldListenedProperties != null) {
- for (final Iterator<Property> i = oldListenedProperties.iterator(); i
- .hasNext();) {
+ for (final Iterator<Property<?>> i = oldListenedProperties
+ .iterator(); i.hasNext();) {
Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next();
if (!listenedProperties.contains(o)) {
o.removeListener(this);
@@ -2024,8 +2135,8 @@ public class Table extends AbstractSelect implements Action.Container,
* fields in memory.
*/
if (component instanceof Field) {
- Field field = (Field) component;
- Property associatedProperty = associatedProperties
+ Field<?> field = (Field<?>) component;
+ Property<?> associatedProperty = associatedProperties
.remove(component);
if (associatedProperty != null
&& field.getPropertyDataSource() == associatedProperty) {
@@ -2073,17 +2184,17 @@ public class Table extends AbstractSelect implements Action.Container,
* @param mode
* the One of the modes listed above.
*/
- public void setRowHeaderMode(int mode) {
- if (ROW_HEADER_MODE_HIDDEN == mode) {
- rowCaptionsAreHidden = true;
- } else {
- rowCaptionsAreHidden = false;
- setItemCaptionMode(mode);
+ public void setRowHeaderMode(RowHeaderMode mode) {
+ if (mode != null) {
+ rowHeaderMode = mode;
+ if (mode != RowHeaderMode.HIDDEN) {
+ setItemCaptionMode(mode.getItemCaptionMode());
+ }
+ // Assures the visual refresh. No need to reset the page buffer
+ // before
+ // as the content has not changed, only the alignments.
+ refreshRenderedCells();
}
-
- // Assures the visual refresh. No need to reset the page buffer before
- // as the content has not changed, only the alignments.
- refreshRenderedCells();
}
/**
@@ -2092,9 +2203,8 @@ public class Table extends AbstractSelect implements Action.Container,
* @return the Row header mode.
* @see #setRowHeaderMode(int)
*/
- public int getRowHeaderMode() {
- return rowCaptionsAreHidden ? ROW_HEADER_MODE_HIDDEN
- : getItemCaptionMode();
+ public RowHeaderMode getRowHeaderMode() {
+ return rowHeaderMode;
}
/**
@@ -2491,7 +2601,7 @@ public class Table extends AbstractSelect implements Action.Container,
(String) variables.get("action"), ",");
if (st.countTokens() == 2) {
final Object itemId = itemIdMapper.get(st.nextToken());
- final Action action = (Action) actionMapper.get(st.nextToken());
+ final Action action = actionMapper.get(st.nextToken());
if (action != null && (itemId == null || containsId(itemId))
&& actionHandlers != null) {
@@ -2931,7 +3041,7 @@ public class Table extends AbstractSelect implements Action.Container,
}
private boolean areColumnHeadersEnabled() {
- return getColumnHeaderMode() != COLUMN_HEADER_MODE_HIDDEN;
+ return getColumnHeaderMode() != ColumnHeaderMode.HIDDEN;
}
private void paintVisibleColumns(PaintTarget target) throws PaintException {
@@ -2962,8 +3072,9 @@ public class Table extends AbstractSelect implements Action.Container,
target.addAttribute("sortable", true);
}
}
- if (!ALIGN_LEFT.equals(getColumnAlignment(colId))) {
- target.addAttribute("align", getColumnAlignment(colId));
+ if (!Align.LEFT.equals(getColumnAlignment(colId))) {
+ target.addAttribute("align", getColumnAlignment(colId)
+ .toString());
}
paintColumnWidth(target, colId);
target.endTag("column");
@@ -3253,7 +3364,7 @@ public class Table extends AbstractSelect implements Action.Container,
target.addText("");
paintCellTooltips(target, itemId, columnId);
} else {
- c.paint(target);
+ LegacyPaint.paint(c, target);
}
} else {
target.addText((String) cells[CELL_FIRSTCOL + currentColumn][indexInRowbuffer]);
@@ -3409,8 +3520,8 @@ public class Table extends AbstractSelect implements Action.Container,
protected Object getPropertyValue(Object rowId, Object colId,
Property property) {
if (isEditable() && fieldFactory != null) {
- final Field f = fieldFactory.createField(getContainerDataSource(),
- rowId, colId, this);
+ final Field<?> f = fieldFactory.createField(
+ getContainerDataSource(), rowId, colId, this);
if (f != null) {
// Remember that we have made this association so we can remove
// it when the component is removed
@@ -3464,11 +3575,27 @@ public class Table extends AbstractSelect implements Action.Container,
* @since 3.1
*/
protected String formatPropertyValue(Object rowId, Object colId,
- Property property) {
+ Property<?> property) {
if (property == null) {
return "";
}
- return property.toString();
+ Converter<String, Object> converter = null;
+
+ if (hasConverter(colId)) {
+ converter = getConverter(colId);
+ } else {
+ Application app = Application.getCurrentApplication();
+ if (app != null) {
+ converter = (Converter<String, Object>) app
+ .getConverterFactory().createConverter(String.class,
+ property.getType());
+ }
+ }
+ Object value = property.getValue();
+ if (converter != null) {
+ return converter.convertToPresentation(value, getLocale());
+ }
+ return (null != value) ? value.toString() : "";
}
/* Action container */
@@ -3484,7 +3611,7 @@ public class Table extends AbstractSelect implements Action.Container,
if (actionHandlers == null) {
actionHandlers = new LinkedList<Handler>();
- actionMapper = new KeyMapper();
+ actionMapper = new KeyMapper<Action>();
}
if (!actionHandlers.contains(actionHandler)) {
@@ -3711,7 +3838,7 @@ public class Table extends AbstractSelect implements Action.Container,
*/
public boolean addContainerProperty(Object propertyId, Class<?> type,
Object defaultValue, String columnHeader, Resource columnIcon,
- String columnAlignment) throws UnsupportedOperationException {
+ Align columnAlignment) throws UnsupportedOperationException {
if (!this.addContainerProperty(propertyId, type, defaultValue)) {
return false;
}
@@ -4029,44 +4156,6 @@ public class Table extends AbstractSelect implements Action.Container,
}
/**
- * Gets the FieldFactory that is used to create editor for table cells.
- *
- * The FieldFactory is only used if the Table is editable.
- *
- * @return FieldFactory used to create the Field instances.
- * @see #isEditable
- * @deprecated use {@link #getTableFieldFactory()} instead
- */
- @Deprecated
- public FieldFactory getFieldFactory() {
- if (fieldFactory instanceof FieldFactory) {
- return (FieldFactory) fieldFactory;
-
- }
- return null;
- }
-
- /**
- * Sets the FieldFactory that is used to create editor for table cells.
- *
- * The FieldFactory is only used if the Table is editable. By default the
- * BaseFieldFactory is used.
- *
- * @param fieldFactory
- * the field factory to set.
- * @see #isEditable
- * @see BaseFieldFactory
- * @deprecated use {@link #setTableFieldFactory(TableFieldFactory)} instead
- */
- @Deprecated
- public void setFieldFactory(FieldFactory fieldFactory) {
- this.fieldFactory = fieldFactory;
-
- // Assure visual refresh
- refreshRowCache();
- }
-
- /**
* Is table editable.
*
* If table is editable a editor of type Field is created for each table
@@ -4377,27 +4466,8 @@ public class Table extends AbstractSelect implements Action.Container,
}
}
- // Virtually identical to AbstractCompoenentContainer.setEnabled();
public void requestRepaintAll() {
- requestRepaint();
- if (visibleComponents != null) {
- for (Iterator<Component> childIterator = visibleComponents
- .iterator(); childIterator.hasNext();) {
- Component c = childIterator.next();
- if (c instanceof Form) {
- // Form has children in layout, but is not
- // ComponentContainer
- c.requestRepaint();
- ((Form) c).getLayout().requestRepaintAll();
- } else if (c instanceof Table) {
- ((Table) c).requestRepaintAll();
- } else if (c instanceof ComponentContainer) {
- ((ComponentContainer) c).requestRepaintAll();
- } else {
- c.requestRepaint();
- }
- }
- }
+ AbstractComponentContainer.requestRepaintAll(this);
}
/**
@@ -5147,13 +5217,86 @@ public class Table extends AbstractSelect implements Action.Container,
return rowGenerator;
}
+ /**
+ * Sets a converter for a property id.
+ * <p>
+ * The converter is used to format the the data for the given property id
+ * before displaying it in the table.
+ * </p>
+ *
+ * @param propertyId
+ * The propertyId to format using the converter
+ * @param converter
+ * The converter to use for the property id
+ */
+ public void setConverter(Object propertyId, Converter<String, ?> converter) {
+ if (!getContainerPropertyIds().contains(propertyId)) {
+ throw new IllegalArgumentException("PropertyId " + propertyId
+ + " must be in the container");
+ }
+ // FIXME: This check should be here but primitive types like Boolean
+ // formatter for boolean property must be handled
+
+ // if (!converter.getSourceType().isAssignableFrom(getType(propertyId)))
+ // {
+ // throw new IllegalArgumentException("Property type ("
+ // + getType(propertyId)
+ // + ") must match converter source type ("
+ // + converter.getSourceType() + ")");
+ // }
+ propertyValueConverters.put(propertyId,
+ (Converter<String, Object>) converter);
+ refreshRowCache();
+ }
+
+ /**
+ * Checks if there is a converter set explicitly for the given property id.
+ *
+ * @param propertyId
+ * The propertyId to check
+ * @return true if a converter has been set for the property id, false
+ * otherwise
+ */
+ protected boolean hasConverter(Object propertyId) {
+ return propertyValueConverters.containsKey(propertyId);
+ }
+
+ /**
+ * Returns the converter used to format the given propertyId.
+ *
+ * @param propertyId
+ * The propertyId to check
+ * @return The converter used to format the propertyId or null if no
+ * converter has been set
+ */
+ public Converter<String, Object> getConverter(Object propertyId) {
+ return propertyValueConverters.get(propertyId);
+ }
+
@Override
public void setVisible(boolean visible) {
- if (!isVisible() && visible) {
+ if (visible) {
// We need to ensure that the rows are sent to the client when the
// Table is made visible if it has been rendered as invisible.
setRowCacheInvalidated(true);
}
super.setVisible(visible);
}
+
+ public Iterator<Component> iterator() {
+ return getComponentIterator();
+ }
+
+ public Iterator<Component> getComponentIterator() {
+ if (visibleComponents == null) {
+ Collection<Component> empty = Collections.emptyList();
+ return empty.iterator();
+ }
+
+ return visibleComponents.iterator();
+ }
+
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ }
}
diff --git a/src/com/vaadin/ui/TableFieldFactory.java b/src/com/vaadin/ui/TableFieldFactory.java
index 9b0495d589..6c9a641aa8 100644
--- a/src/com/vaadin/ui/TableFieldFactory.java
+++ b/src/com/vaadin/ui/TableFieldFactory.java
@@ -39,7 +39,7 @@ public interface TableFieldFactory extends Serializable {
* @return A field suitable for editing the specified data or null if the
* property should not be editable.
*/
- Field createField(Container container, Object itemId, Object propertyId,
+ Field<?> createField(Container container, Object itemId, Object propertyId,
Component uiContext);
}
diff --git a/src/com/vaadin/ui/TextArea.java b/src/com/vaadin/ui/TextArea.java
index e1e5aeabd4..adb980818e 100644
--- a/src/com/vaadin/ui/TextArea.java
+++ b/src/com/vaadin/ui/TextArea.java
@@ -7,12 +7,10 @@ package com.vaadin.ui;
import com.vaadin.data.Property;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VTextArea;
/**
* A text field that supports multi line editing.
*/
-@ClientWidget(VTextArea.class)
public class TextArea extends AbstractTextField {
private static final int DEFAULT_ROWS = 5;
diff --git a/src/com/vaadin/ui/TextField.java b/src/com/vaadin/ui/TextField.java
index 0d719efd12..567e9c1c10 100644
--- a/src/com/vaadin/ui/TextField.java
+++ b/src/com/vaadin/ui/TextField.java
@@ -5,10 +5,6 @@
package com.vaadin.ui;
import com.vaadin.data.Property;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VTextField;
-import com.vaadin.ui.ClientWidget.LoadStyle;
/**
* <p>
@@ -31,30 +27,9 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VTextField.class, loadStyle = LoadStyle.EAGER)
public class TextField extends AbstractTextField {
/**
- * Tells if input is used to enter sensitive information that is not echoed
- * to display. Typically passwords.
- */
- @Deprecated
- private boolean secret = false;
-
- /**
- * Number of visible rows in a multiline TextField. Value 0 implies a
- * single-line text-editor.
- */
- @Deprecated
- private int rows = 0;
-
- /**
- * Tells if word-wrapping should be used in multiline mode.
- */
- @Deprecated
- private boolean wordwrap = true;
-
- /**
* Constructs an empty <code>TextField</code> with no caption.
*/
public TextField() {
@@ -106,7 +81,7 @@ public class TextField extends AbstractTextField {
*
* @param caption
* the caption <code>String</code> for the editor.
- * @param text
+ * @param value
* the initial text content of the editor.
*/
public TextField(String caption, String value) {
@@ -114,180 +89,4 @@ public class TextField extends AbstractTextField {
setCaption(caption);
}
- /**
- * Gets the secret property. If a field is used to enter secret information
- * the information is not echoed to display.
- *
- * @return <code>true</code> if the field is used to enter secret
- * information, <code>false</code> otherwise.
- *
- * @deprecated Starting from 6.5 use {@link PasswordField} instead for
- * secret text input.
- */
- @Deprecated
- public boolean isSecret() {
- return secret;
- }
-
- /**
- * Sets the secret property on and off. If a field is used to enter secret
- * information the information is not echoed to display.
- *
- * @param secret
- * the value specifying if the field is used to enter secret
- * information.
- * @deprecated Starting from 6.5 use {@link PasswordField} instead for
- * secret text input.
- */
- @Deprecated
- public void setSecret(boolean secret) {
- if (this.secret != secret) {
- this.secret = secret;
- requestRepaint();
- }
- }
-
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- if (isSecret()) {
- target.addAttribute("secret", true);
- }
-
- final int rows = getRows();
- if (rows != 0) {
- target.addAttribute("rows", rows);
- target.addAttribute("multiline", true);
-
- if (!isWordwrap()) {
- // Wordwrap is only painted if turned off to minimize
- // communications
- target.addAttribute("wordwrap", false);
- }
- }
-
- super.paintContent(target);
-
- }
-
- /**
- * Gets the number of rows in the editor. If the number of rows is set to 0,
- * the actual number of displayed rows is determined implicitly by the
- * adapter.
- *
- * @return number of explicitly set rows.
- * @deprecated Starting from 6.5 use {@link TextArea} for a multi-line text
- * input.
- *
- */
- @Deprecated
- public int getRows() {
- return rows;
- }
-
- /**
- * Sets the number of rows in the editor.
- *
- * @param rows
- * the number of rows for this editor.
- *
- * @deprecated Starting from 6.5 use {@link TextArea} for a multi-line text
- * input.
- */
- @Deprecated
- public void setRows(int rows) {
- if (rows < 0) {
- rows = 0;
- }
- if (this.rows != rows) {
- this.rows = rows;
- requestRepaint();
- }
- }
-
- /**
- * Tests if the editor is in word-wrap mode.
- *
- * @return <code>true</code> if the component is in the word-wrap mode,
- * <code>false</code> if not.
- * @deprecated Starting from 6.5 use {@link TextArea} for a multi-line text
- * input.
- */
- @Deprecated
- public boolean isWordwrap() {
- return wordwrap;
- }
-
- /**
- * Sets the editor's word-wrap mode on or off.
- *
- * @param wordwrap
- * the boolean value specifying if the editor should be in
- * word-wrap mode after the call or not.
- *
- * @deprecated Starting from 6.5 use {@link TextArea} for a multi-line text
- * input.
- */
- @Deprecated
- public void setWordwrap(boolean wordwrap) {
- if (this.wordwrap != wordwrap) {
- this.wordwrap = wordwrap;
- requestRepaint();
- }
- }
-
- /**
- * Sets the height of the {@link TextField} instance.
- *
- * <p>
- * Setting height for {@link TextField} also has a side-effect that puts
- * {@link TextField} into multiline mode (aka "textarea"). Multiline mode
- * can also be achieved by calling {@link #setRows(int)}. The height value
- * overrides the number of rows set by {@link #setRows(int)}.
- * <p>
- * If you want to set height of single line {@link TextField}, call
- * {@link #setRows(int)} with value 0 after setting the height. Setting rows
- * to 0 resets the side-effect.
- * <p>
- * Starting from 6.5 you should use {@link TextArea} instead of
- * {@link TextField} for multiline text input.
- *
- *
- * @see com.vaadin.ui.AbstractComponent#setHeight(float, int)
- */
- @Override
- public void setHeight(float height, int unit) {
- super.setHeight(height, unit);
- if (height > 1 && getClass() == TextField.class) {
- /*
- * In html based terminals we most commonly want to make component
- * to be textarea if height is defined. Setting row field above 0
- * will render component as textarea.
- */
-
- setRows(2);
- }
- }
-
- /**
- * Sets the height of the {@link TextField} instance.
- *
- * <p>
- * Setting height for {@link TextField} also has a side-effect that puts
- * {@link TextField} into multiline mode (aka "textarea"). Multiline mode
- * can also be achieved by calling {@link #setRows(int)}. The height value
- * overrides the number of rows set by {@link #setRows(int)}.
- * <p>
- * If you want to set height of single line {@link TextField}, call
- * {@link #setRows(int)} with value 0 after setting the height. Setting rows
- * to 0 resets the side-effect.
- *
- * @see com.vaadin.ui.AbstractComponent#setHeight(java.lang.String)
- */
- @Override
- public void setHeight(String height) {
- // will call setHeight(float, int) the actually does the magic. Method
- // is overridden just to document side-effects.
- super.setHeight(height);
- }
-
}
diff --git a/src/com/vaadin/ui/Tree.java b/src/com/vaadin/ui/Tree.java
index 554afda97c..db738fee58 100644
--- a/src/com/vaadin/ui/Tree.java
+++ b/src/com/vaadin/ui/Tree.java
@@ -28,7 +28,6 @@ import com.vaadin.event.DataBoundTransferable;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
-import com.vaadin.event.ItemClickEvent.ItemClickSource;
import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DragSource;
@@ -44,10 +43,11 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
-import com.vaadin.terminal.gwt.client.ui.VTree;
import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
import com.vaadin.terminal.gwt.client.ui.dd.VTargetInSubtree;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.terminal.gwt.client.ui.tree.TreeConnector;
+import com.vaadin.terminal.gwt.client.ui.tree.VTree;
import com.vaadin.tools.ReflectTools;
/**
@@ -60,10 +60,8 @@ import com.vaadin.tools.ReflectTools;
* @since 3.0
*/
@SuppressWarnings({ "serial", "deprecation" })
-@ClientWidget(VTree.class)
public class Tree extends AbstractSelect implements Container.Hierarchical,
- Action.Container, ItemClickSource, ItemClickNotifier, DragSource,
- DropTarget {
+ Action.Container, ItemClickNotifier, DragSource, DropTarget {
/* Private members */
@@ -80,7 +78,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
/**
* Action mapper.
*/
- private KeyMapper actionMapper = null;
+ private KeyMapper<Action> actionMapper = null;
/**
* Is the tree selectable on the client side.
@@ -447,7 +445,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
(String) variables.get("action"), ",");
if (st.countTokens() == 2) {
final Object itemId = itemIdMapper.get(st.nextToken());
- final Action action = (Action) actionMapper.get(st.nextToken());
+ final Action action = actionMapper.get(st.nextToken());
if (action != null && (itemId == null || containsId(itemId))
&& actionHandlers != null) {
for (Handler ah : actionHandlers) {
@@ -609,7 +607,8 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
if (itemStyleGenerator != null) {
String stylename = itemStyleGenerator.getStyle(itemId);
if (stylename != null) {
- target.addAttribute("style", stylename);
+ target.addAttribute(TreeConnector.ATTRIBUTE_NODE_STYLE,
+ stylename);
}
}
@@ -622,10 +621,12 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
}
// Adds the attributes
- target.addAttribute("caption", getItemCaption(itemId));
+ target.addAttribute(TreeConnector.ATTRIBUTE_NODE_CAPTION,
+ getItemCaption(itemId));
final Resource icon = getItemIcon(itemId);
if (icon != null) {
- target.addAttribute("icon", getItemIcon(itemId));
+ target.addAttribute(TreeConnector.ATTRIBUTE_NODE_ICON,
+ getItemIcon(itemId));
}
final String key = itemIdMapper.key(itemId);
target.addAttribute("key", key);
@@ -682,10 +683,12 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
final Action a = i.next();
target.startTag("action");
if (a.getCaption() != null) {
- target.addAttribute("caption", a.getCaption());
+ target.addAttribute(TreeConnector.ATTRIBUTE_ACTION_CAPTION,
+ a.getCaption());
}
if (a.getIcon() != null) {
- target.addAttribute("icon", a.getIcon());
+ target.addAttribute(TreeConnector.ATTRIBUTE_ACTION_ICON,
+ a.getIcon());
}
target.addAttribute("key", actionMapper.key(a));
target.endTag("action");
@@ -1021,7 +1024,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
if (actionHandlers == null) {
actionHandlers = new LinkedList<Action.Handler>();
- actionMapper = new KeyMapper();
+ actionMapper = new KeyMapper<Action>();
}
if (!actionHandlers.contains(actionHandler)) {
diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java
index 43bc7a80fe..9607add2c9 100644
--- a/src/com/vaadin/ui/TreeTable.java
+++ b/src/com/vaadin/ui/TreeTable.java
@@ -13,22 +13,21 @@ import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
-import com.google.gwt.user.client.ui.Tree;
+import com.vaadin.data.Collapsible;
import com.vaadin.data.Container;
import com.vaadin.data.Container.Hierarchical;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.data.util.ContainerHierarchicalWrapper;
import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.data.util.HierarchicalContainerOrderedWrapper;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VTreeTable;
+import com.vaadin.terminal.gwt.client.ui.treetable.TreeTableConnector;
import com.vaadin.ui.Tree.CollapseEvent;
import com.vaadin.ui.Tree.CollapseListener;
import com.vaadin.ui.Tree.ExpandEvent;
import com.vaadin.ui.Tree.ExpandListener;
-import com.vaadin.ui.treetable.Collapsible;
-import com.vaadin.ui.treetable.HierarchicalContainerOrderedWrapper;
/**
* TreeTable extends the {@link Table} component so that it can also visualize a
@@ -48,7 +47,6 @@ import com.vaadin.ui.treetable.HierarchicalContainerOrderedWrapper;
* share UI state in the container.
*/
@SuppressWarnings({ "serial" })
-@ClientWidget(VTreeTable.class)
public class TreeTable extends Table implements Hierarchical {
private static final Logger logger = Logger.getLogger(TreeTable.class
@@ -454,7 +452,8 @@ public class TreeTable extends Table implements Hierarchical {
Object object = visibleColumns2[i];
if (hierarchyColumnId.equals(object)) {
target.addAttribute(
- VTreeTable.ATTRIBUTE_HIERARCHY_COLUMN_INDEX, i);
+ TreeTableConnector.ATTRIBUTE_HIERARCHY_COLUMN_INDEX,
+ i);
break;
}
}
@@ -552,7 +551,9 @@ public class TreeTable extends Table implements Hierarchical {
public void setContainerDataSource(Container newDataSource) {
cStrategy = null;
- containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier);
+ // FIXME: This disables partial updates until TreeTable is fixed so it
+ // does not change component hierarchy during paint
+ containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier) && false;
if (!(newDataSource instanceof Hierarchical)) {
newDataSource = new ContainerHierarchicalWrapper(newDataSource);
diff --git a/src/com/vaadin/ui/TwinColSelect.java b/src/com/vaadin/ui/TwinColSelect.java
index 1c1fe07a5c..5539236f77 100644
--- a/src/com/vaadin/ui/TwinColSelect.java
+++ b/src/com/vaadin/ui/TwinColSelect.java
@@ -9,14 +9,13 @@ import java.util.Collection;
import com.vaadin.data.Container;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VTwinColSelect;
+import com.vaadin.terminal.gwt.client.ui.twincolselect.VTwinColSelect;
/**
* Multiselect component with two lists: left side for available items and right
* side for selected items.
*/
@SuppressWarnings("serial")
-@ClientWidget(VTwinColSelect.class)
public class TwinColSelect extends AbstractSelect {
private int columns = 0;
diff --git a/src/com/vaadin/ui/Upload.java b/src/com/vaadin/ui/Upload.java
index 9d684291a5..4dff71e45b 100644
--- a/src/com/vaadin/ui/Upload.java
+++ b/src/com/vaadin/ui/Upload.java
@@ -15,10 +15,9 @@ import java.util.Map;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.StreamVariable.StreamingProgressEvent;
-import com.vaadin.terminal.gwt.client.ui.VUpload;
+import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.gwt.server.NoInputStreamException;
import com.vaadin.terminal.gwt.server.NoOutputStreamException;
-import com.vaadin.ui.ClientWidget.LoadStyle;
/**
* Component for uploading files from client to server.
@@ -61,8 +60,8 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VUpload.class, loadStyle = LoadStyle.LAZY)
-public class Upload extends AbstractComponent implements Component.Focusable {
+public class Upload extends AbstractComponent implements Component.Focusable,
+ Vaadin6Component {
/**
* Should the field be focused on next repaint?
@@ -123,7 +122,6 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
* java.util.Map)
*/
- @Override
public void changeVariables(Object source, Map<String, Object> variables) {
if (variables.containsKey("pollForStart")) {
int id = (Integer) variables.get("pollForStart");
@@ -143,7 +141,6 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @throws PaintException
* if the paint operation failed.
*/
- @Override
public void paintContent(PaintTarget target) throws PaintException {
if (notStarted) {
target.addAttribute("notStarted", true);
diff --git a/src/com/vaadin/ui/UriFragmentUtility.java b/src/com/vaadin/ui/UriFragmentUtility.java
deleted file mode 100644
index 5eaffbde6f..0000000000
--- a/src/com/vaadin/ui/UriFragmentUtility.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.util.Map;
-
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
-/**
- * Experimental web browser dependent component for URI fragment (part after
- * hash mark "#") reading and writing.
- *
- * Component can be used to workaround common ajax web applications pitfalls:
- * bookmarking a program state and back button.
- *
- */
-@SuppressWarnings("serial")
-@ClientWidget(value = VUriFragmentUtility.class, loadStyle = LoadStyle.EAGER)
-public class UriFragmentUtility extends AbstractComponent {
-
- /**
- * Listener that listens changes in URI fragment.
- */
- public interface FragmentChangedListener extends Serializable {
-
- public void fragmentChanged(FragmentChangedEvent source);
-
- }
-
- /**
- * Event fired when uri fragment changes.
- */
- public class FragmentChangedEvent extends Component.Event {
-
- /**
- * Creates a new instance of UriFragmentReader change event.
- *
- * @param source
- * the Source of the event.
- */
- public FragmentChangedEvent(Component source) {
- super(source);
- }
-
- /**
- * Gets the UriFragmentReader where the event occurred.
- *
- * @return the Source of the event.
- */
- public UriFragmentUtility getUriFragmentUtility() {
- return (UriFragmentUtility) getSource();
- }
- }
-
- private static final Method FRAGMENT_CHANGED_METHOD;
-
- static {
- try {
- FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
- .getDeclaredMethod("fragmentChanged",
- new Class[] { FragmentChangedEvent.class });
- } catch (final java.lang.NoSuchMethodException e) {
- // This should never happen
- throw new java.lang.RuntimeException(
- "Internal error finding methods in FragmentChangedListener");
- }
- }
-
- public void addListener(FragmentChangedListener listener) {
- addListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
- }
-
- public void removeListener(FragmentChangedListener listener) {
- removeListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
- }
-
- private String fragment;
-
- public UriFragmentUtility() {
- // immediate by default
- setImmediate(true);
- }
-
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
- target.addVariable(this, "fragment", fragment);
- }
-
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
- fragment = (String) variables.get("fragment");
- fireEvent(new FragmentChangedEvent(this));
- }
-
- /**
- * Gets currently set URI fragment.
- * <p>
- * To listen changes in fragment, hook a {@link FragmentChangedListener}.
- * <p>
- * Note that initial URI fragment that user used to enter the application
- * will be read after application init. It fires FragmentChangedEvent only
- * if it is not the same as on server side.
- *
- * @return the current fragment in browser uri or null if not known
- */
- public String getFragment() {
- return fragment;
- }
-
- /**
- * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
- *
- * @param newFragment
- * id of the new fragment
- * @param fireEvent
- * true to fire event
- * @see FragmentChangedEvent
- * @see FragmentChangedListener
- */
- public void setFragment(String newFragment, boolean fireEvent) {
- if ((newFragment == null && fragment != null)
- || (newFragment != null && !newFragment.equals(fragment))) {
- fragment = newFragment;
- if (fireEvent) {
- fireEvent(new FragmentChangedEvent(this));
- }
- requestRepaint();
- }
- }
-
- /**
- * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
- *
- * @param newFragment
- * id of the new fragment
- * @see FragmentChangedEvent
- * @see FragmentChangedListener
- */
- public void setFragment(String newFragment) {
- setFragment(newFragment, true);
- }
-
-}
diff --git a/src/com/vaadin/ui/VerticalLayout.java b/src/com/vaadin/ui/VerticalLayout.java
index c40aeaea30..a04d052d98 100644
--- a/src/com/vaadin/ui/VerticalLayout.java
+++ b/src/com/vaadin/ui/VerticalLayout.java
@@ -3,9 +3,6 @@
*/
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VVerticalLayout;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
/**
* Vertical layout
*
@@ -19,7 +16,6 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* @since 5.3
*/
@SuppressWarnings("serial")
-@ClientWidget(value = VVerticalLayout.class, loadStyle = LoadStyle.EAGER)
public class VerticalLayout extends AbstractOrderedLayout {
public VerticalLayout() {
diff --git a/src/com/vaadin/ui/VerticalSplitPanel.java b/src/com/vaadin/ui/VerticalSplitPanel.java
index 699bd9287c..0630240e9c 100644
--- a/src/com/vaadin/ui/VerticalSplitPanel.java
+++ b/src/com/vaadin/ui/VerticalSplitPanel.java
@@ -3,9 +3,6 @@
*/
package com.vaadin.ui;
-import com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
/**
* A vertical split panel contains two components and lays them vertically. The
* first component is above the second component.
@@ -23,7 +20,6 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
* </pre>
*
*/
-@ClientWidget(value = VSplitPanelVertical.class, loadStyle = LoadStyle.EAGER)
public class VerticalSplitPanel extends AbstractSplitPanel {
public VerticalSplitPanel() {
diff --git a/src/com/vaadin/ui/Video.java b/src/com/vaadin/ui/Video.java
index ed6588f96a..28fbfb0547 100644
--- a/src/com/vaadin/ui/Video.java
+++ b/src/com/vaadin/ui/Video.java
@@ -7,7 +7,7 @@ package com.vaadin.ui;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.gwt.client.ui.VVideo;
+import com.vaadin.terminal.gwt.client.ui.video.VideoConnector;
/**
* The Video component translates into an HTML5 &lt;video&gt; element and as
@@ -30,7 +30,6 @@ import com.vaadin.terminal.gwt.client.ui.VVideo;
* @author Vaadin Ltd
* @since 6.7.0
*/
-@ClientWidget(VVideo.class)
public class Video extends AbstractMedia {
private Resource poster;
@@ -80,7 +79,7 @@ public class Video extends AbstractMedia {
public void paintContent(PaintTarget target) throws PaintException {
super.paintContent(target);
if (getPoster() != null) {
- target.addAttribute(VVideo.ATTR_POSTER, getPoster());
+ target.addAttribute(VideoConnector.ATTR_POSTER, getPoster());
}
}
}
diff --git a/src/com/vaadin/ui/Window.java b/src/com/vaadin/ui/Window.java
index 5f6c29f182..3c17baf414 100644
--- a/src/com/vaadin/ui/Window.java
+++ b/src/com/vaadin/ui/Window.java
@@ -6,15 +6,7 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.Map;
-import java.util.Set;
import com.vaadin.Application;
import com.vaadin.event.FieldEvents.BlurEvent;
@@ -23,20 +15,17 @@ import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier;
+import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.event.ShortcutListener;
-import com.vaadin.terminal.DownloadStream;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.ParameterHandler;
-import com.vaadin.terminal.Resource;
-import com.vaadin.terminal.Sizeable;
-import com.vaadin.terminal.Terminal;
-import com.vaadin.terminal.URIHandler;
-import com.vaadin.terminal.gwt.client.ui.VView;
-import com.vaadin.terminal.gwt.client.ui.VWindow;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.ui.window.WindowServerRpc;
+import com.vaadin.terminal.gwt.client.ui.window.WindowState;
/**
* A component that represents an application (browser native) window or a sub
@@ -84,144 +73,15 @@ import com.vaadin.terminal.gwt.client.ui.VWindow;
* @since 3.0
*/
@SuppressWarnings("serial")
-@ClientWidget(VWindow.class)
-public class Window extends Panel implements URIHandler, ParameterHandler,
- FocusNotifier, BlurNotifier {
+public class Window extends Panel implements FocusNotifier, BlurNotifier,
+ Vaadin6Component {
- /**
- * <b>Application window only</b>. A border style used for opening resources
- * in a window without a border.
- */
- public static final int BORDER_NONE = 0;
-
- /**
- * <b>Application window only</b>. A border style used for opening resources
- * in a window with a minimal border.
- */
- public static final int BORDER_MINIMAL = 1;
-
- /**
- * <b>Application window only</b>. A border style that indicates that the
- * default border style should be used when opening resources.
- */
- public static final int BORDER_DEFAULT = 2;
-
- /**
- * <b>Application window only</b>. The user terminal for this window.
- */
- private Terminal terminal = null;
-
- /**
- * <b>Application window only</b>. The application this window is attached
- * to or null.
- */
- private Application application = null;
-
- /**
- * <b>Application window only</b>. List of URI handlers for this window.
- */
- private LinkedList<URIHandler> uriHandlerList = null;
-
- /**
- * <b>Application window only</b>. List of parameter handlers for this
- * window.
- */
- private LinkedList<ParameterHandler> parameterHandlerList = null;
-
- /**
- * <b>Application window only</b>. List of sub windows in this window. A sub
- * window cannot have other sub windows.
- */
- private final LinkedHashSet<Window> subwindows = new LinkedHashSet<Window>();
-
- /**
- * <b>Application window only</b>. Explicitly specified theme of this window
- * or null if the application theme should be used.
- */
- private String theme = null;
-
- /**
- * <b>Application window only</b>. Resources to be opened automatically on
- * next repaint. The list is automatically cleared when it has been sent to
- * the client.
- */
- private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>();
+ private WindowServerRpc rpc = new WindowServerRpc() {
- /**
- * <b>Application window only</b>. Unique name of the window used to
- * identify it.
- */
- private String name = null;
-
- /**
- * <b>Application window only.</b> Border mode of the Window.
- */
- private int border = BORDER_DEFAULT;
-
- /**
- * <b>Sub window only</b>. Top offset in pixels for the sub window (relative
- * to the parent application window) or -1 if unspecified.
- */
- private int positionY = -1;
-
- /**
- * <b>Sub window only</b>. Left offset in pixels for the sub window
- * (relative to the parent application window) or -1 if unspecified.
- */
- private int positionX = -1;
-
- /**
- * <b>Application window only</b>. A list of notifications that are waiting
- * to be sent to the client. Cleared (set to null) when the notifications
- * have been sent.
- */
- private LinkedList<Notification> notifications;
-
- /**
- * <b>Sub window only</b>. Modality flag for sub window.
- */
- private boolean modal = false;
-
- /**
- * <b>Sub window only</b>. Controls if the end user can resize the window.
- */
- private boolean resizable = true;
-
- /**
- * <b>Sub window only</b>. Controls if the end user can move the window by
- * dragging.
- */
- private boolean draggable = true;
-
- /**
- * <b>Sub window only</b>. Flag which is true if the window is centered on
- * the screen.
- */
- private boolean centerRequested = false;
-
- /**
- * Should resize recalculate layouts lazily (as opposed to immediately)
- */
- private boolean resizeLazy = false;
-
- /**
- * Component that should be focused after the next repaint. Null if no focus
- * change should take place.
- */
- private Focusable pendingFocus;
-
- /**
- * <b>Application window only</b>. A list of javascript commands that are
- * waiting to be sent to the client. Cleared (set to null) when the commands
- * have been sent.
- */
- private ArrayList<String> jsExecQueue = null;
-
- /**
- * The component that should be scrolled into view after the next repaint.
- * Null if nothing should be scrolled into view.
- */
- private Component scrollIntoView;
+ public void click(MouseEventDetails mouseDetails) {
+ fireEvent(new ClickEvent(Window.this, mouseDetails));
+ }
+ };
/**
* Creates a new unnamed window with a default layout.
@@ -250,7 +110,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
*/
public Window(String caption, ComponentContainer content) {
super(caption, content);
- setScrollable(true);
+ registerRpc(rpc);
setSizeUndefined();
}
@@ -269,288 +129,8 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
super.addComponent(c);
}
- /**
- * <b>Application window only</b>. Gets the user terminal.
- *
- * @return the user terminal
- */
- public Terminal getTerminal() {
- return terminal;
- }
-
/* ********************************************************************* */
- /**
- * Gets the parent window of the component.
- * <p>
- * This is always the window itself.
- * </p>
- * <p>
- * <b>This method is not meant to be overridden. Due to CDI requirements we
- * cannot declare it as final even though it should be final.</b>
- * </p>
- *
- * @see Component#getWindow()
- * @return the window itself
- */
- @Override
- public Window getWindow() {
- return this;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#getApplication()
- */
- @Override
- public Application getApplication() {
- if (getParent() == null) {
- return application;
- }
- return getParent().getApplication();
- }
-
- /**
- * Gets the parent component of the window.
- *
- * <p>
- * The parent of an application window is always null. The parent of a sub
- * window is the application window the sub window is attached to.
- * </p>
- * <p>
- * <b>This method is not meant to be overridden. Due to CDI requirements we
- * cannot declare it as final even though it should be final.</b>
- * </p>
- *
- *
- * @return the parent window
- * @see Component#getParent()
- */
- @Override
- public Window getParent() {
- return (Window) super.getParent();
- }
-
- /* ********************************************************************* */
-
- /**
- * <b>Application window only</b>. Adds a new URI handler to this window. If
- * this is a sub window the URI handler is attached to the parent
- * application window.
- *
- * @param handler
- * the URI handler to add.
- */
- public void addURIHandler(URIHandler handler) {
- if (getParent() != null) {
- // this is subwindow, attach to main level instead
- // TODO hold internal list also and remove on detach
- Window mainWindow = getParent();
- mainWindow.addURIHandler(handler);
- } else {
- if (uriHandlerList == null) {
- uriHandlerList = new LinkedList<URIHandler>();
- }
- synchronized (uriHandlerList) {
- if (!uriHandlerList.contains(handler)) {
- uriHandlerList.addLast(handler);
- }
- }
- }
- }
-
- /**
- * <b>Application window only</b>. Removes the URI handler from this window.
- * If this is a sub window the URI handler is removed from the parent
- * application window.
- *
- * @param handler
- * the URI handler to remove.
- */
- public void removeURIHandler(URIHandler handler) {
- if (getParent() != null) {
- // this is subwindow
- Window mainWindow = getParent();
- mainWindow.removeURIHandler(handler);
- } else {
- if (handler == null || uriHandlerList == null) {
- return;
- }
- synchronized (uriHandlerList) {
- uriHandlerList.remove(handler);
- if (uriHandlerList.isEmpty()) {
- uriHandlerList = null;
- }
- }
- }
- }
-
- /**
- * <b>Application window only</b>. Handles an URI by passing the URI to all
- * URI handlers defined using {@link #addURIHandler(URIHandler)}. All URI
- * handlers are called for each URI but no more than one handler may return
- * a {@link DownloadStream}. If more than one stream is returned a
- * {@code RuntimeException} is thrown.
- *
- * @param context
- * The URL of the application
- * @param relativeUri
- * The URI relative to {@code context}
- * @return A {@code DownloadStream} that one of the URI handlers returned,
- * null if no {@code DownloadStream} was returned.
- */
- public DownloadStream handleURI(URL context, String relativeUri) {
-
- DownloadStream result = null;
- if (uriHandlerList != null) {
- Object[] handlers;
- synchronized (uriHandlerList) {
- handlers = uriHandlerList.toArray();
- }
- for (int i = 0; i < handlers.length; i++) {
- final DownloadStream ds = ((URIHandler) handlers[i]).handleURI(
- context, relativeUri);
- if (ds != null) {
- if (result != null) {
- throw new RuntimeException("handleURI for " + context
- + " uri: '" + relativeUri
- + "' returns ambigious result.");
- }
- result = ds;
- }
- }
- }
- return result;
- }
-
- /* ********************************************************************* */
-
- /**
- * <b>Application window only</b>. Adds a new parameter handler to this
- * window. If this is a sub window the parameter handler is attached to the
- * parent application window.
- *
- * @param handler
- * the parameter handler to add.
- */
- public void addParameterHandler(ParameterHandler handler) {
- if (getParent() != null) {
- // this is subwindow
- // TODO hold internal list also and remove on detach
- Window mainWindow = getParent();
- mainWindow.addParameterHandler(handler);
- } else {
- if (parameterHandlerList == null) {
- parameterHandlerList = new LinkedList<ParameterHandler>();
- }
- synchronized (parameterHandlerList) {
- if (!parameterHandlerList.contains(handler)) {
- parameterHandlerList.addLast(handler);
- }
- }
- }
-
- }
-
- /**
- * <b>Application window only</b>. Removes the parameter handler from this
- * window. If this is a sub window the parameter handler is removed from the
- * parent application window.
- *
- * @param handler
- * the parameter handler to remove.
- */
- public void removeParameterHandler(ParameterHandler handler) {
- if (getParent() != null) {
- // this is subwindow
- Window mainWindow = getParent();
- mainWindow.removeParameterHandler(handler);
- } else {
- if (handler == null || parameterHandlerList == null) {
- return;
- }
- synchronized (parameterHandlerList) {
- parameterHandlerList.remove(handler);
- if (parameterHandlerList.isEmpty()) {
- parameterHandlerList = null;
- }
- }
- }
- }
-
- /**
- * <b>Application window only</b>. Handles parameters by passing the
- * parameters to all {@code ParameterHandler}s defined using
- * {@link #addParameterHandler(ParameterHandler)}. All
- * {@code ParameterHandler}s are called for each set of parameters.
- *
- * @param parameters
- * a map containing the parameter names and values
- * @see ParameterHandler#handleParameters(Map)
- */
- public void handleParameters(Map<String, String[]> parameters) {
- if (parameterHandlerList != null) {
- Object[] handlers;
- synchronized (parameterHandlerList) {
- handlers = parameterHandlerList.toArray();
- }
- for (int i = 0; i < handlers.length; i++) {
- ((ParameterHandler) handlers[i]).handleParameters(parameters);
- }
- }
- }
-
- /* ********************************************************************* */
-
- /**
- * <b>Application window only</b>. Gets the theme for this window.
- * <p>
- * If the theme for this window is not explicitly set, the application theme
- * name is returned. If the window is not attached to an application, the
- * terminal default theme name is returned. If the theme name cannot be
- * determined, null is returned
- * </p>
- * <p>
- * Subwindows do not support themes and return the theme used by the parent
- * window
- * </p>
- *
- * @return the name of the theme used for the window
- */
- public String getTheme() {
- if (getParent() != null) {
- return (getParent()).getTheme();
- }
- if (theme != null) {
- return theme;
- }
- if ((application != null) && (application.getTheme() != null)) {
- return application.getTheme();
- }
- if (terminal != null) {
- return terminal.getDefaultTheme();
- }
- return null;
- }
-
- /**
- * <b>Application window only</b>. Sets the name of the theme to use for
- * this window. Changing the theme will cause the page to be reloaded.
- *
- * @param theme
- * the name of the new theme for this window or null to use the
- * application theme.
- */
- public void setTheme(String theme) {
- if (getParent() != null) {
- throw new UnsupportedOperationException(
- "Setting theme for sub-windows is not supported.");
- }
- this.theme = theme;
- requestRepaint();
- }
-
/*
* (non-Javadoc)
*
@@ -559,505 +139,13 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
@Override
public synchronized void paintContent(PaintTarget target)
throws PaintException {
-
- // Sets the window name
- final String name = getName();
- target.addAttribute("name", name == null ? "" : name);
-
- // Sets the window theme
- final String theme = getTheme();
- target.addAttribute("theme", theme == null ? "" : theme);
-
- if (modal) {
- target.addAttribute("modal", true);
- }
-
- if (resizable) {
- target.addAttribute("resizable", true);
- }
- if (resizeLazy) {
- target.addAttribute(VView.RESIZE_LAZY, resizeLazy);
- }
-
- if (!draggable) {
- // Inverted to prevent an extra attribute for almost all sub windows
- target.addAttribute("fixedposition", true);
- }
-
if (bringToFront != null) {
target.addAttribute("bringToFront", bringToFront.intValue());
bringToFront = null;
}
- if (centerRequested) {
- target.addAttribute("center", true);
- centerRequested = false;
- }
-
- if (scrollIntoView != null) {
- target.addAttribute("scrollTo", scrollIntoView);
- scrollIntoView = null;
- }
-
- // Marks the main window
- if (getApplication() != null
- && this == getApplication().getMainWindow()) {
- target.addAttribute("main", true);
- }
-
- if (getContent() != null) {
- if (getContent().getHeightUnits() == Sizeable.UNITS_PERCENTAGE) {
- target.addAttribute("layoutRelativeHeight", true);
- }
- if (getContent().getWidthUnits() == Sizeable.UNITS_PERCENTAGE) {
- target.addAttribute("layoutRelativeWidth", true);
- }
- }
-
- // Open requested resource
- synchronized (openList) {
- if (!openList.isEmpty()) {
- for (final Iterator<OpenResource> i = openList.iterator(); i
- .hasNext();) {
- (i.next()).paintContent(target);
- }
- openList.clear();
- }
- }
-
// Contents of the window panel is painted
super.paintContent(target);
-
- // Add executable javascripts if needed
- if (jsExecQueue != null) {
- for (String script : jsExecQueue) {
- target.startTag("execJS");
- target.addAttribute("script", script);
- target.endTag("execJS");
- }
- jsExecQueue = null;
- }
-
- // Window position
- target.addVariable(this, "positionx", getPositionX());
- target.addVariable(this, "positiony", getPositionY());
-
- // Window closing
- target.addVariable(this, "close", false);
-
- if (getParent() == null) {
- // Paint subwindows
- for (final Iterator<Window> i = subwindows.iterator(); i.hasNext();) {
- final Window w = i.next();
- w.paint(target);
- }
- } else {
- // mark subwindows
- target.addAttribute("sub", true);
- }
-
- // Paint notifications
- if (notifications != null) {
- target.startTag("notifications");
- for (final Iterator<Notification> it = notifications.iterator(); it
- .hasNext();) {
- final Notification n = it.next();
- target.startTag("notification");
- if (n.getCaption() != null) {
- target.addAttribute("caption", n.getCaption());
- }
- if (n.getMessage() != null) {
- target.addAttribute("message", n.getMessage());
- }
- if (n.getIcon() != null) {
- target.addAttribute("icon", n.getIcon());
- }
- if (!n.isHtmlContentAllowed()) {
- target.addAttribute(
- VView.NOTIFICATION_HTML_CONTENT_NOT_ALLOWED, true);
- }
- target.addAttribute("position", n.getPosition());
- target.addAttribute("delay", n.getDelayMsec());
- if (n.getStyleName() != null) {
- target.addAttribute("style", n.getStyleName());
- }
- target.endTag("notification");
- }
- target.endTag("notifications");
- notifications = null;
- }
-
- if (pendingFocus != null) {
- // ensure focused component is still attached to this main window
- if (pendingFocus.getWindow() == this
- || (pendingFocus.getWindow() != null && pendingFocus
- .getWindow().getParent() == this)) {
- target.addAttribute("focused", pendingFocus);
- }
- pendingFocus = null;
- }
-
- }
-
- /* ********************************************************************* */
-
- /**
- * Scrolls any component between the component and window to a suitable
- * position so the component is visible to the user. The given component
- * must be inside this window.
- *
- * @param component
- * the component to be scrolled into view
- * @throws IllegalArgumentException
- * if {@code component} is not inside this window
- */
- public void scrollIntoView(Component component)
- throws IllegalArgumentException {
- if (component.getWindow() != this) {
- throw new IllegalArgumentException(
- "The component where to scroll must be inside this window.");
- }
- scrollIntoView = component;
- requestRepaint();
- }
-
- /**
- * Opens the given resource in this window. The contents of this Window is
- * replaced by the {@code Resource}.
- *
- * @param resource
- * the resource to show in this window
- */
- public void open(Resource resource) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, null, -1, -1,
- BORDER_DEFAULT));
- }
- }
- requestRepaint();
- }
-
- /* ********************************************************************* */
-
- /**
- * Opens the given resource in a window with the given name.
- * <p>
- * The supplied {@code windowName} is used as the target name in a
- * window.open call in the client. This means that special values such as
- * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
- * <code>null</code> window name is also a special case.
- * </p>
- * <p>
- * "", null and "_self" as {@code windowName} all causes the resource to be
- * opened in the current window, replacing any old contents. For
- * downloadable content you should avoid "_self" as "_self" causes the
- * client to skip rendering of any other changes as it considers them
- * irrelevant (the page will be replaced by the resource). This can speed up
- * the opening of a resource, but it might also put the client side into an
- * inconsistent state if the window content is not completely replaced e.g.,
- * if the resource is downloaded instead of displayed in the browser.
- * </p>
- * <p>
- * "_blank" as {@code windowName} causes the resource to always be opened in
- * a new window or tab (depends on the browser and browser settings).
- * </p>
- * <p>
- * "_top" and "_parent" as {@code windowName} works as specified by the HTML
- * standard.
- * </p>
- * <p>
- * Any other {@code windowName} will open the resource in a window with that
- * name, either by opening a new window/tab in the browser or by replacing
- * the contents of an existing window with that name.
- * </p>
- *
- * @param resource
- * the resource.
- * @param windowName
- * the name of the window.
- */
- public void open(Resource resource, String windowName) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, windowName, -1, -1,
- BORDER_DEFAULT));
- }
- }
- requestRepaint();
- }
-
- /**
- * Opens the given resource in a window with the given size, border and
- * name. For more information on the meaning of {@code windowName}, see
- * {@link #open(Resource, String)}.
- *
- * @param resource
- * the resource.
- * @param windowName
- * the name of the window.
- * @param width
- * the width of the window in pixels
- * @param height
- * the height of the window in pixels
- * @param border
- * the border style of the window. See {@link #BORDER_NONE
- * Window.BORDER_* constants}
- */
- public void open(Resource resource, String windowName, int width,
- int height, int border) {
- synchronized (openList) {
- if (!openList.contains(resource)) {
- openList.add(new OpenResource(resource, windowName, width,
- height, border));
- }
- }
- requestRepaint();
- }
-
- /* ********************************************************************* */
-
- /**
- * Gets the full URL of the window. The returned URL is window specific and
- * can be used to directly refer to the window.
- * <p>
- * Note! This method can not be used for portlets.
- * </p>
- *
- * @return the URL of the window or null if the window is not attached to an
- * application
- */
- public URL getURL() {
-
- if (application == null) {
- return null;
- }
-
- try {
- return new URL(application.getURL(), getName() + "/");
- } catch (final MalformedURLException e) {
- throw new RuntimeException(
- "Internal problem getting window URL, please report");
- }
- }
-
- /**
- * <b>Application window only</b>. Gets the unique name of the window. The
- * name of the window is used to uniquely identify it.
- * <p>
- * The name also determines the URL that can be used for direct access to a
- * window. All windows can be accessed through
- * {@code http://host:port/app/win} where {@code http://host:port/app} is
- * the application URL (as returned by {@link Application#getURL()} and
- * {@code win} is the window name.
- * </p>
- * <p>
- * Note! Portlets do not support direct window access through URLs.
- * </p>
- *
- * @return the Name of the Window.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the border style of the window.
- *
- * @see #setBorder(int)
- * @return the border style for the window
- */
- public int getBorder() {
- return border;
- }
-
- /**
- * Sets the border style for this window. Valid values are
- * {@link Window#BORDER_NONE}, {@link Window#BORDER_MINIMAL},
- * {@link Window#BORDER_DEFAULT}.
- * <p>
- * <b>Note!</b> Setting this seems to currently have no effect whatsoever on
- * the window.
- * </p>
- *
- * @param border
- * the border style to set
- */
- public void setBorder(int border) {
- this.border = border;
- }
-
- /**
- * Sets the application this window is attached to.
- *
- * <p>
- * This method is called by the framework and should not be called directly
- * from application code. {@link com.vaadin.Application#addWindow(Window)}
- * should be used to add the window to an application and
- * {@link com.vaadin.Application#removeWindow(Window)} to remove the window
- * from the application.
- * </p>
- * <p>
- * This method invokes {@link Component#attach()} and
- * {@link Component#detach()} methods when necessary.
- * <p>
- *
- * @param application
- * the application the window is attached to
- */
- public void setApplication(Application application) {
-
- // If the application is not changed, dont do nothing
- if (application == this.application) {
- return;
- }
-
- // Sends detach event if the window is connected to application
- if (this.application != null) {
- detach();
- }
-
- // Connects to new parent
- this.application = application;
-
- // Sends the attach event if connected to a window
- if (application != null) {
- attach();
- }
- }
-
- /**
- * <b>Application window only</b>. Sets the unique name of the window. The
- * name of the window is used to uniquely identify it inside the
- * application.
- * <p>
- * The name also determines the URL that can be used for direct access to a
- * window. All windows can be accessed through
- * {@code http://host:port/app/win} where {@code http://host:port/app} is
- * the application URL (as returned by {@link Application#getURL()} and
- * {@code win} is the window name.
- * </p>
- * <p>
- * This method can only be called before the window is added to an
- * application.
- * </p>
- * <p>
- * Note! Portlets do not support direct window access through URLs.
- * </p>
- *
- * @param name
- * the new name for the window or null if the application should
- * automatically assign a name to it
- * @throws IllegalStateException
- * if the window is attached to an application
- */
- public void setName(String name) throws IllegalStateException {
-
- // The name can not be changed in application
- if (getApplication() != null) {
- throw new IllegalStateException(
- "Window name can not be changed while "
- + "the window is in application");
- }
-
- this.name = name;
- }
-
- /**
- * Sets the user terminal. Used by the terminal adapter, should never be
- * called from application code.
- *
- * @param type
- * the terminal to set.
- */
- public void setTerminal(Terminal type) {
- terminal = type;
- }
-
- /**
- * Private class for storing properties related to opening resources.
- */
- private class OpenResource implements Serializable {
-
- /**
- * The resource to open
- */
- private final Resource resource;
-
- /**
- * The name of the target window
- */
- private final String name;
-
- /**
- * The width of the target window
- */
- private final int width;
-
- /**
- * The height of the target window
- */
- private final int height;
-
- /**
- * The border style of the target window
- */
- private final int border;
-
- /**
- * Creates a new open resource.
- *
- * @param resource
- * The resource to open
- * @param name
- * The name of the target window
- * @param width
- * The width of the target window
- * @param height
- * The height of the target window
- * @param border
- * The border style of the target window
- */
- private OpenResource(Resource resource, String name, int width,
- int height, int border) {
- this.resource = resource;
- this.name = name;
- this.width = width;
- this.height = height;
- this.border = border;
- }
-
- /**
- * Paints the open request. Should be painted inside the window.
- *
- * @param target
- * the paint target
- * @throws PaintException
- * if the paint operation fails
- */
- private void paintContent(PaintTarget target) throws PaintException {
- target.startTag("open");
- target.addAttribute("src", resource);
- if (name != null && name.length() > 0) {
- target.addAttribute("name", name);
- }
- if (width >= 0) {
- target.addAttribute("width", width);
- }
- if (height >= 0) {
- target.addAttribute("height", height);
- }
- switch (border) {
- case Window.BORDER_MINIMAL:
- target.addAttribute("border", "minimal");
- break;
- case Window.BORDER_NONE:
- target.addAttribute("border", "none");
- break;
- }
-
- target.endTag("open");
- }
}
/*
@@ -1068,6 +156,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
@Override
public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Are these for top level windows or sub windows?
boolean sizeHasChanged = false;
// size is handled in super class, but resize events only in windows ->
// so detect if size change occurs before super.changeVariables()
@@ -1137,17 +226,15 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* {@link CloseListener}.
* </p>
*/
- protected void close() {
- Window parent = getParent();
- if (parent == null) {
- fireClose();
- } else {
+ public void close() {
+ Root root = getRoot();
+ // Don't do anything if not attached to a root
+ if (root != null) {
// focus is restored to the parent window
- parent.focus();
-
- // subwindow is removed from parent
- parent.removeWindow(this);
+ root.focus();
+ // subwindow is removed from the root
+ root.removeWindow(this);
}
}
@@ -1160,7 +247,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @since 4.0.0
*/
public int getPositionX() {
- return positionX;
+ return getState().getPositionX();
}
/**
@@ -1188,8 +275,8 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @since 6.3.4
*/
private void setPositionX(int positionX, boolean repaintRequired) {
- this.positionX = positionX;
- centerRequested = false;
+ getState().setPositionX(positionX);
+ getState().setCentered(false);
if (repaintRequired) {
requestRepaint();
}
@@ -1205,7 +292,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @since 4.0.0
*/
public int getPositionY() {
- return positionY;
+ return getState().getPositionY();
}
/**
@@ -1235,8 +322,8 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @since 6.3.4
*/
private void setPositionY(int positionY, boolean repaintRequired) {
- this.positionY = positionY;
- centerRequested = false;
+ getState().setPositionY(positionY);
+ getState().setCentered(false);
if (repaintRequired) {
requestRepaint();
}
@@ -1413,136 +500,53 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
fireEvent(new ResizeEvent(this));
}
- private void attachWindow(Window w) {
- subwindows.add(w);
- w.setParent(this);
- requestRepaint();
- }
-
- /**
- * Adds a window inside another window.
- *
- * <p>
- * Adding windows inside another window creates "subwindows". These windows
- * should not be added to application directly and are not accessible
- * directly with any url. Addding windows implicitly sets their parents.
- * </p>
- *
- * <p>
- * Only one level of subwindows are supported. Thus you can add windows
- * inside such windows whose parent is <code>null</code>.
- * </p>
- *
- * @param window
- * @throws IllegalArgumentException
- * if a window is added inside non-application level window.
- * @throws NullPointerException
- * if the given <code>Window</code> is <code>null</code>.
- */
- public void addWindow(Window window) throws IllegalArgumentException,
- NullPointerException {
-
- if (window == null) {
- throw new NullPointerException("Argument must not be null");
- }
-
- if (window.getApplication() != null) {
- throw new IllegalArgumentException(
- "Window was already added to application"
- + " - it can not be added to another window also.");
- } else if (getParent() != null) {
- throw new IllegalArgumentException(
- "You can only add windows inside application-level windows.");
- } else if (window.subwindows.size() > 0) {
- throw new IllegalArgumentException(
- "Only one level of subwindows are supported.");
- }
-
- attachWindow(window);
- }
-
/**
- * Remove the given subwindow from this window.
- *
- * Since Vaadin 6.5, {@link CloseListener}s are called also when explicitly
- * removing a window by calling this method.
- *
- * Since Vaadin 6.5, returns a boolean indicating if the window was removed
- * or not.
- *
- * @param window
- * Window to be removed.
- * @return true if the subwindow was removed, false otherwise
- */
- public boolean removeWindow(Window window) {
- if (!subwindows.remove(window)) {
- // Window window is not a subwindow of this window.
- return false;
- }
- window.setParent(null);
- window.fireClose();
- requestRepaint();
-
- return true;
- }
-
- private Integer bringToFront = null;
-
- /*
- * This sequesnce is used to keep the right order of windows if multiple
- * windows are brought to front in a single changeset. Incremented and saved
- * by childwindows. If sequence is not used, the order is quite random
- * (depends on the order getting to dirty list. e.g. which window got
+ * Used to keep the right order of windows if multiple windows are brought
+ * to front in a single changeset. If this is not used, the order is quite
+ * random (depends on the order getting to dirty list. e.g. which window got
* variable changes).
*/
- private int bringToFrontSequence = 0;
+ private Integer bringToFront = null;
/**
- * If there are currently several sub windows visible, calling this method
- * makes this window topmost.
+ * If there are currently several windows visible, calling this method makes
+ * this window topmost.
* <p>
- * This method can only be called if this window is a sub window and
- * connected a top level window. Else an illegal state exception is thrown.
- * Also if there are modal windows and this window is not modal, and illegal
- * state exception is thrown.
+ * This method can only be called if this window connected a root. Else an
+ * illegal state exception is thrown. Also if there are modal windows and
+ * this window is not modal, and illegal state exception is thrown.
* <p>
- * <strong> Note, this API works on sub windows only. Browsers can't reorder
- * OS windows.</strong>
*/
public void bringToFront() {
- Window parent = getParent();
- if (parent == null) {
+ Root root = getRoot();
+ if (root == null) {
throw new IllegalStateException(
"Window must be attached to parent before calling bringToFront method.");
}
- for (Window w : parent.getChildWindows()) {
- if (w.isModal() && !isModal()) {
+ int maxBringToFront = -1;
+ for (Window w : root.getWindows()) {
+ if (!isModal() && w.isModal()) {
throw new IllegalStateException(
- "There are modal windows currently visible, non-modal window cannot be brought to front.");
+ "The root contains modal windows, non-modal window cannot be brought to front.");
+ }
+ if (w.bringToFront != null) {
+ maxBringToFront = Math.max(maxBringToFront,
+ w.bringToFront.intValue());
}
}
- bringToFront = getParent().bringToFrontSequence++;
+ bringToFront = Integer.valueOf(maxBringToFront + 1);
requestRepaint();
}
/**
- * Get the set of all child windows.
- *
- * @return Set of child windows.
- */
- public Set<Window> getChildWindows() {
- return Collections.unmodifiableSet(subwindows);
- }
-
- /**
* Sets sub-window modal, so that widgets behind it cannot be accessed.
* <b>Note:</b> affects sub-windows only.
*
- * @param modality
+ * @param modal
* true if modality is to be turned on
*/
- public void setModal(boolean modality) {
- modal = modality;
+ public void setModal(boolean modal) {
+ getState().setModal(modal);
center();
requestRepaint();
}
@@ -1551,7 +555,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @return true if this window is modal.
*/
public boolean isModal() {
- return modal;
+ return getState().isModal();
}
/**
@@ -1560,8 +564,8 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @param resizable
* true if resizability is to be turned on
*/
- public void setResizable(boolean resizeability) {
- resizable = resizeability;
+ public void setResizable(boolean resizable) {
+ getState().setResizable(resizable);
requestRepaint();
}
@@ -1570,7 +574,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* @return true if window is resizable by the end-user, otherwise false.
*/
public boolean isResizable() {
- return resizable;
+ return getState().isResizable();
}
/**
@@ -1579,7 +583,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* sizes are recalculated immediately.
*/
public boolean isResizeLazy() {
- return resizeLazy;
+ return getState().isResizeLazy();
}
/**
@@ -1595,7 +599,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* calculate immediately.
*/
public void setResizeLazy(boolean resizeLazy) {
- this.resizeLazy = resizeLazy;
+ getState().setResizeLazy(resizeLazy);
requestRepaint();
}
@@ -1604,534 +608,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* sub-windows only.
*/
public void center() {
- centerRequested = true;
- requestRepaint();
- }
-
- /**
- * Shows a notification message on the middle of the window. The message
- * automatically disappears ("humanized message").
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption is
- * rendered as html.
- *
- * @see #showNotification(com.vaadin.ui.Window.Notification)
- * @see Notification
- *
- * @param caption
- * The message
- */
- public void showNotification(String caption) {
- addNotification(new Notification(caption));
- }
-
- /**
- * Shows a notification message the window. The position and behavior of the
- * message depends on the type, which is one of the basic types defined in
- * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption is
- * rendered as html.
- *
- * @see #showNotification(com.vaadin.ui.Window.Notification)
- * @see Notification
- *
- * @param caption
- * The message
- * @param type
- * The message type
- */
- public void showNotification(String caption, int type) {
- addNotification(new Notification(caption, type));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description on the middle of the window. The message automatically
- * disappears ("humanized message").
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption and
- * description are rendered as html.
- *
- * @see #showNotification(com.vaadin.ui.Window.Notification)
- * @see Notification
- *
- * @param caption
- * The caption of the message
- * @param description
- * The message description
- *
- */
- public void showNotification(String caption, String description) {
- addNotification(new Notification(caption, description));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description. The position and behavior of the message depends on the
- * type, which is one of the basic types defined in {@link Notification},
- * for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption and
- * description are rendered as html.
- *
- * @see #showNotification(com.vaadin.ui.Window.Notification)
- * @see Notification
- *
- * @param caption
- * The caption of the message
- * @param description
- * The message description
- * @param type
- * The message type
- */
- public void showNotification(String caption, String description, int type) {
- addNotification(new Notification(caption, description, type));
- }
-
- /**
- * Shows a notification consisting of a bigger caption and a smaller
- * description. The position and behavior of the message depends on the
- * type, which is one of the basic types defined in {@link Notification},
- * for instance Notification.TYPE_WARNING_MESSAGE.
- *
- * Care should be taken to avoid XSS vulnerabilities if html content is
- * allowed.
- *
- * @see #showNotification(com.vaadin.ui.Window.Notification)
- * @see Notification
- *
- * @param caption
- * The message caption
- * @param description
- * The message description
- * @param type
- * The type of message
- * @param htmlContentAllowed
- * Whether html in the caption and description should be
- * displayed as html or as plain text
- */
- public void showNotification(String caption, String description, int type,
- boolean htmlContentAllowed) {
- addNotification(new Notification(caption, description, type,
- htmlContentAllowed));
- }
-
- /**
- * Shows a notification message.
- *
- * @see Notification
- * @see #showNotification(String)
- * @see #showNotification(String, int)
- * @see #showNotification(String, String)
- * @see #showNotification(String, String, int)
- *
- * @param notification
- * The notification message to show
- */
- public void showNotification(Notification notification) {
- addNotification(notification);
- }
-
- private void addNotification(Notification notification) {
- if (notifications == null) {
- notifications = new LinkedList<Notification>();
- }
- notifications.add(notification);
- requestRepaint();
- }
-
- /**
- * This method is used by Component.Focusable objects to request focus to
- * themselves. Focus renders must be handled at window level (instead of
- * Component.Focusable) due we want the last focused component to be focused
- * in client too. Not the one that is rendered last (the case we'd get if
- * implemented in Focusable only).
- *
- * To focus component from Vaadin application, use Focusable.focus(). See
- * {@link Focusable}.
- *
- * @param focusable
- * to be focused on next paint
- */
- void setFocusedComponent(Focusable focusable) {
- if (getParent() != null) {
- // focus is handled by main windows
- (getParent()).setFocusedComponent(focusable);
- } else {
- pendingFocus = focusable;
- requestRepaint();
- }
- }
-
- /**
- * A notification message, used to display temporary messages to the user -
- * for example "Document saved", or "Save failed".
- * <p>
- * The notification message can consist of several parts: caption,
- * description and icon. It is usually used with only caption - one should
- * be wary of filling the notification with too much information.
- * </p>
- * <p>
- * The notification message tries to be as unobtrusive as possible, while
- * still drawing needed attention. There are several basic types of messages
- * that can be used in different situations:
- * <ul>
- * <li>TYPE_HUMANIZED_MESSAGE fades away quickly as soon as the user uses
- * the mouse or types something. It can be used to show fairly unimportant
- * messages, such as feedback that an operation succeeded ("Document Saved")
- * - the kind of messages the user ignores once the application is familiar.
- * </li>
- * <li>TYPE_WARNING_MESSAGE is shown for a short while after the user uses
- * the mouse or types something. It's default style is also more noticeable
- * than the humanized message. It can be used for messages that do not
- * contain a lot of important information, but should be noticed by the
- * user. Despite the name, it does not have to be a warning, but can be used
- * instead of the humanized message whenever you want to make the message a
- * little more noticeable.</li>
- * <li>TYPE_ERROR_MESSAGE requires to user to click it before disappearing,
- * and can be used for critical messages.</li>
- * <li>TYPE_TRAY_NOTIFICATION is shown for a while in the lower left corner
- * of the window, and can be used for "convenience notifications" that do
- * not have to be noticed immediately, and should not interfere with the
- * current task - for instance to show "You have a new message in your
- * inbox" while the user is working in some other area of the application.</li>
- * </ul>
- * </p>
- * <p>
- * In addition to the basic pre-configured types, a Notification can also be
- * configured to show up in a custom position, for a specified time (or
- * until clicked), and with a custom stylename. An icon can also be added.
- * </p>
- *
- */
- public static class Notification implements Serializable {
- public static final int TYPE_HUMANIZED_MESSAGE = 1;
- public static final int TYPE_WARNING_MESSAGE = 2;
- public static final int TYPE_ERROR_MESSAGE = 3;
- public static final int TYPE_TRAY_NOTIFICATION = 4;
-
- public static final int POSITION_CENTERED = 1;
- public static final int POSITION_CENTERED_TOP = 2;
- public static final int POSITION_CENTERED_BOTTOM = 3;
- public static final int POSITION_TOP_LEFT = 4;
- public static final int POSITION_TOP_RIGHT = 5;
- public static final int POSITION_BOTTOM_LEFT = 6;
- public static final int POSITION_BOTTOM_RIGHT = 7;
-
- public static final int DELAY_FOREVER = -1;
- public static final int DELAY_NONE = 0;
-
- private String caption;
- private String description;
- private Resource icon;
- private int position = POSITION_CENTERED;
- private int delayMsec = 0;
- private String styleName;
- private boolean htmlContentAllowed;
-
- /**
- * Creates a "humanized" notification message.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption
- * is by default rendered as html.
- *
- * @param caption
- * The message to show
- */
- public Notification(String caption) {
- this(caption, null, TYPE_HUMANIZED_MESSAGE);
- }
-
- /**
- * Creates a notification message of the specified type.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption
- * is by default rendered as html.
- *
- * @param caption
- * The message to show
- * @param type
- * The type of message
- */
- public Notification(String caption, int type) {
- this(caption, null, type);
- }
-
- /**
- * Creates a "humanized" notification message with a bigger caption and
- * smaller description.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption
- * and description are by default rendered as html.
- *
- * @param caption
- * The message caption
- * @param description
- * The message description
- */
- public Notification(String caption, String description) {
- this(caption, description, TYPE_HUMANIZED_MESSAGE);
- }
-
- /**
- * Creates a notification message of the specified type, with a bigger
- * caption and smaller description.
- *
- * Care should be taken to to avoid XSS vulnerabilities as the caption
- * and description are by default rendered as html.
- *
- * @param caption
- * The message caption
- * @param description
- * The message description
- * @param type
- * The type of message
- */
- public Notification(String caption, String description, int type) {
- this(caption, description, type, true);
- }
-
- /**
- * Creates a notification message of the specified type, with a bigger
- * caption and smaller description.
- *
- * Care should be taken to to avoid XSS vulnerabilities if html is
- * allowed.
- *
- * @param caption
- * The message caption
- * @param description
- * The message description
- * @param type
- * The type of message
- * @param htmlContentAllowed
- * Whether html in the caption and description should be
- * displayed as html or as plain text
- */
- public Notification(String caption, String description, int type,
- boolean htmlContentAllowed) {
- this.caption = caption;
- this.description = description;
- this.htmlContentAllowed = htmlContentAllowed;
- setType(type);
- }
-
- private void setType(int type) {
- switch (type) {
- case TYPE_WARNING_MESSAGE:
- delayMsec = 1500;
- styleName = "warning";
- break;
- case TYPE_ERROR_MESSAGE:
- delayMsec = -1;
- styleName = "error";
- break;
- case TYPE_TRAY_NOTIFICATION:
- delayMsec = 3000;
- position = POSITION_BOTTOM_RIGHT;
- styleName = "tray";
-
- case TYPE_HUMANIZED_MESSAGE:
- default:
- break;
- }
-
- }
-
- /**
- * Gets the caption part of the notification message.
- *
- * @return The message caption
- */
- public String getCaption() {
- return caption;
- }
-
- /**
- * Sets the caption part of the notification message
- *
- * @param caption
- * The message caption
- */
- public void setCaption(String caption) {
- this.caption = caption;
- }
-
- /**
- * @deprecated Use {@link #getDescription()} instead.
- * @return
- */
- @Deprecated
- public String getMessage() {
- return description;
- }
-
- /**
- * @deprecated Use {@link #setDescription(String)} instead.
- * @param description
- */
- @Deprecated
- public void setMessage(String description) {
- this.description = description;
- }
-
- /**
- * Gets the description part of the notification message.
- *
- * @return The message description.
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Sets the description part of the notification message.
- *
- * @param description
- */
- public void setDescription(String description) {
- this.description = description;
- }
-
- /**
- * Gets the position of the notification message.
- *
- * @return The position
- */
- public int getPosition() {
- return position;
- }
-
- /**
- * Sets the position of the notification message.
- *
- * @param position
- * The desired notification position
- */
- public void setPosition(int position) {
- this.position = position;
- }
-
- /**
- * Gets the icon part of the notification message.
- *
- * @return The message icon
- */
- public Resource getIcon() {
- return icon;
- }
-
- /**
- * Sets the icon part of the notification message.
- *
- * @param icon
- * The desired message icon
- */
- public void setIcon(Resource icon) {
- this.icon = icon;
- }
-
- /**
- * Gets the delay before the notification disappears.
- *
- * @return the delay in msec, -1 indicates the message has to be
- * clicked.
- */
- public int getDelayMsec() {
- return delayMsec;
- }
-
- /**
- * Sets the delay before the notification disappears.
- *
- * @param delayMsec
- * the desired delay in msec, -1 to require the user to click
- * the message
- */
- public void setDelayMsec(int delayMsec) {
- this.delayMsec = delayMsec;
- }
-
- /**
- * Sets the style name for the notification message.
- *
- * @param styleName
- * The desired style name.
- */
- public void setStyleName(String styleName) {
- this.styleName = styleName;
- }
-
- /**
- * Gets the style name for the notification message.
- *
- * @return
- */
- public String getStyleName() {
- return styleName;
- }
-
- /**
- * Sets whether html is allowed in the caption and description. If set
- * to true, the texts are passed to the browser as html and the
- * developer is responsible for ensuring no harmful html is used. If set
- * to false, the texts are passed to the browser as plain text.
- *
- * @param htmlContentAllowed
- * true if the texts are used as html, false if used as plain
- * text
- */
- public void setHtmlContentAllowed(boolean htmlContentAllowed) {
- this.htmlContentAllowed = htmlContentAllowed;
- }
-
- /**
- * Checks whether caption and description are interpreted as html or
- * plain text.
- *
- * @return true if the texts are used as html, false if used as plain
- * text
- * @see #setHtmlContentAllowed(boolean)
- */
- public boolean isHtmlContentAllowed() {
- return htmlContentAllowed;
- }
- }
-
- /**
- * Executes JavaScript in this window.
- *
- * <p>
- * This method allows one to inject javascript from the server to client. A
- * client implementation is not required to implement this functionality,
- * but currently all web-based clients do implement this.
- * </p>
- *
- * <p>
- * Executing javascript this way often leads to cross-browser compatibility
- * issues and regressions that are hard to resolve. Use of this method
- * should be avoided and instead it is recommended to create new widgets
- * with GWT. For more info on creating own, reusable client-side widgets in
- * Java, read the corresponding chapter in Book of Vaadin.
- * </p>
- *
- * @param script
- * JavaScript snippet that will be executed.
- */
- public void executeJavaScript(String script) {
-
- if (getParent() != null) {
- throw new UnsupportedOperationException(
- "Only application level windows can execute javascript.");
- }
-
- if (jsExecQueue == null) {
- jsExecQueue = new ArrayList<String>();
- }
-
- jsExecQueue.add(script);
-
+ getState().setCentered(true);
requestRepaint();
}
@@ -2183,7 +660,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* true if the sub window can be dragged by the user
*/
public boolean isDraggable() {
- return draggable;
+ return getState().isDraggable();
}
/**
@@ -2196,7 +673,7 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
* true if the sub window can be dragged by the user
*/
public void setDraggable(boolean draggable) {
- this.draggable = draggable;
+ getState().setDraggable(draggable);
requestRepaint();
}
@@ -2344,40 +821,18 @@ public class Window extends Panel implements URIHandler, ParameterHandler,
*/
@Override
public void focus() {
- if (getParent() != null) {
- /*
- * When focusing a sub-window it basically means it should be
- * brought to the front. Instead of just moving the keyboard focus
- * we focus the window and bring it top-most.
- */
- bringToFront();
- } else {
- super.focus();
- }
- }
-
- /**
- * Notifies the child components and subwindows that the window is attached
- * to the application.
- */
- @Override
- public void attach() {
- super.attach();
- for (Window w : subwindows) {
- w.attach();
- }
+ /*
+ * When focusing a sub-window it basically means it should be brought to
+ * the front. Instead of just moving the keyboard focus we focus the
+ * window and bring it top-most.
+ */
+ super.focus();
+ bringToFront();
}
- /**
- * Notifies the child components and subwindows that the window is detached
- * from the application.
- */
@Override
- public void detach() {
- super.detach();
- for (Window w : subwindows) {
- w.detach();
- }
+ public WindowState getState() {
+ return (WindowState) super.getState();
}
}
diff --git a/src/com/vaadin/ui/themes/BaseTheme.java b/src/com/vaadin/ui/themes/BaseTheme.java
index c652a8a675..6f448746bf 100644
--- a/src/com/vaadin/ui/themes/BaseTheme.java
+++ b/src/com/vaadin/ui/themes/BaseTheme.java
@@ -50,4 +50,10 @@ public class BaseTheme {
*/
public static final String TREE_CONNECTORS = "connectors";
+ /**
+ * Clips the component so it will be constrained to its given size and not
+ * overflow.
+ */
+ public static final String CLIP = "v-clip";
+
} \ No newline at end of file