diff options
Diffstat (limited to 'client')
23 files changed, 710 insertions, 129 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java index 5c3ec36d78..9ba660626e 100644 --- a/client/src/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/com/vaadin/client/ApplicationConfiguration.java @@ -381,11 +381,14 @@ public class ApplicationConfiguration implements EntryPoint { @Override public void execute() { + Profiler.enter("ApplicationConfiguration.startApplication"); ApplicationConfiguration appConf = getConfigFromDOM(applicationId); ApplicationConnection a = GWT .create(ApplicationConnection.class); a.init(widgetSet, appConf); runningApplications.add(a); + Profiler.leave("ApplicationConfiguration.startApplication"); + a.start(); } }); @@ -532,6 +535,8 @@ public class ApplicationConfiguration implements EntryPoint { @Override public void onModuleLoad() { + Profiler.reset(); + Profiler.enter("ApplicationConfiguration.onModuleLoad"); BrowserInfo browserInfo = BrowserInfo.get(); @@ -567,6 +572,7 @@ public class ApplicationConfiguration implements EntryPoint { VConsole.getImplementation().error(e); } }); + Profiler.leave("ApplicationConfiguration.onModuleLoad"); if (SuperDevMode.enableBasedOnParameter()) { // Do not start any application as super dev mode will refresh the diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 44c7397655..de034a65a6 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -1311,8 +1311,6 @@ public class ApplicationConnection { return; } - Profiler.reset(); - VConsole.log("Handling message from server"); eventBus.fireEvent(new ResponseHandlingStartedEvent(this)); @@ -1397,15 +1395,12 @@ public class ApplicationConnection { Profiler.leave("Handling locales"); Profiler.enter("Handling meta information"); - 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; - uIConnector.getWidget().clear(); - getConnectorMap().clear(); + prepareRepaintAll(); if (meta.containsKey("invalidLayouts")) { validatingLayouts = true; } @@ -1528,10 +1523,46 @@ public class ApplicationConnection { endRequest(); if (Profiler.isEnabled()) { - Profiler.logTimings(); - Profiler.reset(); + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + Profiler.logTimings(); + Profiler.reset(); + } + }); + } + + } + + /** + * Properly clean up any old stuff to ensure everything is properly + * reinitialized. + */ + private void prepareRepaintAll() { + String uiConnectorId = uIConnector.getConnectorId(); + if (uiConnectorId == null) { + // Nothing to clear yet + return; } + // Create fake server response that says that the uiConnector + // has no children + JSONObject fakeHierarchy = new JSONObject(); + fakeHierarchy.put(uiConnectorId, new JSONArray()); + JSONObject fakeJson = new JSONObject(); + fakeJson.put("hierarchy", fakeHierarchy); + ValueMap fakeValueMap = fakeJson.getJavaScriptObject().cast(); + + // Update hierarchy based on the fake response + ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(fakeValueMap); + + // Send hierarchy events based on the fake update + sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events); + + // Unregister all the old connectors that have now been removed + unregisterRemovedConnectors(); + + getLayoutManager().cleanMeasuredSizes(); } private void updateCaptions( @@ -1548,8 +1579,10 @@ public class ApplicationConnection { // Find components with potentially changed caption state for (StateChangeEvent event : pendingStateChangeEvents) { - ServerConnector connector = event.getConnector(); - needsCaptionUpdate.add(connector); + if (VCaption.mightChange(event)) { + ServerConnector connector = event.getConnector(); + needsCaptionUpdate.add(connector); + } } // Update captions for all suitable candidates @@ -1916,6 +1949,7 @@ public class ApplicationConnection { } } + Profiler.enter("updateConnectorState newWithoutState"); // Fire events for properties using the default value for newly // created connectors even if there were no state changes for (ServerConnector connector : remainingNewConnectors) { @@ -1928,6 +1962,7 @@ public class ApplicationConnection { events.add(event); } + Profiler.leave("updateConnectorState newWithoutState"); Profiler.leave("updateConnectorState"); diff --git a/client/src/com/vaadin/client/ConnectorMap.java b/client/src/com/vaadin/client/ConnectorMap.java index 5f6053dd32..810f12824a 100644 --- a/client/src/com/vaadin/client/ConnectorMap.java +++ b/client/src/com/vaadin/client/ConnectorMap.java @@ -126,8 +126,9 @@ public class ConnectorMap { componentDetail.setConnector(connector); if (connector instanceof ComponentConnector) { ComponentConnector pw = (ComponentConnector) connector; + Widget widget = pw.getWidget(); Profiler.enter("ConnectorMap.setConnectorId"); - setConnectorId(pw.getWidget().getElement(), id); + setConnectorId(widget.getElement(), id); Profiler.leave("ConnectorMap.setConnectorId"); } Profiler.leave("ConnectorMap.registerConnector"); diff --git a/client/src/com/vaadin/client/FastStringMap.java b/client/src/com/vaadin/client/FastStringMap.java index cbb61427d2..5b15b1263a 100644 --- a/client/src/com/vaadin/client/FastStringMap.java +++ b/client/src/com/vaadin/client/FastStringMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Vaadin Ltd. + * Copyright 2000-2013 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of diff --git a/client/src/com/vaadin/client/JsArrayObject.java b/client/src/com/vaadin/client/JsArrayObject.java index 182b68ac3f..5b45650684 100644 --- a/client/src/com/vaadin/client/JsArrayObject.java +++ b/client/src/com/vaadin/client/JsArrayObject.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Vaadin Ltd. + * Copyright 2000-2013 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of diff --git a/client/src/com/vaadin/client/VCaption.java b/client/src/com/vaadin/client/VCaption.java index efa9be1850..47287636c4 100644 --- a/client/src/com/vaadin/client/VCaption.java +++ b/client/src/com/vaadin/client/VCaption.java @@ -20,6 +20,7 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.HTML; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.Icon; import com.vaadin.shared.AbstractComponentState; @@ -408,6 +409,29 @@ public class VCaption extends HTML { } /** + * Checks whether anything in a given state change might cause the caption + * to change. + * + * @param event + * the state change event to check + * @return <code>true</code> if the caption might have changed; otherwise + * <code>false</code> + */ + public static boolean mightChange(StateChangeEvent event) { + if (event.hasPropertyChanged("caption")) { + return true; + } + if (event.hasPropertyChanged("resources")) { + return true; + } + if (event.hasPropertyChanged("errorMessage")) { + return true; + } + + return false; + } + + /** * Returns Paintable for which this Caption belongs to. * * @return owner Widget diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 6e365b4017..759b90a8cd 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -366,8 +366,10 @@ public class VTooltip extends VOverlay { * Widget which DOM handlers are connected */ public void connectHandlersToWidget(Widget widget) { + Profiler.enter("VTooltip.connectHandlersToWidget"); widget.addDomHandler(tooltipEventHandler, MouseMoveEvent.getType()); widget.addDomHandler(tooltipEventHandler, ClickEvent.getType()); widget.addDomHandler(tooltipEventHandler, KeyDownEvent.getType()); + Profiler.leave("VTooltip.connectHandlersToWidget"); } } diff --git a/client/src/com/vaadin/client/metadata/AsyncBundleLoader.java b/client/src/com/vaadin/client/metadata/AsyncBundleLoader.java index e0ebb5e047..6be89c9cc9 100644 --- a/client/src/com/vaadin/client/metadata/AsyncBundleLoader.java +++ b/client/src/com/vaadin/client/metadata/AsyncBundleLoader.java @@ -19,6 +19,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.vaadin.client.Profiler; + public abstract class AsyncBundleLoader { public enum State { NOT_STARTED, LOADING, LOADED, ERROR; @@ -63,9 +65,11 @@ public abstract class AsyncBundleLoader { public void load(BundleLoadCallback callback, TypeDataStore store) { assert state == State.NOT_STARTED; + Profiler.enter("AsyncBundleLoader.load"); state = State.LOADING; addCallback(callback); load(store); + Profiler.leave("AsyncBundleLoader.load"); } public void addCallback(BundleLoadCallback callback) { diff --git a/client/src/com/vaadin/client/metadata/TypeDataStore.java b/client/src/com/vaadin/client/metadata/TypeDataStore.java index 5b466146a3..c1eca0a168 100644 --- a/client/src/com/vaadin/client/metadata/TypeDataStore.java +++ b/client/src/com/vaadin/client/metadata/TypeDataStore.java @@ -38,6 +38,7 @@ public class TypeDataStore { private final FastStringSet delayedMethods = FastStringSet.create(); private final FastStringSet lastOnlyMethods = FastStringSet.create(); + private final FastStringSet hasGetTooltipInfo = FastStringSet.create(); private final FastStringMap<Type> returnTypes = FastStringMap.create(); private final FastStringMap<Invoker> invokers = FastStringMap.create(); @@ -276,4 +277,22 @@ public class TypeDataStore { public static boolean hasProperties(Type type) { return get().properties.containsKey(type.getSignature()); } + + /** + * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards + * compatibility and will be removed in Vaadin 7.1 + */ + @Deprecated + public void setHasGetTooltipInfo(Class<?> clazz) { + hasGetTooltipInfo.add(getType(clazz).getSignature()); + } + + /** + * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards + * compatibility and will be removed in Vaadin 7.1 + */ + @Deprecated + public static boolean getHasGetTooltipInfo(Class clazz) { + return get().hasGetTooltipInfo.contains(getType(clazz).getSignature()); + } } diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index e35e3eafcf..694db6f02c 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -15,9 +15,6 @@ */ package com.vaadin.client.ui; -import java.util.ArrayList; -import java.util.List; - import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.Focusable; @@ -27,6 +24,7 @@ import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.HasComponentsConnector; import com.vaadin.client.LayoutManager; +import com.vaadin.client.Profiler; import com.vaadin.client.ServerConnector; import com.vaadin.client.StyleConstants; import com.vaadin.client.TooltipInfo; @@ -37,6 +35,7 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.metadata.NoDataException; import com.vaadin.client.metadata.Type; import com.vaadin.client.metadata.TypeData; +import com.vaadin.client.metadata.TypeDataStore; import com.vaadin.client.ui.datefield.PopupDateFieldConnector; import com.vaadin.client.ui.ui.UIConnector; import com.vaadin.shared.AbstractComponentState; @@ -55,6 +54,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector private boolean initialStateEvent = true; + private boolean tooltipListenersAttached = false; + /** * The style names from getState().getStyles() which are currently applied * to the widget. @@ -67,14 +68,6 @@ public abstract class AbstractComponentConnector extends AbstractConnector public AbstractComponentConnector() { } - @Override - protected void init() { - super.init(); - - getConnection().getVTooltip().connectHandlersToWidget(getWidget()); - - } - /** * Creates and returns the widget for this VPaintableWidget. This method * should only be called once when initializing the paintable. @@ -105,7 +98,11 @@ public abstract class AbstractComponentConnector extends AbstractConnector @Override public Widget getWidget() { if (widget == null) { + Profiler.enter("AbstractComponentConnector.createWidget for " + + Util.getSimpleName(this)); widget = createWidget(); + Profiler.leave("AbstractComponentConnector.createWidget for " + + Util.getSimpleName(this)); } return widget; @@ -123,6 +120,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { + Profiler.enter("AbstractComponentConnector.onStateChanged"); + Profiler.enter("AbstractComponentConnector.onStateChanged update id"); if (stateChangeEvent.hasPropertyChanged("id")) { if (getState().id != null) { getWidget().getElement().setId(getState().id); @@ -130,17 +129,20 @@ public abstract class AbstractComponentConnector extends AbstractConnector getWidget().getElement().removeAttribute("id"); } } + Profiler.leave("AbstractComponentConnector.onStateChanged update id"); /* * Disabled state may affect (override) tabindex so the order must be * first setting tabindex, then enabled state (through super * implementation). */ + Profiler.enter("AbstractComponentConnector.onStateChanged update tab index"); if (getState() instanceof TabIndexState && getWidget() instanceof Focusable) { ((Focusable) getWidget()) .setTabIndex(((TabIndexState) getState()).tabIndex); } + Profiler.leave("AbstractComponentConnector.onStateChanged update tab index"); super.onStateChanged(stateChangeEvent); @@ -154,7 +156,20 @@ public abstract class AbstractComponentConnector extends AbstractConnector updateComponentSize(); + Profiler.enter("AbstractComponentContainer.onStateChanged check tooltip"); + if (!tooltipListenersAttached && hasTooltip()) { + /* + * Add event handlers for tooltips if they are needed but have not + * yet been added. + */ + tooltipListenersAttached = true; + getConnection().getVTooltip().connectHandlersToWidget(getWidget()); + } + Profiler.leave("AbstractComponentContainer.onStateChanged check tooltip"); + initialStateEvent = false; + + Profiler.leave("AbstractComponentConnector.onStateChanged"); } @Override @@ -182,6 +197,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector } private void updateComponentSize() { + Profiler.enter("AbstractComponentConnector.updateComponentSize"); + String newWidth = getState().width == null ? "" : getState().width; String newHeight = getState().height == null ? "" : getState().height; @@ -209,11 +226,17 @@ public abstract class AbstractComponentConnector extends AbstractConnector // Set defined sizes Widget widget = getWidget(); + Profiler.enter("AbstractComponentConnector.updateComponentSize update styleNames"); widget.setStyleName("v-has-width", !isUndefinedWidth()); widget.setStyleName("v-has-height", !isUndefinedHeight()); + Profiler.leave("AbstractComponentConnector.updateComponentSize update styleNames"); + Profiler.enter("AbstractComponentConnector.updateComponentSize update DOM"); widget.setHeight(newHeight); widget.setWidth(newWidth); + Profiler.leave("AbstractComponentConnector.updateComponentSize update DOM"); + + Profiler.leave("AbstractComponentConnector.updateComponentSize"); } @Override @@ -257,10 +280,12 @@ public abstract class AbstractComponentConnector extends AbstractConnector * </p> */ protected void updateWidgetStyleNames() { + Profiler.enter("AbstractComponentConnector.updateWidgetStyleNames"); AbstractComponentState state = getState(); String primaryStyleName = getWidget().getStylePrimaryName(); - if (state.primaryStyleName != null) { + if (state.primaryStyleName != null + && !state.primaryStyleName.equals(primaryStyleName)) { /* * We overwrite the widgets primary stylename if state defines a * primary stylename. @@ -302,7 +327,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector } } - + Profiler.leave("AbstractComponentConnector.updateWidgetStyleNames"); } /** @@ -394,11 +419,16 @@ public abstract class AbstractComponentConnector extends AbstractConnector } } - /* - * (non-Javadoc) + /** + * {@inheritDoc} + * + * <p> + * When overriding this method, {@link #hasTooltip()} should also be + * overridden to return true in all situations where this method might + * return a non-empty result. + * </p> * - * @see com.vaadin.client.ComponentConnector#getTooltipInfo(com. - * google.gwt.dom.client.Element) + * @see ComponentConnector#getTooltipInfo(Element) */ @Override public TooltipInfo getTooltipInfo(Element element) { @@ -406,6 +436,35 @@ public abstract class AbstractComponentConnector extends AbstractConnector } /** + * Check whether there might be a tooltip for this component. The framework + * will only add event listeners for automatically handling tooltips (using + * {@link #getTooltipInfo(Element)}) if this method returns true. + * + * @return <code>true</code> if some part of the component might have a + * tooltip, otherwise <code>false</code> + */ + private boolean hasTooltip() { + /* + * Hack to avoid breaking backwards compatibility - use a generator to + * know whether there's a custom implementation of getTooltipInfo, and + * in that case always assume that there might be tooltip. + */ + if (TypeDataStore.getHasGetTooltipInfo(getClass())) { + return true; + } + + // Normally, there is a tooltip if description or errorMessage is set + AbstractComponentState state = getState(); + if (state.description != null && !state.description.equals("")) { + return true; + } else if (state.errorMessage != null && !state.errorMessage.equals("")) { + return true; + } else { + return false; + } + } + + /** * Gets the icon set for this component. * * @return the URL of the icon, or <code>null</code> if no icon has been diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java index 32f5370dc3..2c76aa93fe 100644 --- a/client/src/com/vaadin/client/ui/AbstractConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractConnector.java @@ -218,6 +218,7 @@ public abstract class AbstractConnector implements ServerConnector, } if (statePropertyHandlerManagers != null && event instanceof StateChangeEvent) { + Profiler.enter("AbstractConnector.fireEvent statePropertyHandlerManagers"); StateChangeEvent stateChangeEvent = (StateChangeEvent) event; JsArrayString keys = statePropertyHandlerManagers.getKeys(); for (int i = 0; i < keys.length(); i++) { @@ -226,6 +227,7 @@ public abstract class AbstractConnector implements ServerConnector, statePropertyHandlerManagers.get(property).fireEvent(event); } } + Profiler.leave("AbstractConnector.fireEvent statePropertyHandlerManagers"); } if (Profiler.isEnabled()) { Profiler.leave(profilerKey); @@ -400,6 +402,7 @@ public abstract class AbstractConnector implements ServerConnector, if (lastEnabledState == enabledState) { return; } + Profiler.enter("AbstractConnector.updateEnabledState"); lastEnabledState = enabledState; for (ServerConnector c : getChildren()) { @@ -407,6 +410,7 @@ public abstract class AbstractConnector implements ServerConnector, // their parent c.updateEnabledState(c.isEnabled()); } + Profiler.leave("AbstractConnector.updateEnabledState"); } /** diff --git a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java index 92a51f209d..88fbae6e88 100644 --- a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java +++ b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java @@ -326,6 +326,10 @@ public class VAbsoluteLayout extends ComplexPanel { Style wrapperStyle = wrapper.getElement().getStyle(); Style widgetStyle = wrapper.getWidget().getElement().getStyle(); + + // Ensure previous heights do not affect the measures + wrapperStyle.clearHeight(); + if (widgetStyle.getHeight() != null && widgetStyle.getHeight().endsWith("%")) { int h; @@ -343,8 +347,6 @@ public class VAbsoluteLayout extends ComplexPanel { - wrapper.getElement().getOffsetTop(); } wrapperStyle.setHeight(h, Unit.PX); - } else { - wrapperStyle.clearHeight(); } wrapper.updateCaptionPosition(); @@ -380,6 +382,9 @@ public class VAbsoluteLayout extends ComplexPanel { Style wrapperStyle = wrapper.getElement().getStyle(); Style widgetStyle = wrapper.getWidget().getElement().getStyle(); + // Ensure previous heights do not affect the measures + wrapperStyle.clearWidth(); + if (widgetStyle.getWidth() != null && widgetStyle.getWidth().endsWith("%")) { int w; @@ -398,8 +403,6 @@ public class VAbsoluteLayout extends ComplexPanel { - wrapper.getElement().getOffsetLeft(); } wrapperStyle.setWidth(w, Unit.PX); - } else { - wrapperStyle.clearWidth(); } wrapper.updateCaptionPosition(); diff --git a/client/src/com/vaadin/client/ui/VCssLayout.java b/client/src/com/vaadin/client/ui/VCssLayout.java index 0936859ace..4357116707 100644 --- a/client/src/com/vaadin/client/ui/VCssLayout.java +++ b/client/src/com/vaadin/client/ui/VCssLayout.java @@ -18,6 +18,7 @@ package com.vaadin.client.ui; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.Profiler; import com.vaadin.client.StyleConstants; /** @@ -41,12 +42,15 @@ public class VCssLayout extends FlowPanel { * For internal use only. May be removed or replaced in the future. */ public void addOrMove(Widget child, int index) { + Profiler.enter("VCssLayout.addOrMove"); if (child.getParent() == this) { int currentIndex = getWidgetIndex(child); if (index == currentIndex) { + Profiler.leave("VCssLayout.addOrMove"); return; } } insert(child, index); + Profiler.leave("VCssLayout.addOrMove"); } } diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 75d67b82d6..c76dd38d8f 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1191,6 +1191,39 @@ public class VScrollTable extends FlowPanel implements HasWidgets, return totalRows; } + /** + * Returns the extra space that is given to the header column when column + * width is determined by header text. + * + * @return extra space in pixels + */ + private int getHeaderPadding() { + return scrollBody.getCellExtraWidth(); + } + + /** + * This method exists for the needs of {@link VTreeTable} only. Not part of + * the official API, <b>extend at your own risk</b>. May be removed or + * replaced in the future. + * + * @return index of TreeTable's hierarchy column, or -1 if not applicable + */ + protected int getHierarchyColumnIndex() { + return -1; + } + + /** + * For internal use only. May be removed or replaced in the future. + */ + public void updateMaxIndent() { + int oldIndent = scrollBody.getMaxIndent(); + scrollBody.calculateMaxIndent(); + if (oldIndent != scrollBody.getMaxIndent()) { + // indent updated, headers might need adjusting + triggerLazyColumnAdjustment(true); + } + } + /** For internal use only. May be removed or replaced in the future. */ public void focusRowFromBody() { if (selectedRowKeys.size() == 1) { @@ -1382,6 +1415,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * amount of rows in data set */ public void updateBody(UIDL uidl, int firstRow, int reqRows) { + int oldIndent = scrollBody.getMaxIndent(); if (uidl == null || reqRows < 1) { // container is empty, remove possibly existing rows if (firstRow <= 0) { @@ -1399,6 +1433,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, scrollBody.renderRows(uidl, firstRow, reqRows); discardRowsOutsideCacheWindow(); + scrollBody.calculateMaxIndent(); + if (oldIndent != scrollBody.getMaxIndent()) { + // indent updated, headers might need adjusting + headerChangedDuringUpdate = true; + } } /** For internal use only. May be removed or replaced in the future. */ @@ -1591,31 +1630,55 @@ public class VScrollTable extends FlowPanel implements HasWidgets, return tHead.getHeaderCell(index).getColKey(); } - private void setColWidth(int colIndex, int w, boolean isDefinedWidth) { + /** + * Note: not part of the official API, extend at your own risk. May be + * removed or replaced in the future. + * + * Sets the indicated column's width for headers and scrollBody alike. + * + * @param colIndex + * index of the modified column + * @param w + * new width (may be subject to modifications if doesn't meet + * minimum requirements) + * @param isDefinedWidth + * disables expand ratio if set true + */ + protected void setColWidth(int colIndex, int w, boolean isDefinedWidth) { final HeaderCell hcell = tHead.getHeaderCell(colIndex); // Make sure that the column grows to accommodate the sort indicator if // necessary. - if (w < hcell.getMinWidth()) { - w = hcell.getMinWidth(); + // get min width with no indent or padding + int minWidth = hcell.getMinWidth(false, false); + if (w < minWidth) { + w = minWidth; } - // Set header column width + // Set header column width WITHOUT INDENT hcell.setWidth(w, isDefinedWidth); + // Set footer column width likewise + FooterCell fcell = tFoot.getFooterCell(colIndex); + fcell.setWidth(w, isDefinedWidth); + // Ensure indicators have been taken into account tHead.resizeCaptionContainer(hcell); + // Make sure that the body column grows to accommodate the indent if + // necessary. + // get min width with indent, no padding + minWidth = hcell.getMinWidth(true, false); + if (w < minWidth) { + w = minWidth; + } + // Set body column width scrollBody.setColWidth(colIndex, w); - - // Set footer column width - FooterCell fcell = tFoot.getFooterCell(colIndex); - fcell.setWidth(w, isDefinedWidth); } private int getColWidth(String colKey) { - return tHead.getHeaderCell(colKey).getWidth(); + return tHead.getHeaderCell(colKey).getWidthWithIndent(); } /** @@ -1813,22 +1876,37 @@ public class VScrollTable extends FlowPanel implements HasWidgets, tHead.enableBrowserIntelligence(); tFoot.enableBrowserIntelligence(); + int hierarchyColumnIndent = scrollBody != null ? scrollBody + .getMaxIndent() : 0; + HeaderCell hierarchyHeaderWithExpandRatio = null; + // first loop: collect natural widths while (headCells.hasNext()) { final HeaderCell hCell = (HeaderCell) headCells.next(); final FooterCell fCell = (FooterCell) footCells.next(); + boolean needsIndent = hierarchyColumnIndent > 0 + && hCell.isHierarchyColumn(); int w = hCell.getWidth(); if (hCell.isDefinedWidth()) { // server has defined column width explicitly + if (needsIndent && w < hierarchyColumnIndent) { + // hierarchy indent overrides explicitly set width + w = hierarchyColumnIndent; + } totalExplicitColumnsWidths += w; } else { if (hCell.getExpandRatio() > 0) { expandRatioDivider += hCell.getExpandRatio(); w = 0; + if (needsIndent && w < hierarchyColumnIndent) { + hierarchyHeaderWithExpandRatio = hCell; + // don't add to widths here, because will be included in + // the expand ratio space if there's enough of it + } } else { // get and store greater of header width and column width, - // and - // store it as a minimumn natural col width + // and store it as a minimum natural column width (these + // already contain the indent if any) int headerWidth = hCell.getNaturalColumnWidth(i); int footerWidth = fCell.getNaturalColumnWidth(i); w = headerWidth > footerWidth ? headerWidth : footerWidth; @@ -1840,6 +1918,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets, total += w; i++; } + if (hierarchyHeaderWithExpandRatio != null) { + total += hierarchyColumnIndent; + } tHead.disableBrowserIntelligence(); tFoot.disableBrowserIntelligence(); @@ -1871,13 +1952,24 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (availW > total) { // natural size is smaller than available space - final int extraSpace = availW - total; + int extraSpace = availW - total; + if (hierarchyHeaderWithExpandRatio != null) { + /* + * add the indent's space back to ensure each column gets an + * even share according to the expand ratios (note: if the + * allocated space isn't enough for the hierarchy column it + * shall be treated like a defined width column and the indent + * space gets removed from the extra space again) + */ + extraSpace += hierarchyColumnIndent; + } final int totalWidthR = total - totalExplicitColumnsWidths; int checksum = 0; if (extraSpace == 1) { // We cannot divide one single pixel so we give it the first // undefined column + // no need to worry about indent here headCells = tHead.iterator(); i = 0; checksum = availW; @@ -1891,6 +1983,22 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } else if (expandRatioDivider > 0) { + boolean setIndentToHierarchyHeader = false; + if (hierarchyHeaderWithExpandRatio != null) { + // ensure first that the hierarchyColumn gets at least the + // space allocated for indent + final int newSpace = Math + .round((extraSpace * (hierarchyHeaderWithExpandRatio + .getExpandRatio() / expandRatioDivider))); + if (newSpace < hierarchyColumnIndent) { + // not enough space for indent, remove indent from the + // extraSpace again and handle hierarchy column's header + // separately + setIndentToHierarchyHeader = true; + extraSpace -= hierarchyColumnIndent; + } + } + // visible columns have some active expand ratios, excess // space is divided according to them headCells = tHead.iterator(); @@ -1899,9 +2007,17 @@ public class VScrollTable extends FlowPanel implements HasWidgets, HeaderCell hCell = (HeaderCell) headCells.next(); if (hCell.getExpandRatio() > 0) { int w = widths[i]; - final int newSpace = Math.round((extraSpace * (hCell - .getExpandRatio() / expandRatioDivider))); - w += newSpace; + if (setIndentToHierarchyHeader + && hierarchyHeaderWithExpandRatio.equals(hCell)) { + // hierarchy column's header is no longer part of + // the expansion divide and only gets indent + w += hierarchyColumnIndent; + } else { + final int newSpace = Math + .round((extraSpace * (hCell + .getExpandRatio() / expandRatioDivider))); + w += newSpace; + } widths[i] = w; } checksum += widths[i]; @@ -1911,6 +2027,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // no expand ratios defined, we will share extra space // relatively to "natural widths" among those without // explicit width + // no need to worry about indent here, it's already included headCells = tHead.iterator(); i = 0; while (headCells.hasNext()) { @@ -1946,7 +2063,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } else { - // bodys size will be more than available and scrollbar will appear + // body's size will be more than available and scrollbar will appear } // last loop: set possibly modified values or reset if new tBody @@ -2046,8 +2163,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } /** - * Note, this method is not official api although declared as protected. - * Extend at you own risk. + * Note: this method is not part of official API although declared as + * protected. Extend at your own risk. * * @return true if content area will have scrollbars visible. */ @@ -2430,6 +2547,16 @@ public class VScrollTable extends FlowPanel implements HasWidgets, expandRatio = 0; } + /** + * Sets width to the header cell. This width should not include any + * possible indent modifications that are present in + * {@link VScrollTableBody#getMaxIndent()}. + * + * @param w + * required width of the cell sans indentations + * @param ensureDefinedWidth + * disables expand ratio if required + */ public void setWidth(int w, boolean ensureDefinedWidth) { if (ensureDefinedWidth) { definedWidth = true; @@ -2453,15 +2580,23 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * unless TD width is not explicitly set. */ if (scrollBody != null) { - int tdWidth = width + scrollBody.getCellExtraWidth(); + int maxIndent = scrollBody.getMaxIndent(); + if (w < maxIndent && isHierarchyColumn()) { + w = maxIndent; + } + int tdWidth = w + scrollBody.getCellExtraWidth(); setWidth(tdWidth + "px"); } else { Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { - int tdWidth = width - + scrollBody.getCellExtraWidth(); + int maxIndent = scrollBody.getMaxIndent(); + int tdWidth = width; + if (tdWidth < maxIndent && isHierarchyColumn()) { + tdWidth = maxIndent; + } + tdWidth += scrollBody.getCellExtraWidth(); setWidth(tdWidth + "px"); } }); @@ -2484,10 +2619,45 @@ public class VScrollTable extends FlowPanel implements HasWidgets, return definedWidth && width >= 0; } + /** + * This method exists for the needs of {@link VTreeTable} only. + * + * Returns the pixels width of the header cell. This includes the + * indent, if applicable. + * + * @return The width in pixels + */ + protected int getWidthWithIndent() { + if (scrollBody != null && isHierarchyColumn()) { + int maxIndent = scrollBody.getMaxIndent(); + if (maxIndent > width) { + return maxIndent; + } + } + return width; + } + + /** + * Returns the pixels width of the header cell. + * + * @return The width in pixels + */ public int getWidth() { return width; } + /** + * This method exists for the needs of {@link VTreeTable} only. + * + * @return <code>true</code> if this is hierarcyColumn's header cell, + * <code>false</code> otherwise + */ + private boolean isHierarchyColumn() { + int hierarchyColumnIndex = getHierarchyColumnIndex(); + return hierarchyColumnIndex >= 0 + && tHead.visibleCells.indexOf(this) == hierarchyColumnIndex; + } + public void setText(String headerText) { DOM.setInnerHTML(captionContainer, headerText); } @@ -2742,7 +2912,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, DOM.setCapture(getElement()); dragStartX = DOM.eventGetClientX(event); colIndex = getColIndexByKey(cid); - originalWidth = getWidth(); + originalWidth = getWidthWithIndent(); DOM.eventPreventDefault(event); break; case Event.ONMOUSEUP: @@ -2774,8 +2944,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, tHead.disableAutoColumnWidthCalculation(this); int newWidth = originalWidth + deltaX; - if (newWidth < getMinWidth()) { - newWidth = getMinWidth(); + // get min width with indent, no padding + int minWidth = getMinWidth(true, false); + if (newWidth < minWidth) { + // already includes indent if any + newWidth = minWidth; } setColWidth(colIndex, newWidth, true); triggerLazyColumnAdjustment(false); @@ -2787,12 +2960,37 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } - public int getMinWidth() { - int cellExtraWidth = 0; + /** + * Returns the smallest possible cell width in pixels. + * + * @param includeIndent + * - width should include hierarchy column indent if + * applicable (VTreeTable only) + * @param includeCellExtraWidth + * - width should include paddings etc. + * @return + */ + private int getMinWidth(boolean includeIndent, + boolean includeCellExtraWidth) { + int minWidth = sortIndicator.getOffsetWidth(); if (scrollBody != null) { - cellExtraWidth += scrollBody.getCellExtraWidth(); + // check the need for indent before adding paddings etc. + if (includeIndent && isHierarchyColumn()) { + int maxIndent = scrollBody.getMaxIndent(); + if (minWidth < maxIndent) { + minWidth = maxIndent; + } + } + if (includeCellExtraWidth) { + minWidth += scrollBody.getCellExtraWidth(); + } } - return cellExtraWidth + sortIndicator.getOffsetWidth(); + return minWidth; + } + + public int getMinWidth() { + // get min width with padding, no indent + return getMinWidth(false, true); } public String getCaption() { @@ -2823,16 +3021,20 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * @return */ public int getNaturalColumnWidth(int columnIndex) { + final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody + .getMaxIndent() : 0; if (isDefinedWidth()) { + if (iw > width) { + return iw; + } return width; } else { if (naturalWidth < 0) { // This is recently revealed column. Try to detect a proper - // value (greater of header and data - // cols) + // value (greater of header and data columns) int hw = captionContainer.getOffsetWidth() - + scrollBody.getCellExtraWidth(); + + getHeaderPadding(); if (BrowserInfo.get().isGecko()) { hw += sortIndicator.getOffsetWidth(); } @@ -2848,7 +3050,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, final int cw = scrollBody.getColWidth(columnIndex); naturalWidth = (hw > cw ? hw : cw); } - return naturalWidth; + if (iw > naturalWidth) { + // indent is temporary value, naturalWidth shouldn't be + // updated + return iw; + } else { + return naturalWidth; + } } } @@ -2945,32 +3153,49 @@ public class VScrollTable extends FlowPanel implements HasWidgets, public void resizeCaptionContainer(HeaderCell cell) { HeaderCell lastcell = getHeaderCell(visibleCells.size() - 1); + int columnSelectorOffset = columnSelector.getOffsetWidth(); - // Measure column widths - int columnTotalWidth = 0; - for (Widget w : visibleCells) { - columnTotalWidth += w.getOffsetWidth(); - } - - if (cell == lastcell - && columnSelector.getOffsetWidth() > 0 - && columnTotalWidth >= div.getOffsetWidth() - - columnSelector.getOffsetWidth() + if (cell == lastcell && columnSelectorOffset > 0 && !hasVerticalScrollbar()) { - // Ensure column caption is visible when placed under the column - // selector widget by shifting and resizing the caption. - int offset = 0; - int diff = div.getOffsetWidth() - columnTotalWidth; - if (diff < columnSelector.getOffsetWidth() && diff > 0) { - // If the difference is less than the column selectors width - // then just offset by the - // difference - offset = columnSelector.getOffsetWidth() - diff; + + // Measure column widths + int columnTotalWidth = 0; + for (Widget w : visibleCells) { + int cellExtraWidth = w.getOffsetWidth(); + if (scrollBody != null + && visibleCells.indexOf(w) == getHierarchyColumnIndex() + && cellExtraWidth < scrollBody.getMaxIndent()) { + // indent must be taken into consideration even if it + // hasn't been applied yet + columnTotalWidth += scrollBody.getMaxIndent(); + } else { + columnTotalWidth += cellExtraWidth; + } + } + + int divOffset = div.getOffsetWidth(); + if (columnTotalWidth >= divOffset - columnSelectorOffset) { + /* + * Ensure column caption is visible when placed under the + * column selector widget by shifting and resizing the + * caption. + */ + int offset = 0; + int diff = divOffset - columnTotalWidth; + if (diff < columnSelectorOffset && diff > 0) { + /* + * If the difference is less than the column selectors + * width then just offset by the difference + */ + offset = columnSelectorOffset - diff; + } else { + // Else offset by the whole column selector + offset = columnSelectorOffset; + } + lastcell.resizeCaptionContainer(offset); } else { - // Else offset by the whole column selector - offset = columnSelector.getOffsetWidth(); + cell.resizeCaptionContainer(0); } - lastcell.resizeCaptionContainer(offset); } else { cell.resizeCaptionContainer(0); } @@ -3033,10 +3258,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // Make sure to accomodate for the sort indicator if // necessary. int width = Integer.parseInt(widthStr); - if (width < c.getMinWidth()) { - width = c.getMinWidth(); + int widthWithoutAddedIndent = width; + + // get min width with indent, no padding + int minWidth = c.getMinWidth(true, false); + if (width < minWidth) { + width = minWidth; } - if (width != c.getWidth() && scrollBody != null) { + if (scrollBody != null && width != c.getWidthWithIndent()) { // Do a more thorough update if a column is resized from // the server *after* the header has been properly // initialized @@ -3052,7 +3281,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, }); refreshContentWidths = true; } else { - c.setWidth(width, true); + // get min width with no indent or padding + minWidth = c.getMinWidth(false, false); + if (widthWithoutAddedIndent < minWidth) { + widthWithoutAddedIndent = minWidth; + } + // save min width without indent + c.setWidth(widthWithoutAddedIndent, true); } } else if (recalcWidths) { c.setUndefinedWidth(); @@ -3507,12 +3742,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } /** - * Sets the width of the cell + * Sets the width of the cell. This width should not include any + * possible indent modifications that are present in + * {@link VScrollTableBody#getMaxIndent()}. * * @param w * The width of the cell * @param ensureDefinedWidth - * Ensures the the given width is not recalculated + * Ensures that the given width is not recalculated */ public void setWidth(int w, boolean ensureDefinedWidth) { @@ -3549,7 +3786,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * unless TD width is not explicitly set. */ if (scrollBody != null) { - int tdWidth = width + scrollBody.getCellExtraWidth() + int maxIndent = scrollBody.getMaxIndent(); + if (w < maxIndent + && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) { + // ensure there's room for the indent + w = maxIndent; + } + int tdWidth = w + scrollBody.getCellExtraWidth() - borderWidths; setWidth(Math.max(tdWidth, 0) + "px"); } else { @@ -3557,8 +3800,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, @Override public void execute() { - int tdWidth = width - + scrollBody.getCellExtraWidth() + int tdWidth = width; + int maxIndent = scrollBody.getMaxIndent(); + if (tdWidth < maxIndent + && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) { + // ensure there's room for the indent + tdWidth = maxIndent; + } + tdWidth += scrollBody.getCellExtraWidth() - borderWidths; setWidth(Math.max(tdWidth, 0) + "px"); } @@ -3571,6 +3820,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * Sets the width to undefined */ public void setUndefinedWidth() { + definedWidth = false; setWidth(-1, false); } @@ -3585,7 +3835,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } /** - * Returns the pixels width of the footer cell + * Returns the pixels width of the footer cell. * * @return The width in pixels */ @@ -3699,7 +3949,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * @return */ public int getNaturalColumnWidth(int columnIndex) { + final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody + .getMaxIndent() : 0; if (isDefinedWidth()) { + if (iw > width) { + return iw; + } return width; } else { if (naturalWidth < 0) { @@ -3708,7 +3963,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // cols) final int hw = ((Element) getElement().getLastChild()) - .getOffsetWidth() + scrollBody.getCellExtraWidth(); + .getOffsetWidth() + getHeaderPadding(); if (columnIndex < 0) { columnIndex = 0; for (Iterator<Widget> it = tHead.iterator(); it @@ -3721,7 +3976,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, final int cw = scrollBody.getColWidth(columnIndex); naturalWidth = (hw > cw ? hw : cw); } - return naturalWidth; + if (iw > naturalWidth) { + return iw; + } else { + return naturalWidth; + } } } @@ -4627,6 +4886,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets, return cellExtraWidth; } + /** + * This method exists for the needs of {@link VTreeTable} only. May be + * removed or replaced in the future.</br> </br> Returns the maximum + * indent of the hierarcyColumn, if applicable. + * + * @see {@link VScrollTable#getHierarchyColumnIndex()} + * + * @return maximum indent in pixels + */ + protected int getMaxIndent() { + return 0; + } + + /** + * This method exists for the needs of {@link VTreeTable} only. May be + * removed or replaced in the future.</br> </br> Calculates the maximum + * indent of the hierarcyColumn, if applicable. + */ + protected void calculateMaxIndent() { + // NOP + } + private void detectExtrawidth() { NodeList<TableRowElement> rows = tBodyElement.getRows(); if (rows.getLength() == 0) { @@ -4806,8 +5087,23 @@ public class VScrollTable extends FlowPanel implements HasWidgets, protected void setCellWidth(int cellIx, int width) { final Element cell = DOM.getChild(getElement(), cellIx); - cell.getFirstChildElement().getStyle() - .setPropertyPx("width", width); + Style wrapperStyle = cell.getFirstChildElement().getStyle(); + int wrapperWidth = width; + if (BrowserInfo.get().isWebkit() + || BrowserInfo.get().isOpera10()) { + /* + * Some versions of Webkit and Opera ignore the width + * definition of zero width table cells. Instead, use 1px + * and compensate with a negative margin. + */ + if (width == 0) { + wrapperWidth = 1; + wrapperStyle.setMarginRight(-1, Unit.PX); + } else { + wrapperStyle.clearMarginRight(); + } + } + wrapperStyle.setPropertyPx("width", wrapperWidth); cell.getStyle().setPropertyPx("width", width); } @@ -5866,7 +6162,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, @Override public void execute() { if (showRowHeaders) { - setCellWidth(0, tHead.getHeaderCell(0).getWidth()); + setCellWidth(0, tHead.getHeaderCell(0) + .getWidthWithIndent()); calcAndSetSpanWidthOnCell(1); } else { calcAndSetSpanWidthOnCell(0); @@ -6100,14 +6397,35 @@ public class VScrollTable extends FlowPanel implements HasWidgets, int totalExplicitColumnsWidths = 0; float expandRatioDivider = 0; int colIndex = 0; + + int hierarchyColumnIndent = scrollBody.getMaxIndent(); + int hierarchyColumnIndex = getHierarchyColumnIndex(); + HeaderCell hierarchyHeaderInNeedOfFurtherHandling = null; + while (headCells.hasNext()) { final HeaderCell hCell = (HeaderCell) headCells.next(); + boolean hasIndent = hierarchyColumnIndent > 0 + && hCell.isHierarchyColumn(); if (hCell.isDefinedWidth()) { - totalExplicitColumnsWidths += hCell.getWidth(); - usedMinimumWidth += hCell.getWidth(); + // get width without indent to find out whether adjustments + // are needed (requires special handling further ahead) + int w = hCell.getWidth(); + if (hasIndent && w < hierarchyColumnIndent) { + // enforce indent if necessary + w = hierarchyColumnIndent; + hierarchyHeaderInNeedOfFurtherHandling = hCell; + } + totalExplicitColumnsWidths += w; + usedMinimumWidth += w; } else { - usedMinimumWidth += hCell.getNaturalColumnWidth(colIndex); + // natural width already includes indent if any + int naturalColumnWidth = hCell + .getNaturalColumnWidth(colIndex); + usedMinimumWidth += naturalColumnWidth; expandRatioDivider += hCell.getExpandRatio(); + if (hasIndent) { + hierarchyHeaderInNeedOfFurtherHandling = hCell; + } } colIndex++; } @@ -6152,6 +6470,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets, int totalUndefinedNaturalWidths = usedMinimumWidth - totalExplicitColumnsWidths; + if (hierarchyHeaderInNeedOfFurtherHandling != null + && !hierarchyHeaderInNeedOfFurtherHandling.isDefinedWidth()) { + // ensure the cell gets enough space for the indent + int w = hierarchyHeaderInNeedOfFurtherHandling + .getNaturalColumnWidth(hierarchyColumnIndex); + int newSpace = Math.round(w + (float) extraSpace * (float) w + / totalUndefinedNaturalWidths); + if (newSpace >= hierarchyColumnIndent) { + // no special handling required + hierarchyHeaderInNeedOfFurtherHandling = null; + } else { + // treat as a defined width column of indent's width + totalExplicitColumnsWidths += hierarchyColumnIndent; + usedMinimumWidth -= w - hierarchyColumnIndent; + totalUndefinedNaturalWidths = usedMinimumWidth + - totalExplicitColumnsWidths; + expandRatioDivider += hierarchyHeaderInNeedOfFurtherHandling + .getExpandRatio(); + extraSpace = Math.max(availW - usedMinimumWidth, 0); + } + } + // we have some space that can be divided optimally HeaderCell hCell; colIndex = 0; @@ -6167,7 +6507,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets, newSpace = Math.round((w + extraSpace * hCell.getExpandRatio() / expandRatioDivider)); } else { - if (totalUndefinedNaturalWidths != 0) { + if (hierarchyHeaderInNeedOfFurtherHandling == hCell) { + // still exists, so needs exactly the indent's width + newSpace = hierarchyColumnIndent; + } else if (totalUndefinedNaturalWidths != 0) { // divide relatively to natural column widths newSpace = Math.round(w + (float) extraSpace * (float) w / totalUndefinedNaturalWidths); @@ -6177,8 +6520,21 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } checksum += newSpace; setColWidth(colIndex, newSpace, false); + } else { - checksum += hCell.getWidth(); + if (hierarchyHeaderInNeedOfFurtherHandling == hCell) { + // defined with enforced into indent width + checksum += hierarchyColumnIndent; + setColWidth(colIndex, hierarchyColumnIndent, false); + } else { + int cellWidth = hCell.getWidthWithIndent(); + checksum += cellWidth; + if (hCell.isHierarchyColumn()) { + // update in case the indent has changed + // (not detectable earlier) + setColWidth(colIndex, cellWidth, true); + } + } } colIndex++; } @@ -6194,8 +6550,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, while (headCells.hasNext()) { HeaderCell hc = (HeaderCell) headCells.next(); if (!hc.isDefinedWidth()) { - setColWidth(colIndex, - hc.getWidth() + availW - checksum, false); + setColWidth(colIndex, hc.getWidthWithIndent() + availW + - checksum, false); break; } colIndex++; diff --git a/client/src/com/vaadin/client/ui/VTextField.java b/client/src/com/vaadin/client/ui/VTextField.java index 1229eda093..0fbed0dd90 100644 --- a/client/src/com/vaadin/client/ui/VTextField.java +++ b/client/src/com/vaadin/client/ui/VTextField.java @@ -272,19 +272,16 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, /** For internal use only. May be removed or replaced in the future. */ public void setMaxLength(int newMaxLength) { - if (newMaxLength >= 0 && newMaxLength != maxLength) { - maxLength = newMaxLength; - updateMaxLength(maxLength); - } else if (maxLength != -1) { - maxLength = -1; - updateMaxLength(maxLength); + if (newMaxLength == maxLength) { + return; } - + maxLength = newMaxLength; + updateMaxLength(maxLength); } /** - * This method is reponsible for updating the DOM or otherwise ensuring that - * the given max length is enforced. Called when the max length for the + * This method is responsible for updating the DOM or otherwise ensuring + * that the given max length is enforced. Called when the max length for the * field has changed. * * @param maxLength diff --git a/client/src/com/vaadin/client/ui/VTreeTable.java b/client/src/com/vaadin/client/ui/VTreeTable.java index f65198865c..097b9c7ab2 100644 --- a/client/src/com/vaadin/client/ui/VTreeTable.java +++ b/client/src/com/vaadin/client/ui/VTreeTable.java @@ -122,8 +122,14 @@ public class VTreeTable extends VScrollTable { } } + @Override + protected int getHierarchyColumnIndex() { + return colIndexOfHierarchy + (showRowHeaders ? 1 : 0); + } + public class VTreeTableScrollBody extends VScrollTable.VScrollTableBody { private int indentWidth = -1; + private int maxIndent = 0; VTreeTableScrollBody() { super(); @@ -232,6 +238,11 @@ public class VTreeTable extends VScrollTable { treeSpacer.getParentElement().getStyle() .setPaddingLeft(getIndent(), Unit.PX); treeSpacer.getStyle().setWidth(getIndent(), Unit.PX); + int colWidth = getColWidth(getHierarchyColumnIndex()); + if (colWidth > 0 && getIndent() > colWidth) { + VTreeTable.this.setColWidth(getHierarchyColumnIndex(), + getIndent(), false); + } } } @@ -277,16 +288,12 @@ public class VTreeTable extends VScrollTable { // hierarchy column int indent = getIndent(); if (indent != -1) { - width = Math.max(width - getIndent(), 0); + width = Math.max(width - indent, 0); } } super.setCellWidth(cellIx, width); } - private int getHierarchyColumnIndex() { - return colIndexOfHierarchy + (showRowHeaders ? 1 : 0); - } - private int getIndent() { return (depth + 1) * getIndentWidth(); } @@ -323,7 +330,8 @@ public class VTreeTable extends VScrollTable { @Override public void execute() { if (showRowHeaders) { - setCellWidth(0, tHead.getHeaderCell(0).getWidth()); + setCellWidth(0, tHead.getHeaderCell(0) + .getWidthWithIndent()); calcAndSetSpanWidthOnCell(1); } else { calcAndSetSpanWidthOnCell(0); @@ -421,6 +429,22 @@ public class VTreeTable extends VScrollTable { return indentWidth; } + @Override + protected int getMaxIndent() { + return maxIndent; + } + + @Override + protected void calculateMaxIndent() { + int maxIndent = 0; + Iterator<Widget> iterator = iterator(); + while (iterator.hasNext()) { + VTreeTableRow next = (VTreeTableRow) iterator.next(); + maxIndent = Math.max(maxIndent, next.getIndent()); + } + this.maxIndent = maxIndent; + } + private void detectIndent(VTreeTableRow vTreeTableRow) { indentWidth = vTreeTableRow.treeSpacer.getOffsetWidth(); if (indentWidth == 0) { @@ -432,6 +456,7 @@ public class VTreeTable extends VScrollTable { VTreeTableRow next = (VTreeTableRow) iterator.next(); next.setIndent(); } + calculateMaxIndent(); } protected void unlinkRowsAnimatedAndUpdateCacheWhenFinished( @@ -471,6 +496,7 @@ public class VTreeTable extends VScrollTable { RowExpandAnimation anim = new RowExpandAnimation(insertedRows); anim.run(150); } + scrollBody.calculateMaxIndent(); return insertedRows; } diff --git a/client/src/com/vaadin/client/ui/VUI.java b/client/src/com/vaadin/client/ui/VUI.java index 6e6bcaf587..b627d4a2a9 100644 --- a/client/src/com/vaadin/client/ui/VUI.java +++ b/client/src/com/vaadin/client/ui/VUI.java @@ -41,6 +41,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorMap; import com.vaadin.client.Focusable; import com.vaadin.client.LayoutManager; +import com.vaadin.client.Profiler; import com.vaadin.client.VConsole; import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler; @@ -395,11 +396,13 @@ public class VUI extends SimplePanel implements ResizeHandler, * For internal use only. May be removed or replaced in the future. */ public void sendClientResized() { + Profiler.enter("VUI.sendClientResized"); Element parentElement = getElement().getParentElement(); int viewHeight = parentElement.getClientHeight(); int viewWidth = parentElement.getClientWidth(); ResizeEvent.fire(this, viewWidth, viewHeight); + Profiler.leave("VUI.sendClientResized"); } public native static void goTo(String url) diff --git a/client/src/com/vaadin/client/ui/label/LabelConnector.java b/client/src/com/vaadin/client/ui/label/LabelConnector.java index 896c9d8573..9639987e8d 100644 --- a/client/src/com/vaadin/client/ui/label/LabelConnector.java +++ b/client/src/com/vaadin/client/ui/label/LabelConnector.java @@ -17,6 +17,7 @@ package com.vaadin.client.ui.label; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.PreElement; +import com.vaadin.client.Profiler; import com.vaadin.client.Util; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; @@ -44,6 +45,7 @@ public class LabelConnector extends AbstractComponentConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); boolean sinkOnloads = false; + Profiler.enter("LabelConnector.onStateChanged update content"); switch (getState().contentMode) { case PREFORMATTED: PreElement preElement = Document.get().createPreElement(); @@ -69,8 +71,12 @@ public class LabelConnector extends AbstractComponentConnector { break; } + Profiler.leave("LabelConnector.onStateChanged update content"); + if (sinkOnloads) { + Profiler.enter("LabelConnector.onStateChanged sinkOnloads"); Util.sinkOnloadForImages(getWidget().getElement()); + Profiler.leave("LabelConnector.onStateChanged sinkOnloads"); } } diff --git a/client/src/com/vaadin/client/ui/link/LinkConnector.java b/client/src/com/vaadin/client/ui/link/LinkConnector.java index 807a9bcc64..228897278e 100644 --- a/client/src/com/vaadin/client/ui/link/LinkConnector.java +++ b/client/src/com/vaadin/client/ui/link/LinkConnector.java @@ -42,7 +42,11 @@ public class LinkConnector extends AbstractComponentConnector implements @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE); - getWidget().anchor.setAttribute("href", getWidget().src); + if (getWidget().src == null) { + getWidget().anchor.removeAttribute("href"); + } else { + getWidget().anchor.setAttribute("href", getWidget().src); + } } }); } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index c967642059..fc31cdf8ea 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -176,6 +176,7 @@ public class TableConnector extends AbstractHasComponentsConnector implements // amount of rows) getWidget().scrollBody.setLastRendered(getWidget().scrollBody .getLastRendered()); + getWidget().updateMaxIndent(); } else { getWidget().postponeSanityCheckForLastRendered = false; UIDL rowData = uidl.getChildByTagName("rows"); diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java index 9944ec5308..283bc1b63b 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java @@ -89,6 +89,14 @@ public abstract class TabsheetBaseConnector extends } } + // 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 diff --git a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java index 922e8d4a32..e2ede121b6 100644 --- a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java +++ b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java @@ -77,8 +77,10 @@ public class TextFieldConnector extends AbstractFieldConnector implements } getWidget().setColumns(getState().columns); - final String text = getState().text; - + String text = getState().text; + if (text == null) { + 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 diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index 85e75b943e..0fb7439587 100644 --- a/client/src/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Position; @@ -43,6 +44,7 @@ import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorMap; import com.vaadin.client.Focusable; import com.vaadin.client.Paintable; +import com.vaadin.client.TooltipInfo; import com.vaadin.client.UIDL; import com.vaadin.client.VConsole; import com.vaadin.client.communication.StateChangeEvent; @@ -312,7 +314,12 @@ public class UIConnector extends AbstractSingleComponentContainerConnector if (firstPaint) { // Queue the initial window size to be sent with the following // request. - getWidget().sendClientResized(); + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + getWidget().sendClientResized(); + } + }); } getWidget().rendering = false; } @@ -467,6 +474,16 @@ public class UIConnector extends AbstractSingleComponentContainerConnector } } + @Override + public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) { + /* + * Override method to make AbstractComponentConnector.hasTooltip() + * return true so there's a top level handler that takes care of hiding + * tooltips whenever the mouse is moved somewhere else. + */ + return super.getTooltipInfo(element); + } + /** * Tries to scroll the viewport so that the given connector is in view. * |