package com.vaadin.addon.spreadsheet; /* * #%L * Vaadin Spreadsheet * %% * Copyright (C) 2013 - 2015 Vaadin Ltd * %% * This program is available under Commercial Vaadin Add-On License 3.0 * (CVALv3). * * See the file license.html distributed with this software for more * information about licensing. * * You should have received a copy of the CVALv3 along with this program. * If not, see <http://vaadin.com/license/cval-3>. * #L% */ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Collections; import java.util.Iterator; import org.apache.poi.ss.util.CellReference; import com.vaadin.addon.spreadsheet.client.PopupButtonClientRpc; import com.vaadin.addon.spreadsheet.client.PopupButtonServerRpc; import com.vaadin.addon.spreadsheet.client.PopupButtonState; import com.vaadin.addon.spreadsheet.client.PopupButtonWidget; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.AbstractSingleComponentContainer; import com.vaadin.ui.Component; import com.vaadin.ui.HasComponents; import com.vaadin.util.ReflectTools; /** * A button component that when clicked opens a pop-up next to spreadsheet cell * containing the button. * <p> * Vaadin components can be added inside the pop-up. * <p> * By default, the pop-up displays a close-button and a header containing the * current value of the cell that the pop-up button belongs to. * <p> * A button can be marked with "active" style with {@link #markActive(boolean)}. * <p> * To add the pop-up button to a specific spreadsheet, call * {@link Spreadsheet#setPopup(CellReference, PopupButton)}, * {@link Spreadsheet#setPopup(String, PopupButton)} or * {@link Spreadsheet#setPopup(int, int, PopupButton)}. The button can be * removed from the target cell by giving <code>null</code> as the PopupButton * parameter to one of the previously mentioned methods. * * @author Vaadin Ltd. */ @SuppressWarnings("serial") public class PopupButton extends AbstractComponent implements HasComponents { private PopupButtonServerRpc rpc = new PopupButtonServerRpc() { @Override public void onPopupClose() { setPopupVisible(false); fireClose(); } @Override public void onPopupButtonClick() { setPopupVisible(true); fireOpen(); } }; private Component child; private boolean popupVisible = false; /** * Constructs a new PopupButton. */ public PopupButton() { registerRpc(rpc); } /** * Constructs a new PopupButton with the given content. * * @param content * Content of the pop-up */ public PopupButton(Component content) { this(); child = content; } /** * Gets the cell reference for the cell that contains this pop-up button. * * @return Target cell reference */ public CellReference getCellReference() { return new CellReference(getState(false).sheet, getState(false).row - 1, getState(false).col - 1,false,false); } void setCellReference(CellReference cellReference) { getState().col = cellReference.getCol() + 1; getState().row = cellReference.getRow() + 1; getState().sheet = cellReference.getSheetName(); } /** * Gets the column for this pop-up button. * * @return Column index, 0-based */ public int getColumn() { return getState(false).col - 1; } /** * Gets the row for this pop-up button. * * @return Row index, 0-based */ public int getRow() { return getState(false).row - 1; } /** * Opens the pop-up if the button is currently rendered in the visible area * of the Spreadsheet. */ public void openPopup() { setPopupVisible(true); getRpcProxy(PopupButtonClientRpc.class).openPopup(); } /** * Closes the pop-up if it is open. */ public void closePopup() { setPopupVisible(false); getRpcProxy(PopupButtonClientRpc.class).closePopup(); } /** * Tells if the pop-up header is currently hidden. * * @return true if header is hidden, false otherwise */ public boolean isHeaderHidden() { return getState().headerHidden; } /** * Sets the pop-up header visible or hidden. * * @param headerHidden * <code>true</code> for hidden, <code>false</code> for visible. */ public void setHeaderHidden(boolean headerHidden) { getState().headerHidden = headerHidden; } /** * Set the width for this pop-up button's pop-up. Can be null or empty for * undefined width. * * @param width * New width for the pop-up */ public void setPopupWidth(String width) { getState().popupWidth = width; } /** * Gets the width for this pop-up button's pop-up. Can be null or empty. * * @return Width of the pop-up */ public String getPopupWidth() { return getState().popupWidth; } /** * Set the height for this pop-up button's pop-up. Can be null or empty for * undefined height. * * @param height * New height for the pop-up */ public void setPopupHeight(String height) { getState().popupHeight = height; } /** * Gets the height for this pop-up button's pop-up. Can be null or empty. * * @return Height of the pop-up */ public String getPopupHeight() { return getState().popupHeight; } @Override protected PopupButtonState getState() { return (PopupButtonState) super.getState(); } @Override protected PopupButtonState getState(boolean markAsDirty) { return (PopupButtonState) super.getState(markAsDirty); } /** * Set the contents of the popup. * */ public void setContent(Component content) { child = content; } /** * Get the contents of the popup. */ public Component getContent() { return child; } @Override public Iterator<Component> iterator() { if (child != null && popupVisible) { return Collections.singleton(child).iterator(); } else { return Collections.<Component> emptyList().iterator(); } } /** * Mark the button with "active" - style. See {@link PopupButtonWidget} for * the CSS class name. * * @param active * true to add "active" style, false to remove it */ public void markActive(boolean active) { getState().active = active; } /** * Adds a {@link PopupOpenListener} to this pop-up button. * * @param listener * The listener to add */ public void addPopupOpenListener(PopupOpenListener listener) { addListener(PopupOpenEvent.class, listener, PopupOpenListener.POPUP_OPEN_METHOD); } /** * Removes the given {@link PopupOpenListener} from this pop-up button. * * @param listener * The listener to remove */ public void removePopupOpenListener(PopupOpenListener listener) { removeListener(PopupOpenEvent.class, listener, PopupOpenListener.POPUP_OPEN_METHOD); } /** * Adds a {@link PopupCloseListener} to this pop-up button. * * @param listener */ public void addPopupCloseListener(PopupCloseListener listener) { addListener(PopupCloseEvent.class, listener, PopupCloseListener.POPUP_CLOSE_METHOD); } /** * Removes the given {@link PopupCloseListener} from this pop-up button. * * @param listener */ public void removePopupCloseListener(PopupCloseListener listener) { removeListener(PopupCloseEvent.class, listener, PopupCloseListener.POPUP_CLOSE_METHOD); } @Override public void detach() { // this order needs to be maintained so that // 1) iterator returns empty // 2) child -> parent is cleared properly popupVisible = false; child.setParent(null); super.detach(); } private void fireOpen() { fireEvent(new PopupOpenEvent(this)); } private void fireClose() { fireEvent(new PopupCloseEvent(this)); } /** * An event fired after the pop-up button returned by * {@link #getPopupButton()} has been clicked and the pop-up has been * opened. */ public static class PopupOpenEvent extends Component.Event { /** * Constructs a new open event for the given PopupButton. * * @param source * PopupButton component that has been opened. */ public PopupOpenEvent(Component source) { super(source); } /** * Gets the {@link PopupButton} where the event occurred. * * @return PopupButton component that has been opened. */ public PopupButton getPopupButton() { return (PopupButton) getSource(); } } /** * Interface for listening for a {@link PopupOpenEvent} fired by a * {@link PopupButton}. */ public interface PopupOpenListener extends Serializable { public static final Method POPUP_OPEN_METHOD = ReflectTools.findMethod( PopupOpenListener.class, "onPopupOpen", PopupOpenEvent.class); /** * Called when a {@link PopupButton} has been clicked and the pop-up has * been opened. A reference to the pop-up button is given by * {@link PopupOpenEvent#getPopupButton()}. * * @param event * An event containing the opened pop-up button */ public void onPopupOpen(PopupOpenEvent event); } /** * An event fired after the pop-up for the {@link PopupButton} returned by * {@link #getPopupButton()} has been closed. */ public static class PopupCloseEvent extends Component.Event { /** * Constructs a new close event for the given PopupButton. * * @param source * PopupButton component that has been closed. */ public PopupCloseEvent(Component source) { super(source); } /** * Gets the {@link PopupButton} where the event occurred. * * @return PopupButton component that has been closed. */ public PopupButton getPopupButton() { return (PopupButton) getSource(); } } /** * Interface for listening for a {@link PopupCloseEvent} fired by a * {@link PopupButton}. */ public interface PopupCloseListener extends Serializable { public static final Method POPUP_CLOSE_METHOD = ReflectTools .findMethod(PopupCloseListener.class, "onPopupClose", PopupCloseEvent.class); /** * Called when the pop-up for the {@link PopupButton} returned by * {@link PopupCloseEvent#getPopupButton()} has been closed. * * @param event * An event containing the closed pop-up button */ public void onPopupClose(PopupCloseEvent event); } private void setPopupVisible(boolean visible) { popupVisible = visible; if (child != null) { if (visible) { if (child.getParent() != null && !equals(child.getParent())) { // If the component already has a parent, try to remove it AbstractSingleComponentContainer.removeFromParent(child); } child.setParent(this); } else { if (equals(child.getParent())) { child.setParent(null); } } } markAsDirty(); } }