summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui/AbstractComponentContainer.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/ui/AbstractComponentContainer.java')
-rw-r--r--server/src/com/vaadin/ui/AbstractComponentContainer.java351
1 files changed, 351 insertions, 0 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponentContainer.java b/server/src/com/vaadin/ui/AbstractComponentContainer.java
new file mode 100644
index 0000000000..bc27242bb8
--- /dev/null
+++ b/server/src/com/vaadin/ui/AbstractComponentContainer.java
@@ -0,0 +1,351 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
+
+/**
+ * Extension to {@link AbstractComponent} that defines the default
+ * implementation for the methods in {@link ComponentContainer}. Basic UI
+ * components that need to contain other components inherit this class to easily
+ * qualify as a component container.
+ *
+ * @author Vaadin Ltd
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractComponentContainer extends AbstractComponent
+ implements ComponentContainer {
+
+ /**
+ * Constructs a new component container.
+ */
+ public AbstractComponentContainer() {
+ super();
+ }
+
+ /**
+ * Removes all components from the container. This should probably be
+ * re-implemented in extending classes for a more powerful implementation.
+ */
+ @Override
+ public void removeAllComponents() {
+ final LinkedList<Component> l = new LinkedList<Component>();
+
+ // Adds all components
+ for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
+ l.add(i.next());
+ }
+
+ // Removes all component
+ for (final Iterator<Component> i = l.iterator(); i.hasNext();) {
+ removeComponent(i.next());
+ }
+ }
+
+ /*
+ * Moves all components from an another container into this container. Don't
+ * add a JavaDoc comment here, we use the default documentation from
+ * implemented interface.
+ */
+ @Override
+ public void moveComponentsFrom(ComponentContainer source) {
+ final LinkedList<Component> components = new LinkedList<Component>();
+ for (final Iterator<Component> i = source.getComponentIterator(); i
+ .hasNext();) {
+ components.add(i.next());
+ }
+
+ for (final Iterator<Component> i = components.iterator(); i.hasNext();) {
+ final Component c = i.next();
+ source.removeComponent(c);
+ addComponent(c);
+ }
+ }
+
+ /* Events */
+
+ private static final Method COMPONENT_ATTACHED_METHOD;
+
+ private static final Method COMPONENT_DETACHED_METHOD;
+
+ static {
+ try {
+ COMPONENT_ATTACHED_METHOD = ComponentAttachListener.class
+ .getDeclaredMethod("componentAttachedToContainer",
+ new Class[] { ComponentAttachEvent.class });
+ COMPONENT_DETACHED_METHOD = ComponentDetachListener.class
+ .getDeclaredMethod("componentDetachedFromContainer",
+ new Class[] { ComponentDetachEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error finding methods in AbstractComponentContainer");
+ }
+ }
+
+ /* documented in interface */
+ @Override
+ public void addListener(ComponentAttachListener listener) {
+ addListener(ComponentContainer.ComponentAttachEvent.class, listener,
+ COMPONENT_ATTACHED_METHOD);
+ }
+
+ /* documented in interface */
+ @Override
+ public void addListener(ComponentDetachListener listener) {
+ addListener(ComponentContainer.ComponentDetachEvent.class, listener,
+ COMPONENT_DETACHED_METHOD);
+ }
+
+ /* documented in interface */
+ @Override
+ public void removeListener(ComponentAttachListener listener) {
+ removeListener(ComponentContainer.ComponentAttachEvent.class, listener,
+ COMPONENT_ATTACHED_METHOD);
+ }
+
+ /* documented in interface */
+ @Override
+ public void removeListener(ComponentDetachListener listener) {
+ removeListener(ComponentContainer.ComponentDetachEvent.class, listener,
+ COMPONENT_DETACHED_METHOD);
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Fires the component detached event. This should be called by the
+ * removeComponent methods after the component have been removed from this
+ * container.
+ *
+ * @param component
+ * the component that has been removed from this container.
+ */
+ protected void fireComponentDetachEvent(Component component) {
+ fireEvent(new ComponentDetachEvent(this, component));
+ }
+
+ /**
+ * This only implements the events and component parent calls. The extending
+ * classes must implement component list maintenance and call this method
+ * after component list maintenance.
+ *
+ * @see com.vaadin.ui.ComponentContainer#addComponent(Component)
+ */
+ @Override
+ public void addComponent(Component c) {
+ if (c instanceof ComponentContainer) {
+ // Make sure we're not adding the component inside it's own content
+ for (Component parent = this; parent != null; parent = parent
+ .getParent()) {
+ if (parent == c) {
+ throw new IllegalArgumentException(
+ "Component cannot be added inside it's own content");
+ }
+ }
+ }
+
+ if (c.getParent() != null) {
+ // If the component already has a parent, try to remove it
+ ComponentContainer oldParent = (ComponentContainer) c.getParent();
+ oldParent.removeComponent(c);
+
+ }
+
+ c.setParent(this);
+ fireComponentAttachEvent(c);
+ }
+
+ /**
+ * This only implements the events and component parent calls. The extending
+ * classes must implement component list maintenance and call this method
+ * before component list maintenance.
+ *
+ * @see com.vaadin.ui.ComponentContainer#removeComponent(Component)
+ */
+ @Override
+ public void removeComponent(Component c) {
+ if (c.getParent() == this) {
+ c.setParent(null);
+ fireComponentDetachEvent(c);
+ }
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ if (getState().isVisible() == visible) {
+ return;
+ }
+
+ 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, Unit unit) {
+ /*
+ * child tree repaints may be needed, due to our fall back support for
+ * invalid relative sizes
+ */
+ Collection<Component> dirtyChildren = null;
+ boolean childrenMayBecomeUndefined = false;
+ if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) {
+ // children currently in invalid state may need repaint
+ dirtyChildren = getInvalidSizedChildren(false);
+ } else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED)
+ || (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
+ * due to the fallback support
+ */
+ childrenMayBecomeUndefined = true;
+ dirtyChildren = getInvalidSizedChildren(false);
+ }
+ super.setWidth(width, unit);
+ repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
+ false);
+ }
+
+ private void repaintChangedChildTrees(
+ Collection<Component> invalidChildren,
+ boolean childrenMayBecomeUndefined, boolean vertical) {
+ if (childrenMayBecomeUndefined) {
+ Collection<Component> previouslyInvalidComponents = invalidChildren;
+ invalidChildren = getInvalidSizedChildren(vertical);
+ if (previouslyInvalidComponents != null && invalidChildren != null) {
+ for (Iterator<Component> iterator = invalidChildren.iterator(); iterator
+ .hasNext();) {
+ Component component = iterator.next();
+ if (previouslyInvalidComponents.contains(component)) {
+ // still invalid don't repaint
+ iterator.remove();
+ }
+ }
+ }
+ } else if (invalidChildren != null) {
+ Collection<Component> stillInvalidChildren = getInvalidSizedChildren(vertical);
+ if (stillInvalidChildren != null) {
+ for (Component component : stillInvalidChildren) {
+ // didn't become valid
+ invalidChildren.remove(component);
+ }
+ }
+ }
+ if (invalidChildren != null) {
+ repaintChildTrees(invalidChildren);
+ }
+ }
+
+ private Collection<Component> getInvalidSizedChildren(final boolean vertical) {
+ HashSet<Component> components = null;
+ if (this instanceof Panel) {
+ Panel p = (Panel) this;
+ ComponentContainer content = p.getContent();
+ boolean valid = vertical ? ComponentSizeValidator
+ .checkHeights(content) : ComponentSizeValidator
+ .checkWidths(content);
+
+ if (!valid) {
+ components = new HashSet<Component>(1);
+ components.add(content);
+ }
+ } else {
+ for (Iterator<Component> componentIterator = getComponentIterator(); componentIterator
+ .hasNext();) {
+ Component component = componentIterator.next();
+ boolean valid = vertical ? ComponentSizeValidator
+ .checkHeights(component) : ComponentSizeValidator
+ .checkWidths(component);
+ if (!valid) {
+ if (components == null) {
+ components = new HashSet<Component>();
+ }
+ components.add(component);
+ }
+ }
+ }
+ return components;
+ }
+
+ private void repaintChildTrees(Collection<Component> dirtyChildren) {
+ for (Component c : dirtyChildren) {
+ if (c instanceof ComponentContainer) {
+ ComponentContainer cc = (ComponentContainer) c;
+ cc.requestRepaintAll();
+ } else {
+ c.requestRepaint();
+ }
+ }
+ }
+
+ @Override
+ public void setHeight(float height, Unit unit) {
+ /*
+ * child tree repaints may be needed, due to our fall back support for
+ * invalid relative sizes
+ */
+ Collection<Component> dirtyChildren = null;
+ boolean childrenMayBecomeUndefined = false;
+ if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) {
+ // children currently in invalid state may need repaint
+ dirtyChildren = getInvalidSizedChildren(true);
+ } else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED)
+ || (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
+ * percentage due to the fallback support.
+ */
+ childrenMayBecomeUndefined = true;
+ dirtyChildren = getInvalidSizedChildren(true);
+ }
+ super.setHeight(height, unit);
+ repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
+ true);
+ }
+
+ @Override
+ public Iterator<Component> iterator() {
+ return getComponentIterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.HasComponents#isComponentVisible(com.vaadin.ui.Component)
+ */
+ @Override
+ public boolean isComponentVisible(Component childComponent) {
+ return true;
+ }
+} \ No newline at end of file