diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2016-03-10 22:40:51 +0200 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2016-03-14 07:59:12 +0200 |
commit | a6653d3fe49e6a97468ac09f7f2f4d621bea1078 (patch) | |
tree | 96c82e20ca6551ee4c14c8877f0258b25c63cddf /server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java | |
parent | f7e57d77ce621ee39167369c31d989edc5633266 (diff) | |
download | vaadin-framework-a6653d3fe49e6a97468ac09f7f2f4d621bea1078.tar.gz vaadin-framework-a6653d3fe49e6a97468ac09f7f2f4d621bea1078.zip |
Migrate vaadin-server build to maven
Change-Id: I5c740f4e9cb28103bab199f9a552153d82277e7e
Diffstat (limited to 'server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java')
-rw-r--r-- | server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java b/server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java new file mode 100644 index 0000000000..0561996277 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/AbstractSplitPanel.java @@ -0,0 +1,766 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.ui; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; + +import org.jsoup.nodes.Element; + +import com.vaadin.event.ConnectorEventListener; +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.server.SizeWithUnit; +import com.vaadin.server.Sizeable; +import com.vaadin.shared.EventId; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelRpc; +import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState; +import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; +import com.vaadin.util.ReflectTools; + +/** + * AbstractSplitPanel. + * + * <code>AbstractSplitPanel</code> is base class for a component container that + * can contain two components. The components are split by a divider element. + * + * @author Vaadin Ltd. + * @since 6.5 + */ +public abstract class AbstractSplitPanel extends AbstractComponentContainer { + + // TODO use Unit in AbstractSplitPanelState and remove these + private Unit posUnit; + private Unit posMinUnit; + private Unit posMaxUnit; + + private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() { + + @Override + public void splitterClick(MouseEventDetails mouseDetails) { + fireEvent(new SplitterClickEvent(AbstractSplitPanel.this, + mouseDetails)); + } + + @Override + public void setSplitterPosition(float position) { + getSplitterState().position = position; + fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, + position, getSplitPositionUnit())); + } + }; + + public AbstractSplitPanel() { + registerRpc(rpc); + setSplitPosition(50, Unit.PERCENTAGE, false); + setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE); + } + + /** + * Modifiable and Serializable Iterator for the components, used by + * {@link AbstractSplitPanel#getComponentIterator()}. + */ + private class ComponentIterator implements Iterator<Component>, + Serializable { + + int i = 0; + + @Override + public boolean hasNext() { + if (i < getComponentCount()) { + return true; + } + return false; + } + + @Override + public Component next() { + if (!hasNext()) { + return null; + } + i++; + if (i == 1) { + return (getFirstComponent() == null ? getSecondComponent() + : getFirstComponent()); + } else if (i == 2) { + return getSecondComponent(); + } + return null; + } + + @Override + public void remove() { + if (i == 1) { + if (getFirstComponent() != null) { + setFirstComponent(null); + i = 0; + } else { + setSecondComponent(null); + } + } else if (i == 2) { + setSecondComponent(null); + } + } + } + + /** + * Add a component into this container. The component is added to the right + * or under the previous component. + * + * @param c + * the component to be added. + */ + + @Override + public void addComponent(Component c) { + if (getFirstComponent() == null) { + setFirstComponent(c); + } else if (getSecondComponent() == null) { + setSecondComponent(c); + } else { + throw new UnsupportedOperationException( + "Split panel can contain only two components"); + } + } + + /** + * 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 (getFirstComponent() == c) { + // Nothing to do + return; + } + + if (getFirstComponent() != null) { + // detach old + removeComponent(getFirstComponent()); + } + getState().firstChild = c; + if (c != null) { + super.addComponent(c); + } + } + + /** + * Sets the second component of this split panel. Depending on the direction + * the second component is shown at the bottom or to the right. + * + * @param c + * The component to use as second component + */ + public void setSecondComponent(Component c) { + if (getSecondComponent() == c) { + // Nothing to do + return; + } + + if (getSecondComponent() != null) { + // detach old + removeComponent(getSecondComponent()); + } + getState().secondChild = c; + if (c != null) { + super.addComponent(c); + } + } + + /** + * 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 (Component) getState(false).firstChild; + } + + /** + * 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 (Component) getState(false).secondChild; + } + + /** + * Removes the component from this container. + * + * @param c + * the component to be removed. + */ + + @Override + public void removeComponent(Component c) { + super.removeComponent(c); + if (c == getFirstComponent()) { + getState().firstChild = null; + } else if (c == getSecondComponent()) { + getState().secondChild = null; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.ComponentContainer#getComponentIterator() + */ + + @Override + public Iterator<Component> iterator() { + return new ComponentIterator(); + } + + /** + * Gets the number of contained components. Consistent with the iterator + * returned by {@link #getComponentIterator()}. + * + * @return the number of contained components (zero, one or two) + */ + + @Override + public int getComponentCount() { + int count = 0; + if (getFirstComponent() != null) { + count++; + } + if (getSecondComponent() != null) { + count++; + } + return count; + } + + /* Documented in superclass */ + + @Override + public void replaceComponent(Component oldComponent, Component newComponent) { + if (oldComponent == getFirstComponent()) { + setFirstComponent(newComponent); + } else if (oldComponent == getSecondComponent()) { + setSecondComponent(newComponent); + } + } + + /** + * Moves the position of the splitter. + * + * @param pos + * the new size of the first region in the unit that was last + * used (default is percentage). Fractions are only allowed when + * unit is percentage. + */ + public void setSplitPosition(float pos) { + setSplitPosition(pos, posUnit, false); + } + + /** + * Moves the position of the splitter. + * + * @param pos + * the new size of the region in the unit that was last used + * (default is percentage). Fractions are only allowed when unit + * is percentage. + * + * @param reverse + * if set to true the split splitter position is measured by the + * second region else it is measured by the first region + */ + public void setSplitPosition(float pos, boolean reverse) { + setSplitPosition(pos, posUnit, reverse); + } + + /** + * Moves the position of the splitter with given position and unit. + * + * @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. + */ + public void setSplitPosition(float pos, Unit unit) { + setSplitPosition(pos, unit, false); + } + + /** + * Moves the position of the splitter with given position and unit. + * + * @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 reverse + * if set to true the split splitter position is measured by the + * second region else it is measured by the first region + * + */ + 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 = getSplitterState(); + splitterState.position = pos; + splitterState.positionUnit = unit.getSymbol(); + splitterState.positionReversed = reverse; + posUnit = unit; + fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, pos, + posUnit)); + } + + /** + * Returns the current position of the splitter, in + * {@link #getSplitPositionUnit()} units. + * + * @return position of the splitter + */ + public float getSplitPosition() { + return getSplitterState(false).position; + } + + /** + * Returns the unit of position of the splitter + * + * @return unit of position of the splitter + * @see #setSplitPosition(float, Unit) + */ + public Unit getSplitPositionUnit() { + return posUnit; + } + + /** + * Is the split position reversed. By default the split position is measured + * by the first region, but if split position is reversed the measuring is + * done by the second region instead. + * + * @since 7.3.6 + * @return {@code true} if reversed, {@code false} otherwise. + * @see #setSplitPosition(float, boolean) + */ + public boolean isSplitPositionReversed() { + return getSplitterState(false).positionReversed; + } + + /** + * Sets the minimum split position to the given position and unit. If the + * split position is reversed, maximum and minimum are also reversed. + * + * @param pos + * the minimum position of the split + * @param unit + * the unit (from {@link Sizeable}) in which the size is given. + * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS + */ + public void setMinSplitPosition(float pos, Unit unit) { + setSplitPositionLimits(pos, unit, getSplitterState(false).maxPosition, + posMaxUnit); + } + + /** + * Returns the current minimum position of the splitter, in + * {@link #getMinSplitPositionUnit()} units. + * + * @return the minimum position of the splitter + */ + public float getMinSplitPosition() { + return getSplitterState(false).minPosition; + } + + /** + * Returns the unit of the minimum position of the splitter. + * + * @return the unit of the minimum position of the splitter + */ + public Unit getMinSplitPositionUnit() { + return posMinUnit; + } + + /** + * Sets the maximum split position to the given position and unit. If the + * split position is reversed, maximum and minimum are also reversed. + * + * @param pos + * the maximum position of the split + * @param unit + * the unit (from {@link Sizeable}) in which the size is given. + * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS + */ + public void setMaxSplitPosition(float pos, Unit unit) { + setSplitPositionLimits(getSplitterState(false).minPosition, posMinUnit, + pos, unit); + } + + /** + * Returns the current maximum position of the splitter, in + * {@link #getMaxSplitPositionUnit()} units. + * + * @return the maximum position of the splitter + */ + public float getMaxSplitPosition() { + return getSplitterState(false).maxPosition; + } + + /** + * Returns the unit of the maximum position of the splitter + * + * @return the unit of the maximum position of the splitter + */ + public Unit getMaxSplitPositionUnit() { + return posMaxUnit; + } + + /** + * Sets the maximum and minimum position of the splitter. If the split + * position is reversed, maximum and minimum are also reversed. + * + * @param minPos + * the new minimum position + * @param minPosUnit + * the unit (from {@link Sizeable}) in which the minimum position + * is given. + * @param maxPos + * the new maximum position + * @param maxPosUnit + * the unit (from {@link Sizeable}) in which the maximum position + * is given. + */ + private void setSplitPositionLimits(float minPos, Unit minPosUnit, + float maxPos, Unit maxPosUnit) { + if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS) + || (maxPosUnit != Unit.PERCENTAGE && maxPosUnit != Unit.PIXELS)) { + throw new IllegalArgumentException( + "Only percentage and pixel units are allowed"); + } + + SplitterState state = getSplitterState(); + + state.minPosition = minPos; + state.minPositionUnit = minPosUnit.getSymbol(); + posMinUnit = minPosUnit; + + state.maxPosition = maxPos; + state.maxPositionUnit = maxPosUnit.getSymbol(); + posMaxUnit = maxPosUnit; + } + + /** + * Lock the SplitPanels position, disabling the user from dragging the split + * handle. + * + * @param locked + * Set <code>true</code> if locked, <code>false</code> otherwise. + */ + public void setLocked(boolean locked) { + getSplitterState().locked = locked; + } + + /** + * Is the SplitPanel handle locked (user not allowed to change split + * position by dragging). + * + * @return <code>true</code> if locked, <code>false</code> otherwise. + */ + public boolean isLocked() { + return getSplitterState(false).locked; + } + + /** + * <code>SplitterClickListener</code> interface for listening for + * <code>SplitterClickEvent</code> fired by a <code>SplitPanel</code>. + * + * @see SplitterClickEvent + * @since 6.2 + */ + public interface SplitterClickListener extends ConnectorEventListener { + + public static final Method clickMethod = ReflectTools.findMethod( + SplitterClickListener.class, "splitterClick", + SplitterClickEvent.class); + + /** + * SplitPanel splitter has been clicked + * + * @param event + * SplitterClickEvent event. + */ + public void splitterClick(SplitterClickEvent event); + } + + public static class SplitterClickEvent extends ClickEvent { + + public SplitterClickEvent(Component source, + MouseEventDetails mouseEventDetails) { + super(source, mouseEventDetails); + } + + } + + /** + * Interface for listening for {@link SplitPositionChangeEvent}s fired by a + * SplitPanel. + * + * @since 7.5.0 + */ + public interface SplitPositionChangeListener extends ConnectorEventListener { + + public static final Method moveMethod = ReflectTools.findMethod( + SplitPositionChangeListener.class, "onSplitPositionChanged", + SplitPositionChangeEvent.class); + + /** + * SplitPanel splitter position has been changed. + * + * @param event + * SplitPositionChangeEvent event. + */ + public void onSplitPositionChanged(SplitPositionChangeEvent event); + } + + /** + * Event that indicates a change in SplitPanel's splitter position. + * + * @since 7.5.0 + */ + public static class SplitPositionChangeEvent extends Component.Event { + + private final float position; + private final Unit unit; + + public SplitPositionChangeEvent(final Component source, + final float position, final Unit unit) { + super(source); + this.position = position; + this.unit = unit; + } + + public float getSplitPosition() { + return position; + } + + public Unit getSplitPositionUnit() { + return unit; + } + + } + + public void addSplitterClickListener(SplitterClickListener listener) { + addListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class, + listener, SplitterClickListener.clickMethod); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addSplitterClickListener(SplitterClickListener)} + **/ + @Deprecated + public void addListener(SplitterClickListener listener) { + addSplitterClickListener(listener); + } + + public void removeSplitterClickListener(SplitterClickListener listener) { + removeListener(EventId.CLICK_EVENT_IDENTIFIER, + SplitterClickEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeSplitterClickListener(SplitterClickListener)} + **/ + @Deprecated + public void removeListener(SplitterClickListener listener) { + removeSplitterClickListener(listener); + } + + /** + * Register a listener to handle {@link SplitPositionChangeEvent}s. + * + * @since 7.5.0 + * @param listener + * {@link SplitPositionChangeListener} to be registered. + */ + public void addSplitPositionChangeListener( + SplitPositionChangeListener listener) { + addListener(SplitPositionChangeEvent.class, listener, + SplitPositionChangeListener.moveMethod); + } + + /** + * Removes a {@link SplitPositionChangeListener}. + * + * @since 7.5.0 + * @param listener + * SplitPositionChangeListener to be removed. + */ + public void removeSplitPositionChangeListener( + SplitPositionChangeListener listener) { + removeListener(SplitPositionChangeEvent.class, listener); + } + + @Override + protected AbstractSplitPanelState getState() { + return (AbstractSplitPanelState) super.getState(); + } + + @Override + protected AbstractSplitPanelState getState(boolean markAsDirty) { + return (AbstractSplitPanelState) super.getState(markAsDirty); + } + + private SplitterState getSplitterState() { + return ((AbstractSplitPanelState) super.getState()).splitterState; + } + + private SplitterState getSplitterState(boolean markAsDirty) { + return ((AbstractSplitPanelState) super.getState(markAsDirty)).splitterState; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element, + * com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void readDesign(Element design, DesignContext designContext) { + // handle default attributes + super.readDesign(design, designContext); + // handle custom attributes, use default values if no explicit value + // set + // There is no setter for reversed, so it will be handled using + // setSplitPosition. + boolean reversed = false; + if (design.hasAttr("reversed")) { + reversed = DesignAttributeHandler.readAttribute("reversed", + design.attributes(), Boolean.class); + setSplitPosition(getSplitPosition(), reversed); + } + if (design.hasAttr("split-position")) { + SizeWithUnit splitPosition = SizeWithUnit.parseStringSize( + design.attr("split-position"), Unit.PERCENTAGE); + setSplitPosition(splitPosition.getSize(), splitPosition.getUnit(), + reversed); + } + if (design.hasAttr("min-split-position")) { + SizeWithUnit minSplitPosition = SizeWithUnit.parseStringSize( + design.attr("min-split-position"), Unit.PERCENTAGE); + setMinSplitPosition(minSplitPosition.getSize(), + minSplitPosition.getUnit()); + } + if (design.hasAttr("max-split-position")) { + SizeWithUnit maxSplitPosition = SizeWithUnit.parseStringSize( + design.attr("max-split-position"), Unit.PERCENTAGE); + setMaxSplitPosition(maxSplitPosition.getSize(), + maxSplitPosition.getUnit()); + } + // handle children + if (design.children().size() > 2) { + throw new DesignException( + "A split panel can contain at most two components."); + } + for (Element childElement : design.children()) { + Component childComponent = designContext.readDesign(childElement); + if (childElement.hasAttr(":second")) { + setSecondComponent(childComponent); + } else { + addComponent(childComponent); + } + } + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> attributes = super.getCustomAttributes(); + // the setters of the properties do not accept strings such as "20px" + attributes.add("split-position"); + attributes.add("min-split-position"); + attributes.add("max-split-position"); + // no explicit setter for reversed + attributes.add("reversed"); + return attributes; + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + // handle default attributes (also clears children and attributes) + super.writeDesign(design, designContext); + // handle custom attributes (write only if a value is not the + // default value) + AbstractSplitPanel def = (AbstractSplitPanel) designContext + .getDefaultInstance(this); + if (getSplitPosition() != def.getSplitPosition() + || !def.getSplitPositionUnit().equals(getSplitPositionUnit())) { + String splitPositionString = asString(getSplitPosition()) + + getSplitPositionUnit(); + design.attr("split-position", splitPositionString); + } + if (getMinSplitPosition() != def.getMinSplitPosition() + || !def.getMinSplitPositionUnit().equals( + getMinSplitPositionUnit())) { + design.attr("min-split-position", asString(getMinSplitPosition()) + + getMinSplitPositionUnit()); + } + if (getMaxSplitPosition() != def.getMaxSplitPosition() + || !def.getMaxSplitPositionUnit().equals( + getMaxSplitPositionUnit())) { + design.attr("max-split-position", asString(getMaxSplitPosition()) + + getMaxSplitPositionUnit()); + } + if (getSplitterState().positionReversed) { + design.attr("reversed", true); + } + // handle child components + if (!designContext.shouldWriteChildren(this, def)) { + return; + } + Component firstComponent = getFirstComponent(); + Component secondComponent = getSecondComponent(); + if (firstComponent != null) { + Element childElement = designContext.createElement(firstComponent); + design.appendChild(childElement); + } + if (secondComponent != null) { + Element childElement = designContext.createElement(secondComponent); + if (firstComponent == null) { + childElement.attr(":second", true); + } + design.appendChild(childElement); + } + } + + private String asString(float number) { + int truncated = (int) number; + if (truncated == number) { + return "" + truncated; + } + return "" + number; + } +} |