}
private void unregisterRemovedConnectors() {
+ int unregistered = 0;
List<ServerConnector> currentConnectors = new ArrayList<ServerConnector>(
connectorMap.getConnectors());
for (ServerConnector c : currentConnectors) {
// children. The RootConnector should never be
// unregistered even though it has no parent.
connectorMap.unregisterConnector(cc);
+ unregistered++;
} else if (cc.getParent() != null
&& !cc.getParent().getChildren().contains(cc)) {
VConsole.error("ERROR: Connector is connected to a parent but the parent does not contain the connector");
}
+ VConsole.log("* Unregistered " + unregistered + " connectors");
}
private void createConnectorsIfNeeded(ValueMap json) {
*/
public String getConnectorId();
+ /**
+ * 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();
}
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
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.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;
return result.toArray(new ComponentConnector[result.size()]);
}
- /**
- * Unregisters the child connectors for the given container recursively.
- *
- * Use when after removing a connector that contains other connectors. Does
- * not unregister the given container itself. Does not actually remove the
- * widgets from the DOM.
- *
- * @see #unregisterConnector(ServerConnector)
- * @param container
- * The container that contains the connectors that should be
- * unregistered
- * @deprecated Only here for now to support the broken VScrollTable behavior
- */
- @Deprecated
- public void unregisterChildConnectors(HasWidgets container) {
- // FIXME: This should be based on the paintable hierarchy
- final Iterator<Widget> it = container.iterator();
- while (it.hasNext()) {
- final Widget w = it.next();
- ComponentConnector p = getConnector(w);
- if (p != null) {
- // This will unregister the paintable and all its children
- idToComponentDetail.remove(p.getConnectorId());
- idToConnector.remove(p.getConnectorId());
- }
-
- if (w instanceof HasWidgets) {
- // For normal widget containers, unregister the children
- unregisterChildConnectors((HasWidgets) w);
- }
- }
- }
-
/**
* FIXME: Should not be here
*
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;
+ }
}
}
getWidget().hideScrollPositionAnnotation();
- getWidget().purgeUnregistryBag();
// selection is no in sync with server, avoid excessive server visits by
// clearing to flag used during the normal operation
*/
boolean recalcWidths = false;
- private final ArrayList<Panel> lazyUnregistryBag = new ArrayList<Panel>();
boolean rendering = false;
private boolean hasFocus = false;
private int dragmode;
void initializeRows(UIDL uidl, UIDL rowData) {
if (scrollBody != null) {
scrollBody.removeFromParent();
- lazyUnregistryBag.add(scrollBody);
}
scrollBody = createScrollBody();
}
}
- /**
- * Unregisters Paintables in "trashed" HasWidgets (IScrollTableBodys or
- * IScrollTableRows). This is done lazily as Table must survive from
- * "subtreecaching" logic.
- */
- void purgeUnregistryBag() {
- for (Iterator<Panel> iterator = lazyUnregistryBag.iterator(); iterator
- .hasNext();) {
- ConnectorMap.get(client).unregisterChildConnectors(iterator.next());
- }
- lazyUnregistryBag.clear();
- }
-
void updateActionMap(UIDL mainUidl) {
UIDL actionsUidl = mainUidl.getChildByTagName("actions");
if (actionsUidl == null) {
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);
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;
JSONArray children = new JSONArray();
for (Component child : getChildComponents(parent)) {
- if (child.isVisible()) {
+ if (isVisible(child)) {
String childConnectorId = getPaintableId(child);
children.put(childConnectorId);
}
}
}
+ private boolean isVisible(Component child) {
+ HasComponents parent = (HasComponents) 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() {
final VariableOwner owner = getVariableOwner(invocation
.getConnectorId());
- if (owner != null && owner.isEnabled()) {
+ boolean connectorEnabled;
+ if (owner instanceof Connector) {
+ connectorEnabled = ((Connector) owner).isConnectorEnabled();
+ } else {
+ // TODO Remove
+ connectorEnabled = owner.isEnabled();
+ }
+
+ if (owner != null && connectorEnabled) {
VariableChange change = new VariableChange(invocation);
// TODO could optimize with a single value map if only one
if (componentsRoot != r) {
resultset.remove(p);
} else if (component.getParent() != null
- && !component.getParent().isVisible()) {
+ && !isVisible(component.getParent())) {
/*
* Do not return components in an invisible subtree.
*
/**
* The container this component resides in.
*/
- private Component parent = null;
+ private HasComponents parent = null;
/**
* The EventRouter used for the event model.
}
/*
- * 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 getState().isEnabled()
- && (getParent() == null || getParent().isEnabled());
+ 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 (getState().isEnabled() != enabled) {
}
}
+ /*
+ * (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);
+ }
+ }
+
/*
* Tests if the component is in the immediate mode. Don't add a JavaDoc
* comment here, we use the default documentation from implemented
* @see com.vaadin.ui.Component#isVisible()
*/
public boolean isVisible() {
- if (getParent() == null) {
- return getState().isVisible();
- } else {
- return getState().isVisible() && getParent().isVisible()
- && ((HasComponents) getParent()).isComponentVisible(this);
- }
+ return getState().isVisible();
}
/*
* @see com.vaadin.ui.Component#setVisible(boolean)
*/
public void setVisible(boolean visible) {
-
if (getState().isVisible() != visible) {
getState().setVisible(visible);
// Instead of requesting repaint normally we
* 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;
}
* 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) {
// Paint the contents of the component
// Only paint content of visible components.
- if (isVisible()) {
+ if (isVisibleInContext()) {
if (eventIdentifiers != null) {
target.addAttribute("eventListeners",
repaintRequestListenersNotified = false;
}
+ /**
+ * 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
+ */
+ @Deprecated
+ protected boolean isVisibleInContext() {
+ HasComponents p = getParent();
+ while (p != null) {
+ if (!p.isVisible()) {
+ return false;
+ }
+ p = p.getParent();
+ }
+ // All parents visible, return this state
+ return isVisible();
+ }
+
/**
* Build CSS compatible string representation of height.
*
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>
* to the terminal or null if no items is visible.
*/
public Collection<?> getVisibleItemIds() {
- if (isVisible()) {
+ if (isVisibleInContext()) {
return getItemIds();
}
return null;
* 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,
* 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("Enabled");
*
* <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.
- * </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.
+ * 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>
- * 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}.
+ * 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>
*
- * @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()
*/
*
* <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>
* @return the parent component
* @see #setParent(Component)
*/
- public Component getParent();
+ public HasComponents getParent();
/**
* Sets the parent component of the component.
* 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
}
}
+ @Override
+ public boolean isConnectorEnabled() {
+ // TODO How can a Root be invisible? What does it mean?
+ return isVisible() && isEnabled();
+ }
}
@ClientWidget(TableConnector.class)
public class Table extends AbstractSelect implements Action.Container,
Container.Ordered, Container.Sortable, ItemClickSource,
- ItemClickNotifier, DragSource, DropTarget {
+ ItemClickNotifier, DragSource, DropTarget, HasComponents {
private static final Logger logger = Logger
.getLogger(Table.class.getName());
@Override
public void setVisible(boolean visible) {
- if (!isVisible() && visible) {
+ if (!isVisibleInContext() && 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() {
+ return visibleComponents.iterator();
+ }
+
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ }
}
--- /dev/null
+package com.vaadin.tests.components.table;
+
+import com.vaadin.data.Item;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.NativeButton;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.ColumnGenerator;
+
+public class TableWithChildComponents extends TestBase implements ClickListener {
+
+ private static final String COL2 = "Column 2 - generated";
+ private static final String COL1 = "Column 1 - components";
+ private Log log = new Log(10);
+
+ @Override
+ protected void setup() {
+ Table table = new Table();
+ table.setWidth("500px");
+ table.setPageLength(10);
+ table.addContainerProperty(COL1, Component.class, null);
+ table.addContainerProperty(COL2, Component.class, null);
+
+ table.addGeneratedColumn(COL2, new ColumnGenerator() {
+
+ public Object generateCell(Table source, Object itemId,
+ Object columnId) {
+ return new Button(
+ "Item id: " + itemId + " column: " + columnId,
+ TableWithChildComponents.this);
+ }
+ });
+
+ for (int i = 0; i < 100; i++) {
+ Item item = table.addItem("Row " + i);
+ item.getItemProperty(COL1).setValue(
+ new NativeButton("Row " + i + " native", this));
+ }
+
+ addComponent(table);
+ addComponent(log);
+
+ }
+
+ @Override
+ protected String getDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void buttonClick(ClickEvent event) {
+ log.log("Click on " + event.getButton().getCaption());
+
+ }
+
+}