/* * Copyright 2000-2022 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.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.server.Sizeable; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.absolutelayout.AbsoluteLayoutServerRpc; import com.vaadin.shared.ui.absolutelayout.AbsoluteLayoutState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * AbsoluteLayout is a layout implementation that mimics html absolute * positioning. * */ @SuppressWarnings("serial") public class AbsoluteLayout extends AbstractLayout implements LayoutClickNotifier { // constants for design attributes private static final String ATTR_TOP = ":top"; private static final String ATTR_RIGHT = ":right"; private static final String ATTR_BOTTOM = ":bottom"; private static final String ATTR_LEFT = ":left"; private static final String ATTR_Z_INDEX = ":z-index"; private final AbsoluteLayoutServerRpc rpc = (MouseEventDetails mouseDetails, Connector clickedConnector) -> fireEvent( LayoutClickEvent.createEvent(AbsoluteLayout.this, mouseDetails, clickedConnector)); // Maps each component to a position private final LinkedHashMap componentToCoordinates = new LinkedHashMap<>(); /** * Creates an AbsoluteLayout with full size. */ public AbsoluteLayout() { registerRpc(rpc); setSizeFull(); } @Override protected AbsoluteLayoutState getState() { return (AbsoluteLayoutState) super.getState(); } @Override protected AbsoluteLayoutState getState(boolean markAsDirty) { return (AbsoluteLayoutState) super.getState(markAsDirty); } /** * Gets an iterator for going through all components enclosed in the * absolute layout. */ @Override public Iterator iterator() { return Collections .unmodifiableCollection(componentToCoordinates.keySet()) .iterator(); } /** * Gets the number of contained components. Consistent with the iterator * returned by {@link #getComponentIterator()}. * * @return the number of contained components */ @Override public int getComponentCount() { return componentToCoordinates.size(); } /** * Replaces one component with another one. The new component inherits the * old components position. */ @Override public void replaceComponent(Component oldComponent, Component newComponent) { ComponentPosition position = getPosition(oldComponent); removeComponent(oldComponent); addComponent(newComponent, position); } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponentContainer#addComponent(com.vaadin.ui. * Component ) */ @Override public void addComponent(Component c) { addComponent(c, new ComponentPosition()); } /** * Adds a component to the layout. The component can be positioned by * providing a string formatted in CSS-format. *

* For example the string "top:10px;left:10px" will position the component * 10 pixels from the left and 10 pixels from the top. The identifiers: * "top","left","right" and "bottom" can be used to specify the position. *

* * @param c * The component to add to the layout * @param cssPosition * The css position string */ public void addComponent(Component c, String cssPosition) { ComponentPosition position = new ComponentPosition(); position.setCSSString(cssPosition); addComponent(c, position); } /** * Adds the component using the given position. Ensures the position is only * set if the component is added correctly. * * @param c * The component to add * @param position * The position info for the component. Must not be null. * @throws IllegalArgumentException * If adding the component failed */ private void addComponent(Component c, ComponentPosition position) throws IllegalArgumentException { if (equals(c.getParent())) { removeComponent(c); } /* * Create position instance and add it to componentToCoordinates map. We * need to do this before we call addComponent so the attachListeners * can access this position. #6368 */ internalSetPosition(c, position); try { super.addComponent(c); } catch (IllegalArgumentException e) { internalRemoveComponent(c); throw e; } } /** * Removes the component from all internal data structures. Does not * actually remove the component from the layout (this is assumed to have * been done by the caller). * * @param c * The component to remove */ private void internalRemoveComponent(Component c) { componentToCoordinates.remove(c); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); // This could be in internalRemoveComponent and internalSetComponent if // Map was supported. We cannot get the child // connectorId unless the component is attached to the application so // the String->String map cannot be populated in internal* either. Map connectorToPosition = new HashMap<>(); for (Iterator ci = getComponentIterator(); ci.hasNext();) { Component c = ci.next(); connectorToPosition.put(c.getConnectorId(), getPosition(c).getCSSString()); } getState().connectorToCssPosition = connectorToPosition; } /* * (non-Javadoc) * * @see * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui * .Component) */ @Override public void removeComponent(Component c) { internalRemoveComponent(c); super.removeComponent(c); } /** * Gets the position of a component in the layout. Returns null if component * is not attached to the layout. *

* Note that you cannot update the position by updating this object. Call * {@link #setPosition(Component, ComponentPosition)} with the updated * {@link ComponentPosition} object. *

* * @param component * The component which position is needed * @return An instance of ComponentPosition containing the position of the * component, or null if the component is not enclosed in the * layout. */ public ComponentPosition getPosition(Component component) { return componentToCoordinates.get(component); } /** * Sets the position of a component in the layout. * * @param component * @param position */ public void setPosition(Component component, ComponentPosition position) { if (!componentToCoordinates.containsKey(component)) { throw new IllegalArgumentException( "Component must be a child of this layout"); } internalSetPosition(component, position); } /** * Updates the position for a component. Caller must ensure component is a * child of this layout. * * @param component * The component. Must be a child for this layout. Not enforced. * @param position * New position. Must not be null. */ private void internalSetPosition(Component component, ComponentPosition position) { componentToCoordinates.put(component, position); markAsDirty(); } /** * The CompontPosition class represents a components position within the * absolute layout. It contains the attributes for left, right, top and * bottom and the units used to specify them. */ public class ComponentPosition implements Serializable { private int zIndex = -1; private Float topValue = null; private Float rightValue = null; private Float bottomValue = null; private Float leftValue = null; private Unit topUnits = Unit.PIXELS; private Unit rightUnits = Unit.PIXELS; private Unit bottomUnits = Unit.PIXELS; private Unit leftUnits = Unit.PIXELS; /** * Sets the position attributes using CSS syntax. Attributes not * included in the string are reset to their unset states. * *
         * setCSSString("top:10px;left:20%;z-index:16;");
         * 
* * @param css */ public void setCSSString(String css) { topValue = rightValue = bottomValue = leftValue = null; topUnits = rightUnits = bottomUnits = leftUnits = Unit.PIXELS; zIndex = -1; if (css == null) { return; } for (String cssProperty : css.split(";")) { String[] keyValuePair = cssProperty.split(":"); String key = keyValuePair[0].trim(); if (key.isEmpty()) { continue; } if (key.equals("z-index")) { zIndex = Integer.parseInt(keyValuePair[1].trim()); } else { String value; if (keyValuePair.length > 1) { value = keyValuePair[1].trim(); } else { value = ""; } String symbol = value.replaceAll("[0-9\\.\\-]+", ""); if (!symbol.isEmpty()) { value = value.substring(0, value.indexOf(symbol)) .trim(); } float v = Float.parseFloat(value); Unit unit = Unit.getUnitFromSymbol(symbol); if (key.equals("top")) { topValue = v; topUnits = unit; } else if (key.equals("right")) { rightValue = v; rightUnits = unit; } else if (key.equals("bottom")) { bottomValue = v; bottomUnits = unit; } else if (key.equals("left")) { leftValue = v; leftUnits = unit; } } } markAsDirty(); } /** * Converts the internal values into a valid CSS string. * * @return A valid CSS string */ public String getCSSString() { String s = ""; if (topValue != null) { s += "top:" + topValue + topUnits.getSymbol() + ";"; } if (rightValue != null) { s += "right:" + rightValue + rightUnits.getSymbol() + ";"; } if (bottomValue != null) { s += "bottom:" + bottomValue + bottomUnits.getSymbol() + ";"; } if (leftValue != null) { s += "left:" + leftValue + leftUnits.getSymbol() + ";"; } if (zIndex >= 0) { s += "z-index:" + zIndex + ";"; } return s; } /** * Sets the 'top' attribute; distance from the top of the component to * the top edge of the layout. * * @param topValue * The value of the 'top' attribute * @param topUnits * The unit of the 'top' attribute. See UNIT_SYMBOLS for a * description of the available units. */ public void setTop(Float topValue, Unit topUnits) { this.topValue = topValue; this.topUnits = topUnits; markAsDirty(); } /** * Sets the 'right' attribute; distance from the right of the component * to the right edge of the layout. * * @param rightValue * The value of the 'right' attribute * @param rightUnits * The unit of the 'right' attribute. See UNIT_SYMBOLS for a * description of the available units. */ public void setRight(Float rightValue, Unit rightUnits) { this.rightValue = rightValue; this.rightUnits = rightUnits; markAsDirty(); } /** * Sets the 'bottom' attribute; distance from the bottom of the * component to the bottom edge of the layout. * * @param bottomValue * The value of the 'bottom' attribute * @param bottomUnits * The unit of the 'bottom' attribute. See UNIT_SYMBOLS for a * description of the available units. */ public void setBottom(Float bottomValue, Unit bottomUnits) { this.bottomValue = bottomValue; this.bottomUnits = bottomUnits; markAsDirty(); } /** * Sets the 'left' attribute; distance from the left of the component to * the left edge of the layout. * * @param leftValue * The value of the 'left' attribute * @param leftUnits * The unit of the 'left' attribute. See UNIT_SYMBOLS for a * description of the available units. */ public void setLeft(Float leftValue, Unit leftUnits) { this.leftValue = leftValue; this.leftUnits = leftUnits; markAsDirty(); } /** * Sets the 'z-index' attribute; the visual stacking order. * * @param zIndex * The z-index for the component. */ public void setZIndex(int zIndex) { this.zIndex = zIndex; markAsDirty(); } /** * Sets the value of the 'top' attribute; distance from the top of the * component to the top edge of the layout. * * @param topValue * The value of the 'left' attribute */ public void setTopValue(Float topValue) { this.topValue = topValue; markAsDirty(); } /** * Gets the 'top' attributes value in current units. * * @see #getTopUnits() * @return The value of the 'top' attribute, null if not set */ public Float getTopValue() { return topValue; } /** * Gets the 'right' attributes value in current units. * * @return The value of the 'right' attribute, null if not set * @see #getRightUnits() */ public Float getRightValue() { return rightValue; } /** * Sets the 'right' attribute value (distance from the right of the * component to the right edge of the layout). Currently active units * are maintained. * * @param rightValue * The value of the 'right' attribute * @see #setRightUnits(Unit) */ public void setRightValue(Float rightValue) { this.rightValue = rightValue; markAsDirty(); } /** * Gets the 'bottom' attributes value using current units. * * @return The value of the 'bottom' attribute, null if not set * @see #getBottomUnits() */ public Float getBottomValue() { return bottomValue; } /** * Sets the 'bottom' attribute value (distance from the bottom of the * component to the bottom edge of the layout). Currently active units * are maintained. * * @param bottomValue * The value of the 'bottom' attribute * @see #setBottomUnits(Unit) */ public void setBottomValue(Float bottomValue) { this.bottomValue = bottomValue; markAsDirty(); } /** * Gets the 'left' attributes value using current units. * * @return The value of the 'left' attribute, null if not set * @see #getLeftUnits() */ public Float getLeftValue() { return leftValue; } /** * Sets the 'left' attribute value (distance from the left of the * component to the left edge of the layout). Currently active units are * maintained. * * @param leftValue * The value of the 'left' CSS-attribute * @see #setLeftUnits(Unit) */ public void setLeftValue(Float leftValue) { this.leftValue = leftValue; markAsDirty(); } /** * Gets the unit for the 'top' attribute. * * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public Unit getTopUnits() { return topUnits; } /** * Sets the unit for the 'top' attribute. * * @param topUnits * See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public void setTopUnits(Unit topUnits) { this.topUnits = topUnits; markAsDirty(); } /** * Gets the unit for the 'right' attribute. * * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public Unit getRightUnits() { return rightUnits; } /** * Sets the unit for the 'right' attribute. * * @param rightUnits * See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public void setRightUnits(Unit rightUnits) { this.rightUnits = rightUnits; markAsDirty(); } /** * Gets the unit for the 'bottom' attribute. * * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public Unit getBottomUnits() { return bottomUnits; } /** * Sets the unit for the 'bottom' attribute. * * @param bottomUnits * See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public void setBottomUnits(Unit bottomUnits) { this.bottomUnits = bottomUnits; markAsDirty(); } /** * Gets the unit for the 'left' attribute. * * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public Unit getLeftUnits() { return leftUnits; } /** * Sets the unit for the 'left' attribute. * * @param leftUnits * See {@link Sizeable} UNIT_SYMBOLS for a description of the * available units. */ public void setLeftUnits(Unit leftUnits) { this.leftUnits = leftUnits; markAsDirty(); } /** * Gets the 'z-index' attribute. * * @return the zIndex The z-index attribute */ public int getZIndex() { return zIndex; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return getCSSString(); } } @Override public Registration addLayoutClickListener(LayoutClickListener listener) { return addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener, LayoutClickListener.clickMethod); } @Override @Deprecated public void removeLayoutClickListener(LayoutClickListener listener) { removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener); } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Node, * com.vaadin.ui.declarative.DesignContext) */ @Override public void readDesign(Element design, DesignContext designContext) { // process default attributes super.readDesign(design, designContext); // handle children for (Element childComponent : design.children()) { Attributes attr = childComponent.attributes(); Component newChild = designContext.readDesign(childComponent); StringBuilder css = new StringBuilder(); if (attr.hasKey(ATTR_TOP)) { css.append("top:").append(attr.get(ATTR_TOP)).append(';'); } if (attr.hasKey(ATTR_RIGHT)) { css.append("right:").append(attr.get(ATTR_RIGHT)).append(';'); } if (attr.hasKey(ATTR_BOTTOM)) { css.append("bottom:").append(attr.get(ATTR_BOTTOM)).append(';'); } if (attr.hasKey(ATTR_LEFT)) { css.append("left:").append(attr.get(ATTR_LEFT)).append(';'); } if (attr.hasKey(ATTR_Z_INDEX)) { css.append("z-index:").append(attr.get(ATTR_Z_INDEX)) .append(';'); } addComponent(newChild, css.toString()); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Node, * com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); AbsoluteLayout def = designContext.getDefaultInstance(this); if (!designContext.shouldWriteChildren(this, def)) { return; } // handle children for (Component child : this) { Element childElement = designContext.createElement(child); design.appendChild(childElement); // handle position ComponentPosition position = getPosition(child); writePositionAttribute(childElement, ATTR_TOP, position.getTopUnits().getSymbol(), position.getTopValue()); writePositionAttribute(childElement, ATTR_RIGHT, position.getRightUnits().getSymbol(), position.getRightValue()); writePositionAttribute(childElement, ATTR_BOTTOM, position.getBottomUnits().getSymbol(), position.getBottomValue()); writePositionAttribute(childElement, ATTR_LEFT, position.getLeftUnits().getSymbol(), position.getLeftValue()); // handle z-index if (position.getZIndex() >= 0) { childElement.attr(ATTR_Z_INDEX, String.valueOf(position.zIndex)); } } } /** * Private method for writing position attributes * * @since 7.4 * @param node * target node * @param key * attribute key * @param symbol * value symbol * @param value * the value */ private void writePositionAttribute(Node node, String key, String symbol, Float value) { if (value != null) { String valueString = DesignAttributeHandler.getFormatter() .format(value); node.attr(key, valueString + symbol); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17008 Content-Disposition: inline; filename="AbstractColorPicker.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "6dad2cf86f848d0c48c4a396971fae75f9554099" /* * Copyright 2000-2022 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.util.Collection; import java.util.Locale; import java.util.Objects; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.shared.ui.colorpicker.AbstractColorPickerState; import com.vaadin.shared.ui.colorpicker.Color; import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc; import com.vaadin.ui.components.colorpicker.ColorPickerPopup; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * An abstract class that defines default implementation for a color picker * component. * * @since 7.0.0 */ public abstract class AbstractColorPicker extends AbstractField { /** * Interface for converting 2d-coordinates to a Color. */ public interface Coordinates2Color extends Serializable { /** * Calculates a color from coordinates. * * @param x * the x-coordinate * @param y * the y-coordinate * * @return the color */ public Color calculate(int x, int y); /** * Calculates coordinates from a color. * * @param c * the c * * @return the integer array with the coordinates */ public int[] calculate(Color c); } /** * The style of the color picker popup. */ public enum PopupStyle { /** A full popup with all tabs visible. */ POPUP_NORMAL("normal"), /** A simple popup with only the swatches (palette) tab. */ POPUP_SIMPLE("simple"); private final String style; PopupStyle(String styleName) { style = styleName; } @Override public String toString() { return style; } } protected static final String STYLENAME_DEFAULT = "v-colorpicker"; protected static final String STYLENAME_BUTTON = "v-button"; protected static final String STYLENAME_AREA = "v-colorpicker-area"; protected PopupStyle popupStyle = PopupStyle.POPUP_NORMAL; private ColorPickerPopup window; /** The currently selected color. */ protected Color color; private UI parent; private String popupCaption = null; private int positionX = 0; private int positionY = 0; protected boolean rgbVisible = true; protected boolean hsvVisible = true; protected boolean swatchesVisible = true; protected boolean historyVisible = true; protected boolean textfieldVisible = true; private boolean modal; private ColorPickerServerRpc rpc = new ColorPickerServerRpc() { @Override public void openPopup(boolean openPopup) { showPopup(openPopup); } @Override public void changeColor(String col) { Color valueC = new Color( Integer.parseInt(col.substring(1, col.length()), 16)); color = valueC; setValue(color, true); } }; /** * Instantiates a new color picker. */ public AbstractColorPicker() { this("Colors", Color.WHITE); } /** * Instantiates a new color picker. * * @param popupCaption * the caption of the popup window */ public AbstractColorPicker(String popupCaption) { this(popupCaption, Color.WHITE); } /** * Instantiates a new color picker. * * @param popupCaption * the caption of the popup window * @param initialColor * the initial color */ public AbstractColorPicker(String popupCaption, Color initialColor) { super(); registerRpc(rpc); setValue(initialColor); this.popupCaption = popupCaption; setDefaultStyles(); setCaption(""); } /** * Returns the current selected color of this color picker. * * @return the selected color, not null */ @Override public Color getValue() { return color; } /** * Sets the selected color of this color picker. If the new color is not * equal to getValue(), fires a {@link ValueChangeEvent}. * * @param color * the new selected color, not null * @throws NullPointerException * if {@code color} is {@code null} */ @Override public void setValue(Color color) { Objects.requireNonNull(color, "color cannot be null"); super.setValue(color); if (window != null) { window.setValue(color); } } /** * Set true if the component should show a default caption (css-code for the * currently selected color, e.g. #ffffff) when no other caption is * available. * * @param enabled * {@code true} to enable the default caption, {@code false} to * disable */ public void setDefaultCaptionEnabled(boolean enabled) { getState().showDefaultCaption = enabled; } /** * Returns true if the component shows the default caption (css-code for the * currently selected color, e.g. #ffffff) if no other caption is available. * * @return {@code true} if the default caption is enabled, {@code false} * otherwise */ public boolean isDefaultCaptionEnabled() { return getState(false).showDefaultCaption; } /** * Sets the position of the popup window. * * @param x * the x-coordinate * @param y * the y-coordinate */ public void setPosition(int x, int y) { positionX = x; positionY = y; if (window != null) { window.setPositionX(x); window.setPositionY(y); } } /** * Sets the style of the popup window. * * @param style * the popup window style */ public void setPopupStyle(PopupStyle style) { popupStyle = style; switch (style) { case POPUP_NORMAL: setRGBVisibility(true); setHSVVisibility(true); setSwatchesVisibility(true); setHistoryVisibility(true); setTextfieldVisibility(true); break; case POPUP_SIMPLE: setRGBVisibility(false); setHSVVisibility(false); setSwatchesVisibility(true); setHistoryVisibility(false); setTextfieldVisibility(false); break; default: assert false : "Unknown popup style " + style; } } /** * Gets the style for the popup window. * * @since 7.5.0 * @return popup window style */ public PopupStyle getPopupStyle() { return popupStyle; } /** * Sets the visibility of the RGB tab. * * @param visible * {@code true} to display the RGB tab, {@code false} to hide it */ public void setRGBVisibility(boolean visible) { if (!visible && !hsvVisible && !swatchesVisible) { throw new IllegalArgumentException("Cannot hide all tabs."); } rgbVisible = visible; if (window != null) { window.setRGBTabVisible(visible); } } /** * Gets the visibility of the RGB Tab. * * @since 7.5.0 * @return visibility of the RGB tab */ public boolean getRGBVisibility() { return rgbVisible; } /** * Sets the visibility of the HSV Tab. * * @param visible * {@code true} to display the HSV tab, {@code false} to hide it */ public void setHSVVisibility(boolean visible) { if (!visible && !rgbVisible && !swatchesVisible) { throw new IllegalArgumentException("Cannot hide all tabs."); } hsvVisible = visible; if (window != null) { window.setHSVTabVisible(visible); } } /** * Gets the visibility of the HSV tab. * * @since 7.5.0 * @return {@code true} if the HSV tab is currently displayed, {@code false} * otherwise */ public boolean getHSVVisibility() { return hsvVisible; } /** * Sets the visibility of the Swatches (palette) tab. * * @param visible * {@code true} to display the Swatches tab, {@code false} to * hide it */ public void setSwatchesVisibility(boolean visible) { if (!visible && !hsvVisible && !rgbVisible) { throw new IllegalArgumentException("Cannot hide all tabs."); } swatchesVisible = visible; if (window != null) { window.setSwatchesTabVisible(visible); } } /** * Gets the visibility of the Swatches (palette) tab. * * @since 7.5.0 * @return {@code true} if the Swatches tab is currently displayed, * {@code false} otherwise */ public boolean getSwatchesVisibility() { return swatchesVisible; } /** * Sets the visibility of the color history, displaying recently picked * colors. * * @param visible * {@code true} to display the history, {@code false} to hide it */ public void setHistoryVisibility(boolean visible) { historyVisible = visible; if (window != null) { window.setHistoryVisible(visible); } } /** * Gets the visibility of the Color history. * * @since 7.5.0 * @return {@code true} if the history is currently displayed, {@code false} * otherwise */ public boolean getHistoryVisibility() { return historyVisible; } /** * Sets the visibility of the CSS color code text field. * * @param visible * {@code true} to display the CSS text field, {@code false} to * hide it */ public void setTextfieldVisibility(boolean visible) { textfieldVisible = visible; if (window != null) { window.setPreviewVisible(visible); } } /** * Gets the visibility of CSS color code text field. * * @since 7.5.0 * @return {@code true} if the CSS text field is currently displayed, * {@code false} otherwise */ public boolean getTextfieldVisibility() { return textfieldVisible; } @Override protected AbstractColorPickerState getState() { return (AbstractColorPickerState) super.getState(); } @Override protected AbstractColorPickerState getState(boolean markAsDirty) { return (AbstractColorPickerState) super.getState(markAsDirty); } /** * Sets the default styles of the component. */ protected abstract void setDefaultStyles(); /** * Shows a popup-window for color selection. */ public void showPopup() { showPopup(true); } /** * Hides a popup-window for color selection. */ public void hidePopup() { showPopup(false); } /** * Shows or hides the popup window depending on the given parameter. If * there is no such window yet, one is created. * * @param open * {@code true} to display the popup, {@code false} to hide it */ protected void showPopup(boolean open) { if (open && !isReadOnly()) { if (parent == null) { parent = getUI(); } Color color = getValue(); if (window == null) { window = new ColorPickerPopup(color); window.setCaption(popupCaption); window.setRGBTabVisible(rgbVisible); window.setHSVTabVisible(hsvVisible); window.setSwatchesTabVisible(swatchesVisible); window.setHistoryVisible(historyVisible); window.setPreviewVisible(textfieldVisible); window.addCloseListener( event -> getState().popupVisible = false); window.addValueChangeListener(event -> { window.setValue(color); setValue(event.getValue(), true); }); window.getHistory().setValue(color); window.setPositionX(positionX); window.setPositionY(positionY); window.setVisible(true); window.setValue(color); window.setModal(modal); parent.addWindow(window); window.focus(); } else if (!parent.equals(window.getParent())) { window.setRGBTabVisible(rgbVisible); window.setHSVTabVisible(hsvVisible); window.setSwatchesTabVisible(swatchesVisible); window.setHistoryVisible(historyVisible); window.setPreviewVisible(textfieldVisible); window.setValue(color); window.getHistory().setValue(color); window.setVisible(true); window.setModal(modal); parent.addWindow(window); window.focus(); } window.setValue(color); } else if (window != null) { window.setVisible(false); parent.removeWindow(window); } getState().popupVisible = open; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); Attributes attributes = design.attributes(); if (design.hasAttr("color")) { // Ignore the # character String hexColor = DesignAttributeHandler .readAttribute("color", attributes, String.class) .substring(1); doSetValue(new Color(Integer.parseInt(hexColor, 16))); } if (design.hasAttr("popup-style")) { setPopupStyle(PopupStyle.valueOf("POPUP_" + attributes.get("popup-style").toUpperCase(Locale.ROOT))); } if (design.hasAttr("position")) { String[] position = attributes.get("position").split(","); setPosition(Integer.parseInt(position[0]), Integer.parseInt(position[1])); } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); Attributes attribute = design.attributes(); DesignAttributeHandler.writeAttribute("color", attribute, getValue().getCSS(), Color.WHITE.getCSS(), String.class, designContext); DesignAttributeHandler.writeAttribute("popup-style", attribute, popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple", "normal", String.class, designContext); DesignAttributeHandler.writeAttribute("position", attribute, positionX + "," + positionY, "0,0", String.class, designContext); } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("color"); result.add("position"); result.add("popup-style"); return result; } @Override protected void doSetValue(Color color) { this.color = color; getState().color = color.getCSS(); } @Override public Color getEmptyValue() { return Color.WHITE; } /** * Sets ColorPicker modality. When a modal ColorPicker is open, components * outside that ColorPicker cannot be accessed. *

* Note: It must be set to {@code true} if ColorPicker is a child of modal * {@link Window} *

* * @see Window#setModal * @since 8.4.1 * @param modal * true if modality is to be turned on */ public void setModal(boolean modal) { this.modal = modal; } /** * Checks the modality of the dialog. * * @see #setModal(boolean) * @since 8.4.1 * @return true if the dialog is modal, false otherwise */ public boolean isModal() { return this.modal; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 45549 Content-Disposition: inline; filename="AbstractComponent.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "94cd7f3a9a5b5df6b2b208b1abefbcdfa90f6f6e" /* * Copyright 2000-2022 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.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.data.HasValue; import com.vaadin.event.ActionManager; import com.vaadin.event.ConnectorActionManager; import com.vaadin.event.ContextClickEvent; import com.vaadin.event.ContextClickEvent.ContextClickListener; import com.vaadin.event.ContextClickEvent.ContextClickNotifier; import com.vaadin.event.ShortcutListener; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.ClientConnector; import com.vaadin.server.ComponentSizeValidator; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Extension; import com.vaadin.server.Resource; import com.vaadin.server.Responsive; import com.vaadin.server.SizeWithUnit; import com.vaadin.server.Sizeable; import com.vaadin.server.UserError; import com.vaadin.server.VaadinSession; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.AbstractFieldState; import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ContextClickRpc; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.ErrorLevel; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.util.ReflectTools; /** * An abstract class that defines default implementation for the * {@link Component} interface. Basic UI components that are not derived from an * external component can inherit this class to easily qualify as Vaadin * components. Most components in Vaadin do just that. * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public abstract class AbstractComponent extends AbstractClientConnector implements Component, ContextClickNotifier { /* Private members */ /** * Application specific data object. The component does not use or modify * this. */ private Object applicationData; /** * The internal error message of the component. */ private ErrorMessage componentError; /** * Locale of this component. */ private Locale locale; /** * The component should receive focus (if {@link Focusable}) when attached. */ private boolean delayedFocus; /* Sizeable fields */ private float width = SIZE_UNDEFINED; private float height = SIZE_UNDEFINED; private Unit widthUnit = Unit.PIXELS; private Unit heightUnit = Unit.PIXELS; /** * Keeps track of the Actions added to this component; the actual * handling/notifying is delegated, usually to the containing window. */ private ConnectorActionManager actionManager; private boolean visible = true; private HasComponents parent; protected static final String DESIGN_ATTR_PLAIN_TEXT = "plain-text"; /* Constructor */ /** * Constructs a new Component. */ public AbstractComponent() { // ComponentSizeValidator.setCreationLocation(this); } /* Get/Set component properties */ /* * (non-Javadoc) * * @see com.vaadin.ui.Component#setId(java.lang.String) */ @Override public void setId(String id) { getState().id = id; } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#getId() */ @Override public String getId() { return getState(false).id; } /** * @deprecated As of 7.0. Use {@link #setId(String)} */ @Deprecated public void setDebugId(String id) { setId(id); } /** * @deprecated As of 7.0. Use {@link #getId()} */ @Deprecated public String getDebugId() { return getId(); } /* * Gets the component's style. Don't add a JavaDoc comment here, we use the * default documentation from implemented interface. */ @Override public String getStyleName() { String s = ""; if (ComponentStateUtil.hasStyles(getState(false))) { for (final Iterator it = getState(false).styles .iterator(); it.hasNext();) { s += it.next(); if (it.hasNext()) { s += " "; } } } return s; } /* * Sets the component's style. Don't add a JavaDoc comment here, we use the * default documentation from implemented interface. */ @Override public void setStyleName(String style) { if (style == null || style.isEmpty()) { getState().styles = null; return; } if (getState().styles == null) { getState().styles = new ArrayList<>(); } List styles = getState().styles; styles.clear(); StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { styles.add(tokenizer.nextToken()); } } @Override public void setPrimaryStyleName(String style) { getState().primaryStyleName = style; } @Override public String getPrimaryStyleName() { return getState(false).primaryStyleName; } @Override public void addStyleName(String style) { if (style == null || style.isEmpty()) { return; } if (getState().styles != null && getState().styles.contains(style)) { return; } if (style.contains(" ")) { // Split space separated style names and add them one by one. StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { addStyleName(tokenizer.nextToken()); } return; } if (getState().styles == null) { getState().styles = new ArrayList<>(); } List styles = getState().styles; styles.add(style); } @Override public void removeStyleName(String style) { if (ComponentStateUtil.hasStyles(getState())) { StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { getState().styles.remove(tokenizer.nextToken()); } } } /* * Get's the component's caption. Don't add a JavaDoc comment here, we use * the default documentation from implemented interface. */ @Override public String getCaption() { return getState(false).caption; } @Override public void setCaption(String caption) { getState().caption = caption; } /** * Sets whether the caption is rendered as HTML. *

* If set to true, the captions are rendered in the browser as HTML and the * developer is responsible for ensuring no harmful HTML is used. If set to * false, the caption is rendered in the browser as plain text. *

* The default is false, i.e. to render that caption as plain text. * * @param captionAsHtml * true if the captions are rendered as HTML, false if rendered * as plain text */ public void setCaptionAsHtml(boolean captionAsHtml) { getState().captionAsHtml = captionAsHtml; } /** * Checks whether captions are rendered as HTML *

* The default is false, i.e. to render that caption as plain text. * * @return true if the captions are rendered as HTML, false if rendered as * plain text */ public boolean isCaptionAsHtml() { return getState(false).captionAsHtml; } /* * Don't add a JavaDoc comment here, we use the default documentation from * implemented interface. */ @Override public Locale getLocale() { if (locale != null) { return locale; } HasComponents parent = getParent(); if (parent != null) { return parent.getLocale(); } final VaadinSession session = getSession(); if (session != null) { return session.getLocale(); } return null; } /** * Sets the locale of this component. * *

     * // Component for which the locale is meaningful
     * InlineDateField date = new InlineDateField("Datum");
     *
     * // German language specified with ISO 639-1 language
     * // code and ISO 3166-1 alpha-2 country code.
     * date.setLocale(new Locale("de", "DE"));
     *
     * date.setResolution(DateField.RESOLUTION_DAY);
     * layout.addComponent(date);
     * 
* * * @param locale * the locale to become this component's locale. */ public void setLocale(Locale locale) { this.locale = locale; if (locale != null && isAttached()) { getUI().getLocaleService().addLocale(locale); } markAsDirty(); } /* * Gets the component's icon resource. Don't add a JavaDoc comment here, we * use the default documentation from implemented interface. */ @Override public Resource getIcon() { return getResource(ComponentConstants.ICON_RESOURCE); } /** * Sets the component's icon. * * @param icon * the icon to be shown with the component's caption. */ @Override public void setIcon(Resource icon) { setResource(ComponentConstants.ICON_RESOURCE, icon); } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#isEnabled() */ @Override public boolean isEnabled() { return getState(false).enabled; } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#setEnabled(boolean) */ @Override public void setEnabled(boolean enabled) { getState().enabled = enabled; } /* * (non-Javadoc) * * @see com.vaadin.client.Connector#isConnectorEnabled() */ @Override public boolean isConnectorEnabled() { if (!isVisible()) { return false; } else if (!isEnabled()) { return false; } else if (!super.isConnectorEnabled()) { return false; } return !(getParent() instanceof SelectiveRenderer) || ((SelectiveRenderer) getParent()).isRendered(this); } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#isVisible() */ @Override public boolean isVisible() { return visible; } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#setVisible(boolean) */ @Override public void setVisible(boolean visible) { if (isVisible() == visible) { return; } this.visible = visible; if (visible) { /* * If the visibility state is toggled from invisible to visible it * affects all children (the whole hierarchy) in addition to this * component. */ markAsDirtyRecursive(); } if (getParent() != null) { // Must always repaint the parent (at least the hierarchy) when // visibility of a child component changes. getParent().markAsDirty(); } } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#getDescription() */ @Override public String getDescription() { return getState(false).description; } /** * Sets the component's description. See {@link #getDescription()} for more * information on what the description is. * * @see #setDescription(String, ContentMode) * @param description * the new description string for the component. */ public void setDescription(String description) { setDescription(description, ContentMode.PREFORMATTED); } /** * Sets the component's description using given content {@code mode}. See * {@link #getDescription()} for more information on what the description * is. *

* If the content {@code mode} is {@literal ContentMode.HTML} the * description is displayed as HTML in tooltips or directly in certain * components so care should be taken to avoid creating the possibility for * HTML injection and possibly XSS vulnerabilities. * * @param description * the new description string for the component. * @param mode * the content mode for the description * @since 8.0 */ public void setDescription(String description, ContentMode mode) { getState().description = description; getState().descriptionContentMode = mode; } /* * Gets the component's parent component. Don't add a JavaDoc comment here, * we use the default documentation from implemented interface. */ @Override public HasComponents getParent() { return parent; } @Override public void setParent(HasComponents parent) { // If the parent is not changed, don't do anything if (parent == null ? this.parent == null : parent.equals(this.parent)) { return; } if (parent != null && this.parent != null) { throw new IllegalStateException( getClass().getName() + " already has a parent."); } ClientConnector oldParent = getParent(); // Send a detach event if the component is currently attached if (isAttached()) { detach(); } // Connect to new parent this.parent = parent; // Send attach event if the component is now attached if (isAttached()) { attach(); } if (oldParent != null) { oldParent.markAsDirty(); } } /** * Returns the closest ancestor with the given type. *

* To find the Window that contains the component, use {@code Window w = * getParent(Window.class);} *

* * @param * The type of the ancestor * @param parentType * The ancestor class we are looking for * @return The first ancestor that can be assigned to the given class. Null * if no ancestor with the correct type could be found. */ public T findAncestor(Class parentType) { HasComponents p = getParent(); while (p != null) { if (parentType.isAssignableFrom(p.getClass())) { return parentType.cast(p); } p = p.getParent(); } return null; } /** * Gets the error message for this component. * * @return ErrorMessage containing the description of the error state of the * component or null, if the component contains no errors. Extending * classes should override this method if they support other error * message types such as validation errors or buffering errors. The * returned error message contains information about all the errors. */ public ErrorMessage getErrorMessage() { return componentError; } /** * Gets the component's error message. * * @link Terminal.ErrorMessage#ErrorMessage(String, int) * * @return the component's error message. */ public ErrorMessage getComponentError() { return componentError; } /** * Sets the component's error message. The message may contain certain XML * tags, for more information see * * @link Component.ErrorMessage#ErrorMessage(String, int) * * @param componentError * the new ErrorMessage of the component. */ public void setComponentError(ErrorMessage componentError) { this.componentError = componentError; fireComponentErrorEvent(); markAsDirty(); } /* * Notify the component that it's attached to a window. Don't add a JavaDoc * comment here, we use the default documentation from implemented * interface. */ @Override public void attach() { super.attach(); if (delayedFocus) { focus(); } setActionManagerViewer(); if (locale != null) { getUI().getLocaleService().addLocale(locale); } } /* * Detach the component from application. Don't add a JavaDoc comment here, * we use the default documentation from implemented interface. */ @Override public void detach() { super.detach(); if (actionManager != null) { // Remove any existing viewer. UI cast is just to make the // compiler happy actionManager.setViewer((UI) null); } } /** * Sets the focus for this component if the component is {@link Focusable}. */ protected void focus() { if (this instanceof Focusable) { final VaadinSession session = getSession(); if (session != null) { getUI().setFocusedComponent((Focusable) this); delayedFocus = false; } else { delayedFocus = true; } } } /** * Build CSS compatible string representation of height. * * @return CSS height */ private String getCSSHeight() { return getHeight() + getHeightUnits().getSymbol(); } /** * Build CSS compatible string representation of width. * * @return CSS width */ private String getCSSWidth() { return getWidth() + getWidthUnits().getSymbol(); } /** * Returns the shared state bean with information to be sent from the server * to the client. * * Subclasses should override this method and set any relevant fields of the * state returned by super.getState(). * * @since 7.0 * * @return updated component shared state */ @Override protected AbstractComponentState getState() { return (AbstractComponentState) super.getState(); } @Override protected AbstractComponentState getState(boolean markAsDirty) { return (AbstractComponentState) super.getState(markAsDirty); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); // TODO This logic should be on the client side and the state should // simply be a data object with "width" and "height". if (getHeight() >= 0 && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator.parentCanDefineHeight(this))) { getState().height = "" + getCSSHeight(); } else { getState().height = ""; } if (getWidth() >= 0 && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator.parentCanDefineWidth(this))) { getState().width = "" + getCSSWidth(); } else { getState().width = ""; } ErrorMessage error = getErrorMessage(); if (null != error) { getState().errorMessage = error.getFormattedHtmlMessage(); getState().errorLevel = error.getErrorLevel(); } else { getState().errorMessage = null; getState().errorLevel = null; } } /* General event framework */ private static final Method COMPONENT_EVENT_METHOD = ReflectTools .findMethod(Component.Listener.class, "componentEvent", Component.Event.class); /* Component event framework */ /* * Registers a new listener to listen events generated by this component. * Don't add a JavaDoc comment here, we use the default documentation from * implemented interface. */ @Override public Registration addListener(Component.Listener listener) { return addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD); } @Override @Deprecated public void removeListener(Component.Listener listener) { removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD); } /** * Emits the component event. It is transmitted to all registered listeners * interested in such events. */ protected void fireComponentEvent() { fireEvent(new Component.Event(this)); } /** * Emits the component error event. It is transmitted to all registered * listeners interested in such events. */ protected void fireComponentErrorEvent() { fireEvent(new Component.ErrorEvent(getComponentError(), this)); } /** * Sets the data object, that can be used for any application specific data. * The component does not use or modify this data. * * @param data * the Application specific data. * @since 3.1 */ public void setData(Object data) { applicationData = data; } /** * Gets the application specific data. See {@link #setData(Object)}. * * @return the Application specific data set with setData function. * @since 3.1 */ public Object getData() { return applicationData; } /* Sizeable and other size related methods */ /* * (non-Javadoc) * * @see com.vaadin.Sizeable#getHeight() */ @Override public float getHeight() { return height; } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#getHeightUnits() */ @Override public Unit getHeightUnits() { return heightUnit; } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#getWidth() */ @Override public float getWidth() { return width; } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#getWidthUnits() */ @Override public Unit getWidthUnits() { return widthUnit; } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setHeight(float, Unit) */ @Override public void setHeight(float height, Unit unit) { if (unit == null) { throw new IllegalArgumentException("Unit can not be null"); } this.height = height; heightUnit = unit; markAsDirty(); // ComponentSizeValidator.setHeightLocation(this); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setSizeFull() */ @Override public void setSizeFull() { setWidth(100, Unit.PERCENTAGE); setHeight(100, Unit.PERCENTAGE); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setWidthFull() */ @Override public void setWidthFull() { setWidth(100, Unit.PERCENTAGE); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setHeightFull() */ @Override public void setHeightFull() { setHeight(100, Unit.PERCENTAGE); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setSizeUndefined() */ @Override public void setSizeUndefined() { setWidthUndefined(); setHeightUndefined(); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setWidthUndefined() */ @Override public void setWidthUndefined() { setWidth(-1, Unit.PIXELS); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setHeightUndefined() */ @Override public void setHeightUndefined() { setHeight(-1, Unit.PIXELS); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setWidth(float, Unit) */ @Override public void setWidth(float width, Unit unit) { if (unit == null) { throw new IllegalArgumentException("Unit can not be null"); } this.width = width; widthUnit = unit; markAsDirty(); // ComponentSizeValidator.setWidthLocation(this); } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setWidth(java.lang.String) */ @Override public void setWidth(String width) { SizeWithUnit size = SizeWithUnit.parseStringSize(width); if (size != null) { setWidth(size.getSize(), size.getUnit()); } else { setWidth(-1, Unit.PIXELS); } } /* * (non-Javadoc) * * @see com.vaadin.server.Sizeable#setHeight(java.lang.String) */ @Override public void setHeight(String height) { SizeWithUnit size = SizeWithUnit.parseStringSize(height); if (size != null) { setHeight(size.getSize(), size.getUnit()); } else { setHeight(-1, Unit.PIXELS); } } /* * (non-Javadoc) * * @see com.vaadin.ui.Component#readDesign(org.jsoup.nodes.Element, * com.vaadin.ui.declarative.DesignContext) */ @Override public void readDesign(Element design, DesignContext designContext) { Attributes attr = design.attributes(); // handle default attributes for (String attribute : getDefaultAttributes()) { if (design.hasAttr(attribute)) { DesignAttributeHandler.assignValue(this, attribute, design.attr(attribute)); } } // handle locale if (attr.hasKey("locale")) { setLocale(getLocaleFromString(attr.get("locale"))); } // handle width and height readSize(attr); // handle component error if (attr.hasKey("error")) { UserError error = new UserError(attr.get("error"), com.vaadin.server.AbstractErrorMessage.ContentMode.HTML, ErrorLevel.ERROR); setComponentError(error); } // Tab index when applicable if (design.hasAttr("tabindex") && this instanceof Focusable) { ((Focusable) this).setTabIndex(DesignAttributeHandler.readAttribute( "tabindex", design.attributes(), Integer.class)); } // check for unsupported attributes Set supported = new HashSet<>(); supported.addAll(getDefaultAttributes()); supported.addAll(getCustomAttributes()); for (Attribute a : attr) { if (!a.getKey().startsWith(":") && !supported.contains(a.getKey())) { // Add unknown attributes as custom attributes to the context designContext.setCustomAttribute(this, a.getKey(), a.getValue()); } } } /** * Constructs a Locale corresponding to the given string. The string should * consist of one, two or three parts with '_' between the different parts * if there is more than one part. The first part specifies the language, * the second part the country and the third part the variant of the locale. * * @param localeString * the locale specified as a string * @return the Locale object corresponding to localeString */ private Locale getLocaleFromString(String localeString) { if (localeString == null) { return null; } String[] parts = localeString.split("_"); if (parts.length > 3) { throw new RuntimeException( "Cannot parse the locale string: " + localeString); } switch (parts.length) { case 1: return new Locale(parts[0]); case 2: return new Locale(parts[0], parts[1]); default: return new Locale(parts[0], parts[1], parts[2]); } } /** * Toggles responsiveness of this component. * * @since 7.5.0 * @param responsive * boolean enables responsiveness, false disables */ public void setResponsive(boolean responsive) { if (responsive) { // make responsive if necessary if (!isResponsive()) { Responsive.makeResponsive(this); } } else { // remove responsive extensions List extensions = new ArrayList<>(getExtensions()); for (Extension e : extensions) { if (e instanceof Responsive) { removeExtension(e); } } } } /** * Returns true if the component is responsive. * * @since 7.5.0 * @return true if the component is responsive */ public boolean isResponsive() { for (Extension e : getExtensions()) { if (e instanceof Responsive) { return true; } } return false; } /** * Sets the read-only status in the state of this {@code AbstractComponent}. * This method should be made public in {@link Component Components} that * implement {@link HasValue}. * * @param readOnly * a boolean value specifying whether the component is put * read-only mode or not */ protected void setReadOnly(boolean readOnly) { if (getState(false) instanceof AbstractFieldState) { if (readOnly != isReadOnly()) { ((AbstractFieldState) getState()).readOnly = readOnly; } } else { throw new IllegalStateException( "This component does not support the read-only mode, since state is of type " + getStateType().getSimpleName() + " and does not inherit " + AbstractFieldState.class.getSimpleName()); } } /** * Returns the read-only status from the state of this * {@code AbstractComponent}. This method should be made public in * {@link Component Components} that implement {@link HasValue}. * * @return {@code true} if state has read-only on; {@code false} if not * @see #setReadOnly(boolean) */ protected boolean isReadOnly() { if (getState(false) instanceof AbstractFieldState) { return ((AbstractFieldState) getState(false)).readOnly; } throw new IllegalStateException( "This component does not support the read-only mode, since state is of type " + getStateType().getSimpleName() + " and does not inherit " + AbstractFieldState.class.getSimpleName()); } /** * Reads the size of this component from the given design attributes. If the * attributes do not contain relevant size information, defaults is * consulted. * * @param attributes * the design attributes */ private void readSize(Attributes attributes) { // read width if (attributes.hasKey("width-auto") || attributes.hasKey("size-auto")) { this.setWidth(null); } else if (attributes.hasKey("width-full") || attributes.hasKey("size-full")) { this.setWidth("100%"); } else if (attributes.hasKey("width")) { this.setWidth(attributes.get("width")); } // read height if (attributes.hasKey("height-auto") || attributes.hasKey("size-auto")) { this.setHeight(null); } else if (attributes.hasKey("height-full") || attributes.hasKey("size-full")) { this.setHeight("100%"); } else if (attributes.hasKey("height")) { this.setHeight(attributes.get("height")); } } /** * Writes the size related attributes for the component if they differ from * the defaults * * @param attributes * the attribute map where the attribute are written * @param defaultInstance * the default instance of the class for fetching the default * values */ private void writeSize(Attributes attributes, Component defaultInstance) { if (hasEqualSize(defaultInstance)) { // we have default values -> ignore return; } boolean widthFull = getWidth() == 100f && getWidthUnits().equals(Sizeable.Unit.PERCENTAGE); boolean heightFull = getHeight() == 100f && getHeightUnits().equals(Sizeable.Unit.PERCENTAGE); boolean widthAuto = getWidth() == -1; boolean heightAuto = getHeight() == -1; // first try the full shorthands if (widthFull && heightFull) { attributes.put("size-full", true); } else if (widthAuto && heightAuto) { attributes.put("size-auto", true); } else { // handle width if (!hasEqualWidth(defaultInstance)) { if (widthFull) { attributes.put("width-full", true); } else if (widthAuto) { attributes.put("width-auto", true); } else { String widthString = DesignAttributeHandler.getFormatter() .format(getWidth()) + getWidthUnits().getSymbol(); attributes.put("width", widthString); } } if (!hasEqualHeight(defaultInstance)) { // handle height if (heightFull) { attributes.put("height-full", true); } else if (heightAuto) { attributes.put("height-auto", true); } else { String heightString = DesignAttributeHandler.getFormatter() .format(getHeight()) + getHeightUnits().getSymbol(); attributes.put("height", heightString); } } } } /** * Test if the given component has equal width with this instance * * @param component * the component for the width comparison * @return true if the widths are equal */ private boolean hasEqualWidth(Component component) { return getWidth() == component.getWidth() && getWidthUnits().equals(component.getWidthUnits()); } /** * Test if the given component has equal height with this instance * * @param component * the component for the height comparison * @return true if the heights are equal */ private boolean hasEqualHeight(Component component) { return getHeight() == component.getHeight() && getHeightUnits().equals(component.getHeightUnits()); } /** * Test if the given components has equal size with this instance * * @param component * the component for the size comparison * @return true if the sizes are equal */ private boolean hasEqualSize(Component component) { return hasEqualWidth(component) && hasEqualHeight(component); } /** * Returns a collection of attributes that do not require custom handling * when reading or writing design. These are typically attributes of some * primitive type. The default implementation searches setters with * primitive values * * @return a collection of attributes that can be read and written using the * default approach. */ private Collection getDefaultAttributes() { Collection attributes = DesignAttributeHandler .getSupportedAttributes(this.getClass()); attributes.removeAll(getCustomAttributes()); return attributes; } /** * Returns a collection of attributes that should not be handled by the * basic implementation of the {@link #readDesign(Element, DesignContext)} * and {@link #writeDesign(Element, DesignContext)} methods. Typically these * are handled in a custom way in the overridden versions of the above * methods * * @since 7.4 * * @return the collection of attributes that are not handled by the basic * implementation */ protected Collection getCustomAttributes() { List l = new ArrayList<>(Arrays.asList(CUSTOM_ATTRIBUTES)); if (this instanceof Focusable) { l.add("tab-index"); l.add("tabindex"); } return l; } private static final String[] CUSTOM_ATTRIBUTES = { "width", "height", "debug-id", "error", "width-auto", "height-auto", "width-full", "height-full", "size-auto", "size-full", "immediate", "locale", "read-only", "_id" }; /* * (non-Javadoc) * * @see com.vaadin.ui.Component#writeDesign(org.jsoup.nodes.Element, * com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { AbstractComponent def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); // handle default attributes for (String attribute : getDefaultAttributes()) { DesignAttributeHandler.writeAttribute(this, attribute, attr, def, designContext); } // handle locale if (getLocale() != null && (getParent() == null || !getLocale().equals(getParent().getLocale()))) { design.attr("locale", getLocale().toString()); } // handle size writeSize(attr, def); // handle component error String errorMsg = getComponentError() != null ? getComponentError().getFormattedHtmlMessage() : null; String defErrorMsg = def.getComponentError() != null ? def.getComponentError().getFormattedHtmlMessage() : null; if (!SharedUtil.equals(errorMsg, defErrorMsg)) { attr.put("error", errorMsg); } // handle tab index if (this instanceof Focusable) { DesignAttributeHandler.writeAttribute("tabindex", attr, ((Focusable) this).getTabIndex(), ((Focusable) def).getTabIndex(), Integer.class, designContext); } // handle custom attributes Map customAttributes = designContext .getCustomAttributes(this); if (customAttributes != null) { for (Entry entry : customAttributes.entrySet()) { attr.put(entry.getKey(), entry.getValue()); } } } /* * Actions */ /** * Gets the {@link ActionManager} used to manage the * {@link ShortcutListener}s added to this component. * * @return the ActionManager in use */ protected ActionManager getActionManager() { if (actionManager == null) { actionManager = new ConnectorActionManager(this); setActionManagerViewer(); } return actionManager; } /** * Set a viewer for the action manager to be the parent sub window (if the * component is in a window) or the UI (otherwise). This is still a * simplification of the real case as this should be handled by the parent * VOverlay (on the client side) if the component is inside an VOverlay * component. */ private void setActionManagerViewer() { if (actionManager != null && getUI() != null) { // Attached and has action manager Window w = findAncestor(Window.class); if (w != null) { actionManager.setViewer(w); } else { actionManager.setViewer(getUI()); } } } public Registration addShortcutListener(ShortcutListener shortcut) { Objects.requireNonNull(shortcut, "Listener must not be null."); getActionManager().addAction(shortcut); return () -> getActionManager().removeAction(shortcut); } @Deprecated public void removeShortcutListener(ShortcutListener shortcut) { getActionManager().removeAction(shortcut); } /** * Determine whether a content component is equal to, or the * ancestor of this component. * * @param content * the potential ancestor element * @return true if the relationship holds */ protected boolean isOrHasAncestor(Component content) { if (content instanceof HasComponents) { for (Component parent = this; parent != null; parent = parent .getParent()) { if (parent.equals(content)) { return true; } } } return false; } @Override public Registration addContextClickListener(ContextClickListener listener) { // Register default Context Click RPC if needed. This RPC won't be // called if there are no listeners on the server-side. A client-side // connector can override this and use a different RPC channel. if (getRpcManager(ContextClickRpc.class.getName()) == null) { registerRpc( (ContextClickRpc) (MouseEventDetails details) -> fireEvent( new ContextClickEvent(AbstractComponent.this, details))); } return addListener(EventId.CONTEXT_CLICK, ContextClickEvent.class, listener, ContextClickEvent.CONTEXT_CLICK_METHOD); } @Override @Deprecated public void removeContextClickListener(ContextClickListener listener) { removeListener(EventId.CONTEXT_CLICK, ContextClickEvent.class, listener); } /** * Sets the visibility of the required indicator. NOTE: Does not * apply for all components!. *

* If the component supports the required indicator (state extends * {@link AbstractFieldState}), then expose this method and * {@link #isRequiredIndicatorVisible()} as {@code public} in the component * and call this method. *

* This method will throw a {@link IllegalStateException} if the component * state (returned by {@link #getState()}) does not inherit * {@link AbstractFieldState}. * * @param visible * true to make the required indicator visible, * false if not * @since 8.0 */ protected void setRequiredIndicatorVisible(boolean visible) { if (getState(false) instanceof AbstractFieldState) { ((AbstractFieldState) getState()).required = visible; } else { throw new IllegalStateException( "This component does not support the required indicator, since state is of type " + getStateType().getSimpleName() + " and does not inherit " + AbstractFieldState.class.getSimpleName()); } } /** * Checks whether the required indicator is visible or not. NOTE: * Does not apply for all components!. *

* This method will throw a {@link IllegalStateException} if the component * state (returned by {@link #getState()}) does not inherit * {@link AbstractFieldState}. * * @return true if visible, false if not * @see #setRequiredIndicatorVisible(boolean) * @since 8.0 */ protected boolean isRequiredIndicatorVisible() { if (getState(false) instanceof AbstractFieldState) { return ((AbstractFieldState) getState(false)).required; } throw new IllegalStateException( "This component does not support the required indicator, since state is of type " + getStateType().getSimpleName() + " and does not inherit " + AbstractFieldState.class.getSimpleName()); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 11601 Content-Disposition: inline; filename="AbstractComponentContainer.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "1c0f8ad2c3703c695c47eea57a3be9a77c291c72" /* * Copyright 2000-2022 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.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import com.vaadin.server.ComponentSizeValidator; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.AbstractComponentContainerState; /** * 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 * @since 3.0 */ @SuppressWarnings("serial") public abstract class AbstractComponentContainer extends AbstractComponent implements ComponentContainer { /** * Constructs a new component container. */ public AbstractComponentContainer() { super(); } /* * (non-Javadoc) * * @see * com.vaadin.ui.ComponentContainer#addComponents(com.vaadin.ui.Component[]) */ @Override public void addComponents(Component... components) { for (Component c : components) { addComponent(c); } } /** * 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 l = new LinkedList<>(); // Adds all components for (final Iterator i = getComponentIterator(); i .hasNext();) { l.add(i.next()); } // Removes all component for (Component aL : l) { removeComponent(aL); } } /* * 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 components = new LinkedList<>(); for (final Iterator i = source.getComponentIterator(); i .hasNext();) { components.add(i.next()); } for (final Component c : components) { source.removeComponent(c); addComponent(c); } } /* documented in interface */ @Override public Registration addComponentAttachListener( ComponentAttachListener listener) { return addListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentAttachListener( ComponentAttachListener listener) { removeListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override public Registration addComponentDetachListener( ComponentDetachListener listener) { return addListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentDetachListener( ComponentDetachListener listener) { removeListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /** * 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) { // Make sure we're not adding the component inside it's own content if (isOrHasAncestor(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 AbstractSingleComponentContainer.removeFromParent(c); } c.setParent(this); fireComponentAttachEvent(c); markAsDirty(); } /** * 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 (equals(c.getParent())) { c.setParent(null); fireComponentDetachEvent(c); markAsDirty(); } } @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 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 invalidChildren, boolean childrenMayBecomeUndefined, boolean vertical) { if (childrenMayBecomeUndefined) { Collection previouslyInvalidComponents = invalidChildren; invalidChildren = getInvalidSizedChildren(vertical); if (previouslyInvalidComponents != null && invalidChildren != null) { for (Iterator 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 stillInvalidChildren = getInvalidSizedChildren( vertical); if (stillInvalidChildren != null) { for (Component component : stillInvalidChildren) { // didn't become valid invalidChildren.remove(component); } } } if (invalidChildren != null) { repaintChildTrees(invalidChildren); } } private Collection getInvalidSizedChildren( final boolean vertical) { HashSet components = null; for (Component component : this) { boolean valid = vertical ? ComponentSizeValidator.checkHeights(component) : ComponentSizeValidator.checkWidths(component); if (!valid) { if (components == null) { components = new HashSet<>(); } components.add(component); } } return components; } private void repaintChildTrees(Collection dirtyChildren) { for (Component c : dirtyChildren) { c.markAsDirtyRecursive(); } } @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 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); } /** * {@inheritDoc} * * @deprecated As of 7.0, use {@link #iterator()} instead. */ @Deprecated @Override public Iterator getComponentIterator() { return iterator(); } @Override protected AbstractComponentContainerState getState() { return (AbstractComponentContainerState) super.getState(); } @Override protected AbstractComponentContainerState getState(boolean markAsDirty) { return (AbstractComponentContainerState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 40143 Content-Disposition: inline; filename="AbstractDateField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "b76ee128e959e2535ca92fa63923f16f015f21ef" /* * Copyright 2000-2022 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.Type; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import org.jsoup.nodes.Element; import com.googlecode.gentyref.GenericTypeReflector; import com.vaadin.data.Result; import com.vaadin.data.ValidationResult; import com.vaadin.data.Validator; import com.vaadin.data.ValueContext; import com.vaadin.data.validator.RangeValidator; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.server.ErrorMessage; import com.vaadin.server.UserError; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.datefield.AbstractDateFieldServerRpc; import com.vaadin.shared.ui.datefield.AbstractDateFieldState; import com.vaadin.shared.ui.datefield.AbstractDateFieldState.AccessibleElement; import com.vaadin.shared.ui.datefield.DateResolution; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.util.TimeZoneUtil; import elemental.json.Json; /** * A date editor component with {@link LocalDate} as an input value. * * @author Vaadin Ltd * * @since 8.0 * * @param * type of date ({@code LocalDate} or {@code LocalDateTime}). * @param * resolution enumeration type * */ public abstract class AbstractDateField, R extends Enum> extends AbstractField implements FocusNotifier, BlurNotifier { private static final DateTimeFormatter RANGE_FORMATTER = DateTimeFormatter .ofPattern("yyyy-MM-dd[ HH:mm:ss]", Locale.ENGLISH); private AbstractDateFieldServerRpc rpc = new AbstractDateFieldServerRpc() { @Override public void update(String newDateString, Map resolutions) { valueUpdate(newDateString, resolutions); } @Override public void updateValueWithDelay(String newDateString, Map resolutions) { valueUpdate(newDateString, resolutions); } private void valueUpdate(String newDateString, Map resolutions) { Set resolutionNames = getResolutions().map(Enum::name) .collect(Collectors.toSet()); resolutionNames.retainAll(resolutions.keySet()); if (!isReadOnly() && (!resolutionNames.isEmpty() || newDateString != null)) { // Old and new dates final T oldDate = getValue(); T newDate; boolean hasChanges = false; if ("".equals(newDateString)) { newDate = null; } else { newDate = reconstructDateFromFields(resolutions, oldDate); } boolean parseErrorWasSet = currentErrorMessage != null; hasChanges |= !Objects.equals(dateString, newDateString) || !Objects.equals(oldDate, newDate) || parseErrorWasSet; if (hasChanges) { dateString = newDateString; currentErrorMessage = null; if (newDateString == null || newDateString.isEmpty()) { boolean valueChanged = setValue(newDate, true); if (!valueChanged && parseErrorWasSet) { doSetValue(newDate); } } else { // invalid date string if (resolutions.isEmpty()) { Result parsedDate = handleUnparsableDateString( dateString); // If handleUnparsableDateString returns the same // date as current, force update state to display // correct representation parsedDate.ifOk(v -> { if (!setValue(v, true) && !isDifferentValue(v)) { updateDiffstate("resolutions", Json.createObject()); doSetValue(v); } }); if (parsedDate.isError()) { dateString = null; currentErrorMessage = parsedDate.getMessage() .orElse("Parsing error"); if (!isDifferentValue(null)) { doSetValue(null); } else { setValue(null, true); } } } else { RangeValidator validator = getRangeValidator(); ValidationResult result = validator.apply(newDate, new ValueContext()); if (!isPreventInvalidInput() || !result.isError()) { setValue(newDate, true); } else { doSetValue(newDate); } } } } } } @Override public void focus() { fireEvent(new FocusEvent(AbstractDateField.this)); } @Override public void blur() { fireEvent(new BlurEvent(AbstractDateField.this)); } }; /** * The default start year (inclusive) from which to calculate the * daylight-saving time zone transition dates. */ private static final int DEFAULT_START_YEAR = 1980; /** * The default value of the number of future years from the current date for * which the daylight-saving time zone transition dates are calculated. */ private static final int DEFAULT_YEARS_FROM_NOW = 20; /** * The optional user-supplied start year (inclusive) from which to calculate * the daylight-saving time zone transition dates. */ private Integer startYear; /** * The optional user-supplied end year (inclusive) until which to calculate * the daylight-saving time zone transition dates. */ private Integer endYear; /** * Value of the field. */ private T value; /** * Default value of the field, displayed when nothing has been selected. * * @since 8.1.2 */ private T defaultValue; /** * Specified smallest modifiable unit for the date field. */ private R resolution; private ZoneId zoneId; private String dateString = ""; private String currentErrorMessage; private String defaultParseErrorMessage = "Date format not recognized"; private String dateOutOfRangeMessage = "Date is out of allowed range"; private boolean preventInvalidInput = false; /* Constructors */ /** * Constructs an empty {@code AbstractDateField} with no caption and * specified {@code resolution}. * * @param resolution * initial resolution for the field, not {@code null} */ public AbstractDateField(R resolution) { registerRpc(rpc); setResolution(resolution); } /** * Constructs an empty {@code AbstractDateField} with caption. * * @param caption * the caption of the datefield * @param resolution * initial resolution for the field, not {@code null} */ public AbstractDateField(String caption, R resolution) { this(resolution); setCaption(caption); } /** * Constructs a new {@code AbstractDateField} with the given caption and * initial text contents. * * @param caption * the caption {@code String} for the editor. * @param value * the date/time value. * @param resolution * initial resolution for the field, not {@code null} */ public AbstractDateField(String caption, T value, R resolution) { this(caption, resolution); setValue(value); } /* Component basic features */ @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); Locale locale = getLocale(); getState().locale = locale == null ? null : locale.toString(); } /** * Construct a date object from the individual field values received from * the client. * * @param resolutions * map of time unit (resolution) name and value, the key is the * resolution name e.g. "HOUR", "MINUTE", the value can be * {@code null} * @param oldDate * used as a fallback to get needed values if they are not * defined in the specified {@code resolutions} * * @return the date object built from the specified resolutions * @since 8.2 */ protected T reconstructDateFromFields(Map resolutions, T oldDate) { Map calendarFields = new HashMap<>(); for (R resolution : getResolutionsHigherOrEqualTo(getResolution())) { // Only handle what the client is allowed to send. The same // resolutions that are painted String resolutionName = resolution.name(); Integer newValue = resolutions.get(resolutionName); if (newValue == null) { newValue = getDatePart(oldDate, resolution); } calendarFields.put(resolution, newValue); } return buildDate(calendarFields); } /** * Sets the start range for this component. If the value is set before this * date (taking the resolution into account), the component will not * validate. If {@code startDate} is set to {@code null}, any value before * {@code endDate} will be accepted by the range *

* Note: Negative, i.e. BC dates are not supported. *

* Note: It's usually recommended to use only one of the following at the * same time: Range validator with Binder or DateField's setRangeStart * check. * * @param startDate * - the allowed range's start date */ public void setRangeStart(T startDate) { if (afterDate(startDate, convertFromDateString(getState().rangeEnd))) { throw new IllegalStateException( "startDate cannot be later than endDate"); } getState().rangeStart = convertToDateString(startDate); } /** * Sets the current error message if the range validation fails. * * @param dateOutOfRangeMessage * - Localizable message which is shown when value (the date) is * set outside allowed range */ public void setDateOutOfRangeMessage(String dateOutOfRangeMessage) { this.dateOutOfRangeMessage = dateOutOfRangeMessage; } /** * Returns current date-out-of-range error message. * * @see #setDateOutOfRangeMessage(String) * @return Current error message for dates out of range. */ public String getDateOutOfRangeMessage() { return dateOutOfRangeMessage; } /** * Gets the resolution. * * @return the date/time field resolution */ public R getResolution() { return resolution; } /** * Sets the resolution of the DateField. * * The default resolution is {@link DateResolution#DAY} since Vaadin 7.0. * * @param resolution * the resolution to set, not {@code null} */ public void setResolution(R resolution) { if (!resolution.equals(this.resolution)) { this.resolution = resolution; setValue(adjustToResolution(getValue(), resolution)); updateResolutions(); } } /** * Sets the end range for this component. If the value is set after this * date (taking the resolution into account), the component will not * validate. If {@code endDate} is set to {@code null}, any value after * {@code startDate} will be accepted by the range. *

* Note: It's usually recommended to use only one of the following at the * same time: Range validator with Binder or DateField's setRangeEnd check. * * @param endDate * the allowed range's end date (inclusive, based on the current * resolution) */ public void setRangeEnd(T endDate) { String date = convertToDateString(endDate); if (afterDate(convertFromDateString(getState().rangeStart), endDate)) { throw new IllegalStateException( "endDate cannot be earlier than startDate"); } getState().rangeEnd = date; } /** * Returns the precise rangeStart used. * * @return the precise rangeStart used, may be {@code null}. */ public T getRangeStart() { return convertFromDateString(getState(false).rangeStart); } /** * Parses string representation of date range limit into date type. * * @param temporalStr * the string representation * @return parsed value * @see AbstractDateFieldState#rangeStart * @see AbstractDateFieldState#rangeEnd * @since 8.4 */ protected T convertFromDateString(String temporalStr) { if (temporalStr == null) { return null; } return toType(RANGE_FORMATTER.parse(temporalStr)); } /** * Converts a temporal value into field-specific data type. * * @param temporalAccessor * - source value * @return conversion result. * @since 8.4 */ protected abstract T toType(TemporalAccessor temporalAccessor); /** * Converts date range limit into string representation. * * @param temporal * the value * @return textual representation * @see AbstractDateFieldState#rangeStart * @see AbstractDateFieldState#rangeEnd * @since 8.4 */ protected String convertToDateString(T temporal) { if (temporal == null) { return null; } return RANGE_FORMATTER.format(temporal); } /** * Checks if {@code value} is after {@code base} or not. * * @param value * temporal value * @param base * temporal value to compare to * @return {@code true} if {@code value} is after {@code base}, * {@code false} otherwise */ protected boolean afterDate(T value, T base) { if (value == null || base == null) { return false; } return value.compareTo(base) > 0; } /** * Returns the precise rangeEnd used. * * @return the precise rangeEnd used, may be {@code null}. */ public T getRangeEnd() { return convertFromDateString(getState(false).rangeEnd); } /** * Sets formatting used by some component implementations. See * {@link SimpleDateFormat} for format details. * * By default it is encouraged to used default formatting defined by Locale, * but due some JVM bugs it is sometimes necessary to use this method to * override formatting. See Vaadin issue #2200. * * @param dateFormat * the dateFormat to set, can be {@code null} * * @see com.vaadin.ui.AbstractComponent#setLocale(Locale)) */ public void setDateFormat(String dateFormat) { getState().format = dateFormat; } /** * Returns a format string used to format date value on client side or * {@code null} if default formatting from {@link Component#getLocale()} is * used. * * @return the dateFormat */ public String getDateFormat() { return getState(false).format; } /** * Sets the {@link ZoneId}, which is used when {@code z} is included inside * the {@link #setDateFormat(String)} . * * @param zoneId * the zone id * @since 8.2 */ public void setZoneId(ZoneId zoneId) { if (zoneId != this.zoneId || (zoneId != null && !zoneId.equals(this.zoneId))) { updateTimeZoneJSON(zoneId, getLocale(), getStartYear(), getEndYear()); } this.zoneId = zoneId; } private void updateTimeZoneJSON(ZoneId zoneId, Locale locale, int startYear, int endYear) { String timeZoneJSON; if (zoneId != null && locale != null) { timeZoneJSON = TimeZoneUtil.toJSON(zoneId, locale, startYear, endYear); } else { timeZoneJSON = null; } getState().timeZoneJSON = timeZoneJSON; } /** * Sets {@link startYear} and {@link endYear}: the start and end years (both * inclusive) between which to calculate the daylight-saving time zone * transition dates. Both parameters are used when '{@code z}' is included * inside the {@link #setDateFormat(String)}, they would have no effect * otherwise. Specifically, these parameters determine the range of years in * which zone names are are adjusted to show the daylight saving names. * * If no values are provided, by default {@link startYear} is set to * {@value #DEFAULT_START_YEAR}, and {@link endYear} is set to * {@value #DEFAULT_YEARS_FROM_NOW} years into the future from the current * date. * * @param startYear * the start year of DST transitions * @param endYear * the end year of DST transitions * @since 8.11 */ public void setDaylightSavingTimeRange(int startYear, int endYear) { if (startYear > endYear) { throw new IllegalArgumentException( "The start year from which to begin calculating the " + "daylight-saving time zone transition dates must" + " be less than or equal to the end year.\n" + startYear + " is greater than " + endYear); } if (this.startYear == null || this.endYear == null || startYear != this.startYear || endYear != this.endYear) { updateTimeZoneJSON(getZoneId(), getLocale(), startYear, endYear); } this.startYear = startYear; this.endYear = endYear; } private int getStartYear() { if (startYear == null) { return DEFAULT_START_YEAR; } else { return startYear; } } private int getEndYear() { if (endYear == null) { return LocalDate.now().getYear() + DEFAULT_YEARS_FROM_NOW; } else { return endYear; } } @Override public void setLocale(Locale locale) { Locale oldLocale = getLocale(); if (locale != oldLocale || (locale != null && !locale.equals(oldLocale))) { updateTimeZoneJSON(getZoneId(), locale, getStartYear(), getEndYear()); } super.setLocale(locale); } private void updateResolutions() { final T currentDate = getValue(); Map resolutions = getState().resolutions; resolutions.clear(); // Only paint variables for the resolution and up, e.g. Resolution DAY // paints DAY,MONTH,YEAR for (R resolution : getResolutionsHigherOrEqualTo(getResolution())) { String resolutionName = resolution.name(); Integer value = getValuePart(currentDate, resolution); resolutions.put(resolutionName, value); Integer defaultValuePart = getValuePart(defaultValue, resolution); resolutions.put("default-" + resolutionName, defaultValuePart); } updateDiffstate("resolutions", Json.createObject()); } private Integer getValuePart(T date, R resolution) { if (date == null) { return null; } return getDatePart(date, resolution); } /** * Returns the {@link ZoneId}, which is used when {@code z} is included * inside the {@link #setDateFormat(String)}. * * @return the zoneId * @since 8.2 */ public ZoneId getZoneId() { return zoneId; } /** * Specifies whether or not date/time interpretation in component is to be * lenient. * * @see Calendar#setLenient(boolean) * @see #isLenient() * * @param lenient * true if the lenient mode is to be turned on; false if it is to * be turned off. */ public void setLenient(boolean lenient) { getState().lenient = lenient; } /** * Returns whether date/time interpretation is lenient. * * @see #setLenient(boolean) * * @return {@code true} if the interpretation mode of this calendar is * lenient; {@code false} otherwise. */ public boolean isLenient() { return getState(false).lenient; } @Override public T getValue() { return value; } /** * Returns the current default value. * * @see #setDefaultValue(Temporal) * @return the default value * @since 8.1.2 */ public T getDefaultValue() { return defaultValue; } /** * Sets the default value for the field. The default value is the starting * point for the date field when nothing has been selected yet. If no * default value is set, current date/time is used. * * @param defaultValue * the default value, may be {@code null} * @since 8.1.2 */ public void setDefaultValue(T defaultValue) { this.defaultValue = defaultValue; updateResolutions(); } /** * Sets the value of this object. If the new value is not equal to * {@code getValue()}, fires a {@link ValueChangeEvent} . * * @param value * the new value, may be {@code null} * @throws IllegalArgumentException * if the value is not within range bounds */ @Override public void setValue(T value) { T adjusted = adjustToResolution(value, getResolution()); RangeValidator validator = getRangeValidator(); ValidationResult result = validator.apply(adjusted, new ValueContext(this, this)); if (result.isError()) { throw new IllegalArgumentException( "value is not within acceptable range"); } else { currentErrorMessage = null; /* * First handle special case when the client side component has a * date string but value is null (e.g. unparsable date string typed * in by the user). No value changes should happen, but we need to * do some internal housekeeping. */ if (adjusted == null && !getState(false).parsable) { /* * Side-effects of doSetValue clears possible previous strings * and flags about invalid input. */ doSetValue(null); markAsDirty(); return; } super.setValue(adjusted); } } /** * Adjusts the given date to the given resolution. Any values that are more * specific than the given resolution are truncated to their default values. * * @param date * the date to adjust, can be {@code null} * @param resolution * the resolution to be used in the adjustment, can be * {@code null} * @return an adjusted date that matches the given resolution, or * {@code null} if the given date, resolution, or both were * {@code null} */ protected abstract T adjustToResolution(T date, R resolution); /** * Checks whether ISO 8601 week numbers are shown in the date selector. * * @return true if week numbers are shown, false otherwise. */ public boolean isShowISOWeekNumbers() { return getState(false).showISOWeekNumbers; } /** * Sets the visibility of ISO 8601 week numbers in the date selector. ISO * 8601 defines that a week always starts with a Monday so the week numbers * are only shown if this is the case. * * @param showWeekNumbers * true if week numbers should be shown, false otherwise. */ public void setShowISOWeekNumbers(boolean showWeekNumbers) { getState().showISOWeekNumbers = showWeekNumbers; } /** * Return the error message that is shown if the user inputted value can't * be parsed into a Date object. If * {@link #handleUnparsableDateString(String)} is overridden and it throws a * custom exception, the message returned by * {@link Exception#getLocalizedMessage()} will be used instead of the value * returned by this method. * * @see #setParseErrorMessage(String) * * @return the error message that the DateField uses when it can't parse the * textual input from user to a Date object */ public String getParseErrorMessage() { return defaultParseErrorMessage; } /** * Sets the default error message used if the DateField cannot parse the * text input by user to a Date field. Note that if the * {@link #handleUnparsableDateString(String)} method is overridden, the * localized message from its exception is used. * * @param parsingErrorMessage * the default parsing error message * * @see #getParseErrorMessage() * @see #handleUnparsableDateString(String) */ public void setParseErrorMessage(String parsingErrorMessage) { defaultParseErrorMessage = parsingErrorMessage; } @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } @Override @SuppressWarnings("unchecked") public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); if (design.hasAttr("value") && !design.attr("value").isEmpty()) { Type dateType = GenericTypeReflector.getTypeParameter(getClass(), AbstractDateField.class.getTypeParameters()[0]); if (dateType instanceof Class) { Class clazz = (Class) dateType; T date = (T) DesignAttributeHandler.getFormatter() .parse(design.attr("value"), clazz); // formatting will return null if it cannot parse the string if (date == null) { Logger.getLogger(AbstractDateField.class.getName()) .info("cannot parse " + design.attr("value") + " as date"); } doSetValue(adjustToResolution(date, getResolution())); } else { throw new RuntimeException("Cannot detect resoluton type " + Optional.ofNullable(dateType).map(Type::getTypeName) .orElse(null)); } } } /** * Formats date according to the components locale. * * @param value * the date or {@code null} * @return textual representation of the date or empty string for * {@code null} * @since 8.1.1 */ protected abstract String formatDate(T value); @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); if (getValue() != null) { design.attr("value", DesignAttributeHandler.getFormatter().format(getValue())); } } /** * This method is called to handle a non-empty date string from the client * if the client could not parse it as a Date. * * By default, an error result is returned whose error message is * {@link #getParseErrorMessage()}. * * This can be overridden to handle conversions, to return a result with * {@code null} value (equivalent to empty input) or to return a custom * error. * * @param dateString * date string to handle * @return result that contains parsed Date as a value or an error */ protected Result handleUnparsableDateString(String dateString) { return Result.error(getParseErrorMessage()); } @Override protected AbstractDateFieldState getState() { return (AbstractDateFieldState) super.getState(); } @Override protected AbstractDateFieldState getState(boolean markAsDirty) { return (AbstractDateFieldState) super.getState(markAsDirty); } @Override protected void doSetValue(T value) { // Also set the internal dateString this.value = value; if (value == null) { value = getEmptyValue(); } dateString = formatDate(value); // TODO move range check to internal validator? RangeValidator validator = getRangeValidator(); ValidationResult result = validator.apply(value, new ValueContext(this, this)); if (result.isError()) { currentErrorMessage = getDateOutOfRangeMessage(); } getState().parsable = currentErrorMessage == null; ErrorMessage errorMessage; if (currentErrorMessage == null) { errorMessage = null; } else { errorMessage = new UserError(currentErrorMessage); } setComponentError(errorMessage); updateResolutions(); } /** * Returns a date integer value part for the given {@code date} for the * given {@code resolution}. * * @param date * the given date, can be {@code null} * @param resolution * the resolution to extract a value from the date by, not * {@code null} * @return the integer value part of the date by the given resolution */ protected abstract int getDatePart(T date, R resolution); /** * Builds date by the given {@code resolutionValues} which is a map whose * keys are resolution and integer values. *

* This is the opposite to {@link #getDatePart(Temporal, Enum)}. * * @param resolutionValues * date values to construct a date * @return date built from the given map of date values */ protected abstract T buildDate(Map resolutionValues); /** * Returns a custom date range validator which is applicable for the type * {@code T}. * * @return the date range validator */ protected abstract RangeValidator getRangeValidator(); /** * Converts {@link Date} to date type {@code T}. * * @param date * a date to convert * @return object of type {@code T} representing the {@code date} */ protected abstract T convertFromDate(Date date); /** * Converts the object of type {@code T} to {@link Date}. *

* This is the opposite to {@link #convertFromDate(Date)}. * * @param date * the date of type {@code T} * @return converted date of type {@code Date} */ protected abstract Date convertToDate(T date); @SuppressWarnings("unchecked") private Stream getResolutions() { Type resolutionType = GenericTypeReflector.getTypeParameter(getClass(), AbstractDateField.class.getTypeParameters()[1]); if (resolutionType instanceof Class) { Class clazz = (Class) resolutionType; return Stream.of(clazz.getEnumConstants()) .map(object -> (R) object); } throw new RuntimeException("Cannot detect resoluton type " + Optional.ofNullable(resolutionType).map(Type::getTypeName) .orElse(null)); } private Iterable getResolutionsHigherOrEqualTo(R resolution) { return getResolutions().skip(resolution.ordinal()) .collect(Collectors.toList()); } @Override public Validator getDefaultValidator() { return new Validator() { @Override public ValidationResult apply(T value, ValueContext context) { // currentErrorMessage contains two type of messages, one is // DateOutOfRangeMessage and the other one is the ParseError if (currentErrorMessage != null) { if (currentErrorMessage .equals(getDateOutOfRangeMessage())) { // if the currentErrorMessage is DateOutOfRangeMessage, // then need to double check whether the error message // has been updated, that is because of #11276. ValidationResult validationResult = getRangeValidator() .apply(value, context); if (validationResult.isError()) { return ValidationResult.error(currentErrorMessage); } } else { // if the current Error is parsing error, pass it to the // ValidationResult return ValidationResult.error(currentErrorMessage); } } // Pass to range validator. return getRangeValidator().apply(value, context); } }; } /** *

* Sets a custom style name for the given date's calendar cell. Setting the * style name will override any previous style names that have been set for * that date, but can contain several actual style names separated by space. * Setting the custom style name {@code null} will only remove the previous * custom style name. *

*

* This logic is entirely separate from {@link #setStyleName(String)} *

*

* Usage examples:
* {@code setDateStyle(LocalDate.now(), "teststyle");}
* {@code setDateStyle(LocalDate.now(), "teststyle1 teststyle2");} *

* * @param date * which date cell to modify, not {@code null} * @param styleName * the custom style name(s) for given date, {@code null} to clear * custom style name(s) * * @since 8.3 */ public void setDateStyle(LocalDate date, String styleName) { Objects.requireNonNull(date, "Date cannot be null"); if (styleName != null) { getState().dateStyles.put(date.toString(), styleName); } else { getState().dateStyles.remove(date.toString()); } } /** * Returns the custom style name that corresponds with the given date's * calendar cell. * * @param date * which date cell's custom style name(s) to return, not * {@code null} * @return the corresponding style name(s), if any, {@code null} otherwise * * @see #setDateStyle(LocalDate, String) * @since 8.3 */ public String getDateStyle(LocalDate date) { Objects.requireNonNull(date, "Date cannot be null"); return getState(false).dateStyles.get(date.toString()); } /** * Returns a map from dates to custom style names in each date's calendar * cell. * * @return unmodifiable map from dates to custom style names in each date's * calendar cell * * @see #setDateStyle(LocalDate, String) * @since 8.3 */ public Map getDateStyles() { HashMap hashMap = new HashMap<>(); for (Entry entry : getState(false).dateStyles .entrySet()) { hashMap.put(LocalDate.parse(entry.getKey()), entry.getValue()); } return Collections.unmodifiableMap(hashMap); } /** * Sets the assistive label for a calendar navigation element. This sets the * {@code aria-label} attribute for the element which is used by screen * reading software. * * @param element * the element for which to set the label. Not {@code null}. * @param label * the assistive label to set * @since 8.4 */ public void setAssistiveLabel(AccessibleElement element, String label) { Objects.requireNonNull(element, "Element cannot be null"); getState().assistiveLabels.put(element, label); } /** * Gets the assistive label of a calendar navigation element. * * @param element * the element of which to get the assistive label * @since 8.4 */ public void getAssistiveLabel(AccessibleElement element) { getState(false).assistiveLabels.get(element); } /** * Control whether value change event is emitted when user input value does * not meet the integrated range validator. * * @param preventInvalidInput * Set to false to disable the value change event. * * @since 8.13 */ public void setPreventInvalidInput(boolean preventInvalidInput) { this.preventInvalidInput = preventInvalidInput; } /** * Check whether value change is emitted when user input value does not meet * integrated range validator. The default is false. * * @return a Boolean value * * @since 8.13 */ public boolean isPreventInvalidInput() { return preventInvalidInput; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 3475 Content-Disposition: inline; filename="AbstractEmbedded.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "a0647b4382c2935f0cb9df5ba91f82978ad1102a" /* * Copyright 2000-2022 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.util.Collection; import org.jsoup.nodes.Element; import com.vaadin.server.Resource; import com.vaadin.shared.ui.AbstractEmbeddedState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * Abstract base for embedding components. * * @author Vaadin Ltd. * @since 7.0 */ @SuppressWarnings("serial") public abstract class AbstractEmbedded extends AbstractComponent { @Override protected AbstractEmbeddedState getState() { return (AbstractEmbeddedState) super.getState(); } @Override protected AbstractEmbeddedState getState(boolean markAsDirty) { return (AbstractEmbeddedState) super.getState(markAsDirty); } /** * Sets the object source resource. The dimensions are assumed if possible. * The type is guessed from resource. * * @param source * the source to set. */ public void setSource(Resource source) { setResource(AbstractEmbeddedState.SOURCE_RESOURCE, source); } /** * Get the object source resource. * * @return the source */ public Resource getSource() { return getResource(AbstractEmbeddedState.SOURCE_RESOURCE); } /** * Sets this component's alternate text that can be presented instead of the * component's normal content for accessibility purposes. * * @param altText * A short, human-readable description of this component's * content. */ public void setAlternateText(String altText) { getState().alternateText = altText; } /** * Gets this component's alternate text that can be presented instead of the * component's normal content for accessibility purposes. * * @returns Alternate text */ public String getAlternateText() { return getState(false).alternateText; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); if (design.hasAttr("alt")) { setAlternateText(DesignAttributeHandler.readAttribute("alt", design.attributes(), String.class)); } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); AbstractEmbedded def = designContext.getDefaultInstance(this); DesignAttributeHandler.writeAttribute("alt", design.attributes(), getAlternateText(), def.getAlternateText(), String.class, designContext); } @Override protected Collection getCustomAttributes() { Collection c = super.getCustomAttributes(); c.add("alternate-text"); c.add("alt"); return c; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 7631 Content-Disposition: inline; filename="AbstractField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "e0e73f400f4c8e795d7f54ed665e8f221efbb22d" /* * Copyright 2000-2022 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.util.Collection; import java.util.Objects; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.data.HasValue; import com.vaadin.shared.AbstractFieldState; import com.vaadin.shared.Registration; import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * An abstract implementation of a field, or a {@code Component} allowing user * input. Implements {@link HasValue} to represent the input value. Examples of * typical field components include text fields, date pickers, and check boxes. *

* This class replaces the Vaadin 7 {@code com.vaadin.ui.AbstractField} class. * The old {@code AbstractField} is retained, under the new name * {@link com.vaadin.v7.ui.AbstractField}, for compatibility and migration * purposes. * * @author Vaadin Ltd. * @since 8.0 * * @param * the input value type */ public abstract class AbstractField extends AbstractComponent implements HasValue, Focusable { @Override public void setValue(T value) { setValue(value, false); } @Override public boolean isReadOnly() { return super.isReadOnly(); } /** * {@inheritDoc} *

* The server ignores (potentially forged) value change requests from the * client to fields that are read-only. Programmatically changing the field * value via {@link #setValue(T)} is still possible. *

* The read-only mode is distinct from the * {@linkplain Component#setEnabled(boolean) disabled} state. When disabled, * a component cannot be interacted with at all, and its content should be * considered irrelevant or not applicable. In contrast, the user should * still be able to read the content and otherwise interact with a read-only * field even though changing the value is disallowed. * * @param readOnly * {@code true} to set read-only mode, {@code false} otherwise. */ @Override public void setReadOnly(boolean readOnly) { super.setReadOnly(readOnly); } @Override public Registration addValueChangeListener( ValueChangeListener listener) { return addListener(ValueChangeEvent.class, listener, ValueChangeListener.VALUE_CHANGE_METHOD); } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); Attributes attr = design.attributes(); if (attr.hasKey("readonly")) { setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr, Boolean.class)); } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); AbstractField def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("readonly", attr, isReadOnly(), def.isReadOnly(), Boolean.class, designContext); } @Override protected Collection getCustomAttributes() { Collection attributes = super.getCustomAttributes(); attributes.add("readonly"); // must be handled by subclasses attributes.add("value"); return attributes; } /** * Sets the value of this field if it has changed and fires a value change * event. If the value originates from the client and this field is * read-only, does nothing. Invokes {@link #doSetValue(Object) doSetValue} * to actually store the value. * * @param value * the new value to set * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. * @return true if the value was updated, false * otherwise */ protected boolean setValue(T value, boolean userOriginated) { if (userOriginated && isReadOnly()) { return false; } if (!isDifferentValue(value)) { return false; } T oldValue = this.getValue(); doSetValue(value); if (!userOriginated) { markAsDirty(); } fireEvent(createValueChange(oldValue, userOriginated)); return true; } /** * Called when a new value is set to determine whether the provided new * value is considered to be a change compared to the current value. This is * used to determine whether {@link #doSetValue(Object)} should be called * and a value change event fired. * * @param newValue * the new value candidate to check, may be null * * @return true if the provided value is considered to be * different and a value change event should be fired; * false if the values are considered to be the same * and no value change should be fired */ protected boolean isDifferentValue(T newValue) { return !Objects.equals(newValue, getValue()); } /** * Sets the value of this field. May do sanitization or throw * {@code IllegalArgumentException} if the value is invalid. Typically saves * the value to shared state. * * @param value * the new value of the field * @throws IllegalArgumentException * if the value is invalid */ protected abstract void doSetValue(T value); /** * Returns a new value change event instance. * * @param oldValue * the value of this field before this value change event * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. * @return the new event */ protected ValueChangeEvent createValueChange(T oldValue, boolean userOriginated) { return new ValueChangeEvent<>(this, oldValue, userOriginated); } @Override protected AbstractFieldState getState() { return (AbstractFieldState) super.getState(); } @Override protected AbstractFieldState getState(boolean markAsDirty) { return (AbstractFieldState) super.getState(markAsDirty); } @Override public void focus() { super.focus(); } @Override public int getTabIndex() { return getState(false).tabIndex; } @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } @Override public void setRequiredIndicatorVisible(boolean visible) { super.setRequiredIndicatorVisible(visible); } @Override public boolean isRequiredIndicatorVisible() { return super.isRequiredIndicatorVisible(); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2493 Content-Disposition: inline; filename="AbstractFocusable.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "a23f3ec13a8a359d1c86625fe6d037872d318bc6" /* * Copyright 2000-2022 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 com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.TabIndexState; import com.vaadin.ui.Component.Focusable; /** * An abstract base class for focusable components. Includes API for setting the * tab index, programmatic focusing, and adding focus and blur listeners. * * @since 7.6 * @author Vaadin Ltd */ public abstract class AbstractFocusable extends AbstractComponent implements Focusable, FocusNotifier, BlurNotifier { protected AbstractFocusable() { registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); } @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } @Override public void focus() { super.focus(); } @Override public int getTabIndex() { return getState(false).tabIndex; } @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } @Override protected TabIndexState getState() { return (TabIndexState) super.getState(); } @Override protected TabIndexState getState(boolean markAsDirty) { return (TabIndexState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 11178 Content-Disposition: inline; filename="AbstractJavaScriptComponent.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "c5949345d2f1d337acac07a30bed5ed892cc33d3" /* * Copyright 2000-2022 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 com.vaadin.server.JavaScriptCallbackHelper; import com.vaadin.server.JsonCodec; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.ui.JavaScriptComponentState; import elemental.json.Json; import elemental.json.JsonValue; /** * Base class for Components with all client-side logic implemented using * JavaScript. *

* When a new JavaScript component is initialized in the browser, the framework * will look for a globally defined JavaScript function that will initialize the * component. The name of the initialization function is formed by replacing . * with _ in the name of the server-side class. If no such function is defined, * each super class is used in turn until a match is found. The framework will * thus first attempt with com_example_MyComponent for the * server-side * com.example.MyComponent extends AbstractJavaScriptComponent * class. If MyComponent instead extends com.example.SuperComponent * , then com_example_SuperComponent will also be attempted if * com_example_MyComponent has not been defined. *

* JavaScript components have a very simple GWT widget ( * {@link com.vaadin.client.ui.JavaScriptWidget} ) just consisting of a single * element (a div by default) to which the JavaScript code should * initialize its own user interface. The tag can be overridden by defining a * string named com_example_MyComponent.tag. If no tag has been * defined, a tag defined in a super class will be located in the same manner as * with the init function. *

* For example, to create a component ({@code my.package.Span}) with the DOM * {@code some text}, taking the {@code span} text from the state, * the JavaScript would be: * *

 * 
my_package_Span = function() {
  this.onStateChange = function() {
    this.getElement().innerText = this.getState().text;
  }
}
my_package_Span.tag = "span";

 * 
* *

* The initialization function will be called with this pointing to * a connector wrapper object providing integration to Vaadin. Please note that * in JavaScript, this is not necessarily defined inside callback * functions and it might therefore be necessary to assign the reference to a * separate variable, e.g. var self = this;. The following * functions are provided by the connector wrapper object: *

    *
  • getConnectorId() - returns a string with the id of the * connector.
  • *
  • getParentId([connectorId]) - returns a string with the id of * the connector's parent. If connectorId is provided, the id of * the parent of the corresponding connector with the passed id is returned * instead.
  • *
  • getElement([connectorId]) - returns the DOM Element that is * the root of a connector's widget. null is returned if the * connector can not be found or if the connector doesn't have a widget. If * connectorId is not provided, the connector id of the current * connector will be used.
  • *
  • getState() - returns an object corresponding to the shared * state defined on the server. The scheme for conversion between Java and * JavaScript types is described bellow.
  • *
  • registerRpc([name, ] rpcObject) - registers the * rpcObject as a RPC handler. rpcObject should be an * object with field containing functions for all eligible RPC functions. If * name is provided, the RPC handler will only used for RPC calls * for the RPC interface with the same fully qualified Java name. If no * name is provided, the RPC handler will be used for all incoming * RPC invocations where the RPC method name is defined as a function field in * the handler. The scheme for conversion between Java types in the RPC * interface definition and the JavaScript values passed as arguments to the * handler functions is described bellow.
  • *
  • getRpcProxy([name]) - returns an RPC proxy object. If * name is provided, the proxy object will contain functions for * all methods in the RPC interface with the same fully qualified name, provided * a RPC handler has been registered by the server-side code. If no * name is provided, the returned RPC proxy object will contain * functions for all methods in all RPC interfaces registered for the connector * on the server. If the same method name is present in multiple registered RPC * interfaces, the corresponding function in the RPC proxy object will throw an * exception when called. The scheme for conversion between Java types in the * RPC interface and the JavaScript values that should be passed to the * functions is described bellow.
  • *
  • translateVaadinUri(uri) - Translates a Vaadin URI to a URL * that can be used in the browser. This is just way of accessing * {@link com.vaadin.client.ApplicationConnection#translateVaadinUri(String)}
  • *
  • addResizeListener(element, callbackFunction) - Registers a * listener that gets notified whenever the size of the provided element * changes. The listener is called with one parameter: an event object with the * element property pointing to the element that has been resized. *
  • removeResizeListener(element, callbackFunction) - * Unregisters a combination of an element and a listener that has previously * been registered using addResizeListener. All registered * listeners are automatically unregistered when this connector is unregistered, * but this method can be use to to unregister a listener at an earlier point in * time. *
* The connector wrapper also supports these special functions: *
    *
  • onStateChange - If the JavaScript code assigns a function to * the field, that function is called whenever the contents of the shared state * is changed.
  • *
  • onUnregister - If the JavaScript code assigns a function to * the field, that function is called when the connector has been * unregistered.
  • *
  • Any field name corresponding to a call to * {@link #addFunction(String, JavaScriptFunction)} on the server will * automatically be present as a function that triggers the registered function * on the server.
  • *
  • Any field name referred to using {@link #callFunction(String, Object...)} * on the server will be called if a function has been assigned to the * field.
  • *
*

* * Values in the Shared State and in RPC calls are converted between Java and * JavaScript using the following conventions: *

    *
  • Primitive Java numbers (byte, char, int, long, float, double) and their * boxed types (Byte, Character, Integer, Long, Float, Double) are represented * by JavaScript numbers.
  • *
  • The primitive Java boolean and the boxed Boolean are represented by * JavaScript booleans.
  • *
  • Java Strings are represented by JavaScript strings.
  • *
  • Java Dates are represented by JavaScript numbers containing the timestamp *
  • *
  • List, Set and all arrays in Java are represented by JavaScript * arrays.
  • *
  • Map<String, ?> in Java is represented by JavaScript object with * fields corresponding to the map keys.
  • *
  • Any other Java Map is represented by a JavaScript array containing two * arrays, the first contains the keys and the second contains the values in the * same order.
  • *
  • A Java Bean is represented by a JavaScript object with fields * corresponding to the bean's properties.
  • *
  • A Java Connector is represented by a JavaScript string containing the * connector's id.
  • *
  • A pluggable serialization mechanism is provided for types not described * here. Please refer to the documentation for specific types for serialization * information.
  • *
* * @author Vaadin Ltd * @since 7.0.0 */ public abstract class AbstractJavaScriptComponent extends AbstractComponent { private final JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( this); @Override protected void registerRpc(T implementation, Class rpcInterfaceType) { super.registerRpc(implementation, rpcInterfaceType); callbackHelper.registerRpc(rpcInterfaceType); } /** * Register a {@link JavaScriptFunction} that can be called from the * JavaScript using the provided name. A JavaScript function with the * provided name will be added to the connector wrapper object (initially * available as this). Calling that JavaScript function will * cause the call method in the registered {@link JavaScriptFunction} to be * invoked with the same arguments. * * @param functionName * the name that should be used for client-side function * @param function * the {@link JavaScriptFunction} object that will be invoked * when the JavaScript function is called */ protected void addFunction(String functionName, JavaScriptFunction function) { callbackHelper.registerCallback(functionName, function); } /** * Invoke a named function that the connector JavaScript has added to the * JavaScript connector wrapper object. The arguments can be any boxed * primitive type, String, {@link JsonValue} or arrays of any other * supported type. Complex types (e.g. List, Set, Map, Connector or any * JavaBean type) must be explicitly serialized to a {@link JsonValue} * before sending. This can be done either with * {@link JsonCodec#encode(Object, JsonValue, java.lang.reflect.Type, com.vaadin.ui.ConnectorTracker)} * or using the factory methods in {@link Json}. * * @param name * the name of the function * @param arguments * function arguments */ protected void callFunction(String name, Object... arguments) { callbackHelper.invokeCallback(name, arguments); } @Override protected JavaScriptComponentState getState() { return (JavaScriptComponentState) super.getState(); } @Override protected JavaScriptComponentState getState(boolean markAsDirty) { return (JavaScriptComponentState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5133 Content-Disposition: inline; filename="AbstractLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "a2a376f0d4b7ca1814862061e7eb43876f45bdee" /* * Copyright 2000-2022 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 org.jsoup.nodes.Element; import com.vaadin.shared.ui.AbstractLayoutState; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * An abstract class that defines default implementation for the {@link Layout} * interface. * * @author Vaadin Ltd. * @since 5.0 */ public abstract class AbstractLayout extends AbstractComponentContainer implements Layout { @Override protected AbstractLayoutState getState() { return (AbstractLayoutState) super.getState(); } @Override protected AbstractLayoutState getState(boolean markAsDirty) { return (AbstractLayoutState) super.getState(markAsDirty); } /** * Reads margin attributes from a design into a MarginInfo object. This * helper method should be called from the * {@link #readDesign(Element, DesignContext) readDesign} method of layouts * that implement {@link MarginHandler}. * * @since 7.5 * * @param design * the design from which to read * @param defMargin * the default margin state for edges that are not set in the * design * @param context * the DesignContext instance used for parsing the design * @return the margin info */ protected MarginInfo readMargin(Element design, MarginInfo defMargin, DesignContext context) { if (design.hasAttr("margin")) { boolean margin = DesignAttributeHandler.readAttribute("margin", design.attributes(), boolean.class); return new MarginInfo(margin); } else { boolean left = DesignAttributeHandler.readAttribute("margin-left", design.attributes(), defMargin.hasLeft(), boolean.class); boolean right = DesignAttributeHandler.readAttribute("margin-right", design.attributes(), defMargin.hasRight(), boolean.class); boolean top = DesignAttributeHandler.readAttribute("margin-top", design.attributes(), defMargin.hasTop(), boolean.class); boolean bottom = DesignAttributeHandler.readAttribute( "margin-bottom", design.attributes(), defMargin.hasBottom(), boolean.class); return new MarginInfo(top, right, bottom, left); } } /** * Writes margin attributes from a MarginInfo object to a design. This * helper method should be called from the * {@link #readDesign(Element, DesignContext) writeDesign} method of layouts * that implement {@link MarginHandler}. * * * @since 7.5 * * @param design * the design to write to * @param margin * the margin state to write * @param defMargin * the default margin state to compare against * @param context * the DesignContext instance used for parsing the design */ protected void writeMargin(Element design, MarginInfo margin, MarginInfo defMargin, DesignContext context) { if (defMargin.getBitMask() == margin.getBitMask()) { // Default, no need to write } else if (margin.hasNone()) { // Write "margin='false'" DesignAttributeHandler.writeAttribute("margin", design.attributes(), false, true, boolean.class, context); } else if (margin.hasAll()) { // Write "margin" DesignAttributeHandler.writeAttribute("margin", design.attributes(), true, false, boolean.class, context); } else { DesignAttributeHandler.writeAttribute("margin-left", design.attributes(), margin.hasLeft(), defMargin.hasLeft(), boolean.class, context); DesignAttributeHandler.writeAttribute("margin-right", design.attributes(), margin.hasRight(), defMargin.hasRight(), boolean.class, context); DesignAttributeHandler.writeAttribute("margin-top", design.attributes(), margin.hasTop(), defMargin.hasTop(), boolean.class, context); DesignAttributeHandler.writeAttribute("margin-bottom", design.attributes(), margin.hasBottom(), defMargin.hasBottom(), boolean.class, context); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17591 Content-Disposition: inline; filename="AbstractListing.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4d6f51838db2aad9b9df58594360b01769ae9564" /* * Copyright 2000-2022 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.util.Objects; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.data.HasDataProvider; import com.vaadin.data.HasFilterableDataProvider; import com.vaadin.data.HasItems; import com.vaadin.data.provider.DataCommunicator; import com.vaadin.data.provider.DataGenerator; import com.vaadin.data.provider.DataProvider; import com.vaadin.data.provider.Query; import com.vaadin.server.AbstractExtension; import com.vaadin.server.Resource; import com.vaadin.server.SerializableConsumer; import com.vaadin.shared.extension.abstractlisting.AbstractListingExtensionState; import com.vaadin.shared.ui.abstractlisting.AbstractListingState; import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; import com.vaadin.ui.declarative.DesignFormatter; /** * A base class for listing components. Provides common handling for fetching * backend data items, selection logic, and server-client communication. *

* Note: concrete component implementations should implement * the {@link HasDataProvider} or {@link HasFilterableDataProvider} interface. * * @author Vaadin Ltd. * @since 8.0 * * @param * the item data type * */ public abstract class AbstractListing extends AbstractComponent implements Focusable, HasItems { /** * The item icon caption provider. */ private ItemCaptionGenerator itemCaptionGenerator = String::valueOf; /** * The item icon provider. It is up to the implementing class to support * this or not. */ private IconGenerator itemIconGenerator = item -> null; /** * A helper base class for creating extensions for Listing components. This * class provides helpers for accessing the underlying parts of the * component and its communication mechanism. * * @param * the listing item type */ public abstract static class AbstractListingExtension extends AbstractExtension implements DataGenerator { /** * Adds this extension to the given parent listing. * * @param listing * the parent component to add to */ public void extend(AbstractListing listing) { super.extend(listing); listing.addDataGenerator(this); } @Override public void remove() { getParent().removeDataGenerator(this); super.remove(); } /** * Gets a data object based on its client-side identifier key. * * @param key * key for data object * @return the data object */ protected T getData(String key) { return getParent().getDataCommunicator().getKeyMapper().get(key); } @Override @SuppressWarnings("unchecked") public AbstractListing getParent() { return (AbstractListing) super.getParent(); } /** * A helper method for refreshing the client-side representation of a * single data item. * * @param item * the item to refresh */ protected void refresh(T item) { getParent().getDataCommunicator().refresh(item); } @Override protected AbstractListingExtensionState getState() { return (AbstractListingExtensionState) super.getState(); } @Override protected AbstractListingExtensionState getState(boolean markAsDirty) { return (AbstractListingExtensionState) super.getState(markAsDirty); } } private final DataCommunicator dataCommunicator; /** * Creates a new {@code AbstractListing} with a default data communicator. *

*/ protected AbstractListing() { this(new DataCommunicator<>()); } /** * Creates a new {@code AbstractListing} with the given custom data * communicator. *

* Note: This method is for creating an * {@code AbstractListing} with a custom communicator. In the common case * {@link AbstractListing#AbstractListing()} should be used. *

* * @param dataCommunicator * the data communicator to use, not null */ protected AbstractListing(DataCommunicator dataCommunicator) { Objects.requireNonNull(dataCommunicator, "dataCommunicator cannot be null"); this.dataCommunicator = dataCommunicator; addExtension(dataCommunicator); } protected void internalSetDataProvider(DataProvider dataProvider) { internalSetDataProvider(dataProvider, null); } protected SerializableConsumer internalSetDataProvider( DataProvider dataProvider, F initialFilter) { return getDataCommunicator().setDataProvider(dataProvider, initialFilter); } protected DataProvider internalGetDataProvider() { return getDataCommunicator().getDataProvider(); } /** * Gets the item caption generator that is used to produce the strings shown * in the combo box for each item. * * @return the item caption generator used, not null */ protected ItemCaptionGenerator getItemCaptionGenerator() { return itemCaptionGenerator; } /** * Sets the item caption generator that is used to produce the strings shown * in the combo box for each item. By default, * {@link String#valueOf(Object)} is used. * * @param itemCaptionGenerator * the item caption provider to use, not null */ protected void setItemCaptionGenerator( ItemCaptionGenerator itemCaptionGenerator) { Objects.requireNonNull(itemCaptionGenerator, "Item caption generators must not be null"); this.itemCaptionGenerator = itemCaptionGenerator; getDataCommunicator().reset(); } /** * Sets the item icon generator that is used to produce custom icons for * shown items. The generator can return null for items with no icon. *

* Implementations that support item icons make this method public. * * @see IconGenerator * * @param itemIconGenerator * the item icon generator to set, not null * @throws NullPointerException * if {@code itemIconGenerator} is {@code null} */ protected void setItemIconGenerator(IconGenerator itemIconGenerator) { Objects.requireNonNull(itemIconGenerator, "Item icon generator must not be null"); this.itemIconGenerator = itemIconGenerator; getDataCommunicator().reset(); } /** * Gets the currently used item icon generator. The default item icon * provider returns null for all items, resulting in no icons being used. *

* Implementations that support item icons make this method public. * * @see IconGenerator * @see #setItemIconGenerator(IconGenerator) * * @return the currently used item icon generator, not null */ protected IconGenerator getItemIconGenerator() { return itemIconGenerator; } /** * Adds the given data generator to this listing. If the generator was * already added, does nothing. * * @param generator * the data generator to add, not null */ protected void addDataGenerator(DataGenerator generator) { getDataCommunicator().addDataGenerator(generator); } /** * Removes the given data generator from this listing. If this listing does * not have the generator, does nothing. * * @param generator * the data generator to remove, not null */ protected void removeDataGenerator(DataGenerator generator) { getDataCommunicator().removeDataGenerator(generator); } /** * Returns the data communicator of this listing. * * @return the data communicator, not null */ public DataCommunicator getDataCommunicator() { return dataCommunicator; } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); doWriteDesign(design, designContext); } /** * Writes listing specific state into the given design. *

* This method is separated from * {@link #writeDesign(Element, DesignContext)} to be overridable in * subclasses that need to replace this, but still must be able to call * {@code super.writeDesign(...)}. * * @see #doReadDesign(Element, DesignContext) * * @param design * The element to write the component state to. Any previous * attributes or child nodes are not cleared. * @param designContext * The DesignContext instance used for writing the design * */ protected void doWriteDesign(Element design, DesignContext designContext) { // Write options if warranted if (designContext.shouldWriteData(this)) { writeItems(design, designContext); } AbstractListing select = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("readonly", attr, isReadOnly(), select.isReadOnly(), Boolean.class, designContext); } /** * Writes the data source items to a design. Hierarchical select components * should override this method to only write the root items. * * @param design * the element into which to insert the items * @param context * the DesignContext instance used in writing */ protected void writeItems(Element design, DesignContext context) { internalGetDataProvider().fetch(new Query<>()) .forEach(item -> writeItem(design, item, context)); } /** * Writes a data source Item to a design. Hierarchical select components * should override this method to recursively write any child items as well. * * @param design * the element into which to insert the item * @param item * the item to write * @param context * the DesignContext instance used in writing * @return a JSOUP element representing the {@code item} */ protected Element writeItem(Element design, T item, DesignContext context) { Element element = design.appendElement("option"); String caption = getItemCaptionGenerator().apply(item); if (caption != null) { element.html(DesignFormatter.encodeForTextNode(caption)); } else { element.html(DesignFormatter.encodeForTextNode(item.toString())); } element.attr("item", serializeDeclarativeRepresentation(item)); Resource icon = getItemIconGenerator().apply(item); if (icon != null) { DesignAttributeHandler.writeAttribute("icon", element.attributes(), icon, null, Resource.class, context); } return element; } @Override public void readDesign(Element design, DesignContext context) { super.readDesign(design, context); doReadDesign(design, context); } /** * Reads the listing specific state from the given design. *

* This method is separated from {@link #readDesign(Element, DesignContext)} * to be overridable in subclasses that need to replace this, but still must * be able to call {@code super.readDesign(...)}. * * @see #doWriteDesign(Element, DesignContext) * * @param design * The element to obtain the state from * @param context * The DesignContext instance used for parsing the design */ protected void doReadDesign(Element design, DesignContext context) { Attributes attr = design.attributes(); if (attr.hasKey("readonly")) { setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr, Boolean.class)); } setItemCaptionGenerator( new DeclarativeCaptionGenerator<>(getItemCaptionGenerator())); setItemIconGenerator( new DeclarativeIconGenerator<>(getItemIconGenerator())); readItems(design, context); } /** * Reads the data source items from the {@code design}. * * @param design * The element to obtain the state from * @param context * The DesignContext instance used for parsing the design */ protected abstract void readItems(Element design, DesignContext context); /** * Reads an Item from a design and inserts it into the data source. *

* Doesn't care about selection/value (if any). * * @param child * a child element representing the item * @param context * the DesignContext instance used in parsing * @return the item id of the new item * * @throws DesignException * if the tag name of the {@code child} element is not * {@code option}. */ @SuppressWarnings({ "rawtypes", "unchecked" }) protected T readItem(Element child, DesignContext context) { if (!"option".equals(child.tagName())) { throw new DesignException("Unrecognized child element in " + getClass().getSimpleName() + ": " + child.tagName()); } String serializedItem = ""; String caption = DesignFormatter.decodeFromTextNode(child.html()); if (child.hasAttr("item")) { serializedItem = child.attr("item"); } T item = deserializeDeclarativeRepresentation(serializedItem); ItemCaptionGenerator captionGenerator = getItemCaptionGenerator(); if (captionGenerator instanceof DeclarativeCaptionGenerator) { ((DeclarativeCaptionGenerator) captionGenerator).setCaption(item, caption); } else { throw new IllegalStateException(String.format("Don't know how " + "to set caption using current caption generator '%s'", captionGenerator.getClass().getName())); } IconGenerator iconGenerator = getItemIconGenerator(); if (child.hasAttr("icon")) { if (iconGenerator instanceof DeclarativeIconGenerator) { ((DeclarativeIconGenerator) iconGenerator).setIcon(item, DesignAttributeHandler.readAttribute("icon", child.attributes(), Resource.class)); } else { throw new IllegalStateException(String.format("Don't know how " + "to set icon using current caption generator '%s'", iconGenerator.getClass().getName())); } } return item; } /** * Deserializes a string to a data item. *

* Default implementation is able to handle only {@link String} as an item * type. There will be a {@link ClassCastException} if {@code T } is not a * {@link String}. * * @see #serializeDeclarativeRepresentation(Object) * * @param item * string to deserialize * @throws ClassCastException * if type {@code T} is not a {@link String} * @return deserialized item */ protected T deserializeDeclarativeRepresentation(String item) { return (T) item; } /** * Serializes an {@code item} to a string for saving declarative format. *

* Default implementation delegates a call to {@code item.toString()}. * * @see #deserializeDeclarativeRepresentation(String) * * @param item * a data item * @return string representation of the {@code item}. */ protected String serializeDeclarativeRepresentation(T item) { return item.toString(); } @Override protected AbstractListingState getState() { return (AbstractListingState) super.getState(); } @Override protected AbstractListingState getState(boolean markAsDirty) { return (AbstractListingState) super.getState(markAsDirty); } @Override public void focus() { super.focus(); } @Override public int getTabIndex() { return getState(false).tabIndex; } @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 6306 Content-Disposition: inline; filename="AbstractLocalDateField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "62880d9579ae79a9e9255a118c3e2d0cddc15eaa" /* * Copyright 2000-2022 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.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.Locale; import java.util.Map; import com.vaadin.data.Result; import com.vaadin.data.validator.DateRangeValidator; import com.vaadin.data.validator.RangeValidator; import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateResolution; /** * Abstract DateField class for {@link LocalDate} type. * * @author Vaadin Ltd * @since 8.0 */ public abstract class AbstractLocalDateField extends AbstractDateField { /** * Constructs an empty AbstractLocalDateField with no caption. */ public AbstractLocalDateField() { super(DateResolution.DAY); } /** * Constructs an empty AbstractLocalDateField with caption. * * @param caption * the caption of the datefield. */ public AbstractLocalDateField(String caption) { super(caption, DateResolution.DAY); } /** * Constructs a new AbstractLocalDateField with the given * caption and initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDate value. */ public AbstractLocalDateField(String caption, LocalDate value) { super(caption, value, DateResolution.DAY); } @Override protected int getDatePart(LocalDate date, DateResolution resolution) { LocalDate value = date; if (value == null) { value = LocalDate.of(1, 1, 1); } switch (resolution) { case DAY: return value.getDayOfMonth(); case MONTH: return value.getMonthValue(); case YEAR: return value.getYear(); default: assert false : "Unexpected resolution argument " + resolution; return -1; } } @Override protected LocalDate buildDate( Map resolutionValues) { return LocalDate.of(resolutionValues.get(DateResolution.YEAR), resolutionValues.getOrDefault(DateResolution.MONTH, 1), resolutionValues.getOrDefault(DateResolution.DAY, 1)); } @Override protected RangeValidator getRangeValidator() { return new DateRangeValidator(getDateOutOfRangeMessage(), adjustToResolution(getRangeStart(), getResolution()), adjustToResolution(getRangeEnd(), getResolution())); } @Override protected AbstractTextualDateFieldState getState() { return (AbstractTextualDateFieldState) super.getState(); } @Override protected AbstractTextualDateFieldState getState(boolean markAsDirty) { return (AbstractTextualDateFieldState) super.getState(markAsDirty); } @Override protected LocalDate convertFromDate(Date date) { if (date == null) { return null; } return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) .toLocalDate(); } @Override protected Date convertToDate(LocalDate date) { if (date == null) { return null; } return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant()); } @Override protected LocalDate adjustToResolution(LocalDate date, DateResolution forResolution) { if (date == null) { return null; } if (forResolution == DateResolution.YEAR) { return date.withDayOfYear(1); } else if (forResolution == DateResolution.MONTH) { return date.withDayOfMonth(1); } else { return date; } } @Override protected String formatDate(LocalDate value) { if (value == null) { return ""; } DateTimeFormatter dateTimeFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.SHORT); Locale locale = getLocale(); if (locale != null) { dateTimeFormatter = dateTimeFormatter.withLocale(locale); } return value.format(dateTimeFormatter); } @Override protected LocalDate toType(TemporalAccessor temporalAccessor) { return temporalAccessor == null ? null : LocalDate.from(temporalAccessor); } @Override protected Result handleUnparsableDateString(String dateString) { // Handle possible week number, which cannot be parsed client side due // limitations in GWT if (getDateFormat() != null && getDateFormat().contains("w")) { Date parsedDate; SimpleDateFormat df = new SimpleDateFormat(getDateFormat(), getLocale()); try { parsedDate = df.parse(dateString); } catch (ParseException e) { return super.handleUnparsableDateString(dateString); } ZoneId zi = getZoneId(); if (zi == null) { zi = ZoneId.systemDefault(); } LocalDate date = Instant.ofEpochMilli(parsedDate.getTime()) .atZone(zi).toLocalDate(); return Result.ok(date); } else { return super.handleUnparsableDateString(dateString); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 6219 Content-Disposition: inline; filename="AbstractLocalDateTimeField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4adf5388a209fffb30ea93e87c5dce98745b9ef5" /* * Copyright 2000-2022 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.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.Locale; import java.util.Map; import com.vaadin.data.validator.DateTimeRangeValidator; import com.vaadin.data.validator.RangeValidator; import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateTimeResolution; /** * Abstract DateField class for {@link LocalDateTime} type. * * @author Vaadin Ltd * * @since 8.0 */ public abstract class AbstractLocalDateTimeField extends AbstractDateField { /** * Constructs an empty AbstractLocalDateTimeField with no * caption. */ public AbstractLocalDateTimeField() { super(DateTimeResolution.MINUTE); } /** * Constructs an empty AbstractLocalDateTimeField with caption. * * @param caption * the caption of the datefield. */ public AbstractLocalDateTimeField(String caption) { super(caption, DateTimeResolution.MINUTE); } /** * Constructs a new AbstractLocalDateTimeField with the given * caption and initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDateTime value. */ public AbstractLocalDateTimeField(String caption, LocalDateTime value) { super(caption, value, DateTimeResolution.MINUTE); } @Override protected AbstractTextualDateFieldState getState() { return (AbstractTextualDateFieldState) super.getState(); } @Override protected AbstractTextualDateFieldState getState(boolean markAsDirty) { return (AbstractTextualDateFieldState) super.getState(markAsDirty); } @Override protected int getDatePart(LocalDateTime date, DateTimeResolution resolution) { LocalDateTime value = date; if (value == null) { value = LocalDateTime.of(1, 1, 1, 0, 0); } switch (resolution) { case DAY: return value.getDayOfMonth(); case MONTH: return value.getMonthValue(); case YEAR: return value.getYear(); case HOUR: return value.getHour(); case MINUTE: return value.getMinute(); case SECOND: return value.getSecond(); default: assert false : "Unexpected resolution argument " + resolution; return -1; } } @Override protected RangeValidator getRangeValidator() { return new DateTimeRangeValidator(getDateOutOfRangeMessage(), adjustToResolution(getRangeStart(), getResolution()), adjustToResolution(getRangeEnd(), getResolution())); } @Override protected LocalDateTime buildDate( Map resolutionValues) { return LocalDateTime.of(resolutionValues.get(DateTimeResolution.YEAR), resolutionValues.getOrDefault(DateTimeResolution.MONTH, 1), resolutionValues.getOrDefault(DateTimeResolution.DAY, 1), resolutionValues.getOrDefault(DateTimeResolution.HOUR, 0), resolutionValues.getOrDefault(DateTimeResolution.MINUTE, 0), resolutionValues.getOrDefault(DateTimeResolution.SECOND, 0)); } @Override protected LocalDateTime convertFromDate(Date date) { if (date == null) { return null; } return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) .toLocalDateTime(); } @Override protected Date convertToDate(LocalDateTime date) { if (date == null) { return null; } return Date.from(date.toInstant(ZoneOffset.UTC)); } @Override protected LocalDateTime adjustToResolution(LocalDateTime date, DateTimeResolution forResolution) { if (date == null) { return null; } switch (forResolution) { case YEAR: return date.withDayOfYear(1).toLocalDate().atStartOfDay(); case MONTH: return date.withDayOfMonth(1).toLocalDate().atStartOfDay(); case DAY: return date.toLocalDate().atStartOfDay(); case HOUR: return date.truncatedTo(ChronoUnit.HOURS); case MINUTE: return date.truncatedTo(ChronoUnit.MINUTES); case SECOND: return date.truncatedTo(ChronoUnit.SECONDS); default: assert false : "Unexpected resolution argument " + forResolution; return null; } } @Override protected String formatDate(LocalDateTime value) { if (value == null) { return ""; } DateTimeFormatter dateTimeFormatter = DateTimeFormatter .ofLocalizedDateTime(FormatStyle.SHORT); Locale locale = getLocale(); if (locale != null) { dateTimeFormatter = dateTimeFormatter.withLocale(locale); } return value.format(dateTimeFormatter); } @Override protected LocalDateTime toType(TemporalAccessor temporalAccessor) { return temporalAccessor == null ? null : LocalDateTime.from(temporalAccessor); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 10382 Content-Disposition: inline; filename="AbstractMedia.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "b6dd63965e28603fc793e5a10c0d259c858d0655" /* * Copyright 2000-2022 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.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import com.vaadin.server.ConnectorResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; import com.vaadin.server.VaadinSession; import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; import com.vaadin.shared.ui.MediaControl; import com.vaadin.shared.ui.PreloadMode; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * Abstract base class for the HTML5 media components. * * @author Vaadin Ltd */ public abstract class AbstractMedia extends AbstractComponent { @Override protected AbstractMediaState getState() { return (AbstractMediaState) super.getState(); } @Override protected AbstractMediaState getState(boolean markAsDirty) { return (AbstractMediaState) super.getState(markAsDirty); } /** * Sets a single media file as the source of the media component. * * @param source */ public void setSource(Resource source) { clearSources(); addSource(source); } private void clearSources() { getState().sources.clear(); getState().sourceTypes.clear(); } /** * Adds an alternative media file to the sources list. Which of the sources * is used is selected by the browser depending on which file formats it * supports. See * wikipedia * for a table of formats supported by different browsers. * * @param source */ public void addSource(Resource source) { if (source != null) { List sources = getState().sources; sources.add(new ResourceReference(source, this, Integer.toString(sources.size()))); getState().sourceTypes.add(source.getMIMEType()); } } @Override public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path) throws IOException { Matcher matcher = Pattern.compile("(\\d+)(/.*)?").matcher(path); if (!matcher.matches()) { return super.handleConnectorRequest(request, response, path); } DownloadStream stream; VaadinSession session = getSession(); session.lock(); try { List sources = getState().sources; int sourceIndex = Integer.parseInt(matcher.group(1)); if (sourceIndex < 0 || sourceIndex >= sources.size()) { getLogger().log(Level.WARNING, "Requested source index {0} is out of bounds", sourceIndex); return false; } URLReference reference = sources.get(sourceIndex); ConnectorResource resource = (ConnectorResource) ResourceReference .getResource(reference); stream = resource.getStream(); } finally { session.unlock(); } stream.writeResponse(request, response); return true; } private Logger getLogger() { return Logger.getLogger(AbstractMedia.class.getName()); } /** * Set multiple sources at once. Which of the sources is used is selected by * the browser depending on which file formats it supports. See * wikipedia * for a table of formats supported by different browsers. * * @param sources */ public void setSources(Resource... sources) { clearSources(); for (Resource source : sources) { addSource(source); } } /** * @return The sources pointed to in this media. */ public List getSources() { List sources = new ArrayList<>(); for (URLReference ref : getState(false).sources) { sources.add(((ResourceReference) ref).getResource()); } return sources; } /** * Sets whether or not the browser should show native media controls. * * @param showControls */ public void setShowControls(boolean showControls) { getState().showControls = showControls; } /** * @return true if the browser is to show native media controls. */ public boolean isShowControls() { return getState(false).showControls; } /** * Sets the alternative text to be displayed if the browser does not support * HTML5. This text is rendered as HTML if * {@link #setHtmlContentAllowed(boolean)} is set to true. With HTML * rendering, this method can also be used to implement fallback to a * flash-based player, see the Mozilla Developer Network for details. * * @param altText */ public void setAltText(String altText) { getState().altText = altText; } /** * @return The text/html that is displayed when a browser doesn't support * HTML5. */ public String getAltText() { return getState(false).altText; } /** * Sets the preload attribute that is intended to provide a hint to the * browser how the media should be preloaded. Valid values are 'none', * 'metadata', 'preload', see the * Mozilla Developer Network for details. * * @param preload * preload mode * @since 7.7.11 */ public void setPreload(final PreloadMode preload) { getState().preload = preload; } /** * @return the configured media preload value * @since 7.7.11 */ public PreloadMode getPreload() { return getState(false).preload; } /** * Enables or disables looping. * * @param loop * if true, enable looping * @since 7.7.11 */ public void setLoop(final boolean loop) { getState().loop = loop; } /** * @return true if looping is enabled * @since 7.7.11 */ public boolean isLoop() { return getState(false).loop; } /** * Set whether the alternative text ({@link #setAltText(String)}) is * rendered as HTML or not. * * @param htmlContentAllowed */ public void setHtmlContentAllowed(boolean htmlContentAllowed) { getState().htmlContentAllowed = htmlContentAllowed; } /** * @return true if the alternative text ({@link #setAltText(String)}) is to * be rendered as HTML. */ public boolean isHtmlContentAllowed() { return getState(false).htmlContentAllowed; } /** * Sets whether the media is to automatically start playback when enough * data has been loaded. * * @param autoplay */ public void setAutoplay(boolean autoplay) { getState().autoplay = autoplay; } /** * @return true if the media is set to automatically start playback. */ public boolean isAutoplay() { return getState(false).autoplay; } /** * Set whether to mute the audio or not. * * @param muted */ public void setMuted(boolean muted) { getState().muted = muted; } /** * @return true if the audio is muted. */ public boolean isMuted() { return getState(false).muted; } /** * Pauses the media. */ public void pause() { getRpcProxy(MediaControl.class).pause(); } /** * Starts playback of the media. */ public void play() { getRpcProxy(MediaControl.class).play(); } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); String altText = getAltText(); if (altText != null && !altText.isEmpty()) { design.append(altText); } for (Resource r : getSources()) { Attributes attr = design.appendElement("source").attributes(); DesignAttributeHandler.writeAttribute("href", attr, r, null, Resource.class, designContext); } } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); String altText = ""; for (Node child : design.childNodes()) { if (child instanceof Element && ((Element) child).tagName().equals("source") && child.hasAttr("href")) { addSource(DesignAttributeHandler.readAttribute("href", child.attributes(), Resource.class)); } else { altText += child.toString(); } } altText = altText.trim(); if (!altText.isEmpty()) { setAltText(altText); } } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("alt-text"); return result; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17679 Content-Disposition: inline; filename="AbstractMultiSelect.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "6f2c6c439b6098637ae5981eeab5f2c019187a68" /* * Copyright 2000-2022 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.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.jsoup.nodes.Element; import com.vaadin.data.HasValue; import com.vaadin.data.SelectionModel; import com.vaadin.data.SelectionModel.Multi; import com.vaadin.data.provider.DataGenerator; import com.vaadin.data.provider.DataProvider; import com.vaadin.event.selection.MultiSelectionEvent; import com.vaadin.event.selection.MultiSelectionListener; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.SerializableConsumer; import com.vaadin.server.SerializablePredicate; import com.vaadin.shared.Registration; import com.vaadin.shared.data.selection.MultiSelectServerRpc; import com.vaadin.shared.ui.ListingJsonConstants; import com.vaadin.shared.ui.abstractmultiselect.AbstractMultiSelectState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; import elemental.json.JsonObject; /** * Base class for listing components that allow selecting multiple items. *

* Sends selection information individually for each item. * * @param * item type * @author Vaadin Ltd * @since 8.0 */ public abstract class AbstractMultiSelect extends AbstractListing implements MultiSelect { private List selection = new ArrayList<>(); private class MultiSelectServerRpcImpl implements MultiSelectServerRpc { @Override public void updateSelection(Set selectedItemKeys, Set deselectedItemKeys) { AbstractMultiSelect.this.updateSelection( getItemsForSelectionChange(selectedItemKeys), getItemsForSelectionChange(deselectedItemKeys), true); } private Set getItemsForSelectionChange(Set keys) { return keys.stream().map(key -> getItemForSelectionChange(key)) .filter(Optional::isPresent).map(Optional::get) .collect(Collectors.toSet()); } private Optional getItemForSelectionChange(String key) { T item = getDataCommunicator().getKeyMapper().get(key); if (item == null || !getItemEnabledProvider().test(item)) { return Optional.empty(); } return Optional.of(item); } } private final class MultiSelectDataGenerator implements DataGenerator { @Override public void generateData(T data, JsonObject jsonObject) { String caption = getItemCaptionGenerator().apply(data); if (caption != null) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_VALUE, caption); } else { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_VALUE, ""); } Resource icon = getItemIconGenerator().apply(data); if (icon != null) { String iconUrl = ResourceReference .create(icon, AbstractMultiSelect.this, null).getURL(); jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_ICON, iconUrl); } if (!getItemEnabledProvider().test(data)) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_DISABLED, true); } if (isSelected(data)) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED, true); } } @Override public void destroyData(T data) { } @Override public void destroyAllData() { AbstractMultiSelect.this.deselectAll(); } @Override public void refreshData(T item) { refreshSelectedItem(item); } } /** * The item enabled status provider. It is up to the implementing class to * support this or not. */ private SerializablePredicate itemEnabledProvider = item -> true; /** * Creates a new multi select with an empty data provider. */ protected AbstractMultiSelect() { registerRpc(new MultiSelectServerRpcImpl()); // #FIXME it should be the responsibility of the SelectionModel // (AbstractSelectionModel) to add selection data for item addDataGenerator(new MultiSelectDataGenerator()); } /** * Adds a selection listener that will be called when the selection is * changed either by the user or programmatically. * * @param listener * the value change listener, not {@code null} * @return a registration for the listener */ @Override public Registration addSelectionListener( MultiSelectionListener listener) { return addListener(MultiSelectionEvent.class, listener, MultiSelectionListener.SELECTION_CHANGE_METHOD); } @Override public ItemCaptionGenerator getItemCaptionGenerator() { return super.getItemCaptionGenerator(); } @Override public void setItemCaptionGenerator( ItemCaptionGenerator itemCaptionGenerator) { super.setItemCaptionGenerator(itemCaptionGenerator); } /** * Returns the current value of this object which is an immutable set of the * currently selected items. *

* The call is delegated to {@link #getSelectedItems()} * * @return the current selection * * @see #getSelectedItems() * @see SelectionModel#getSelectedItems */ @Override public Set getValue() { return getSelectedItems(); } /** * Sets the value of this object which is a set of items to select. If the * new value is not equal to {@code getValue()}, fires a value change event. * May throw {@code IllegalArgumentException} if the value is not * acceptable. *

* The method effectively selects the given items and deselects previously * selected. The call is delegated to * {@link Multi#updateSelection(Set, Set)}. * * @see Multi#updateSelection(Set, Set) * * @param value * the items to select, not {@code null} * @throws NullPointerException * if the value is invalid */ @Override public void setValue(Set value) { Objects.requireNonNull(value); Set copy = value.stream().map(Objects::requireNonNull) .collect(Collectors.toCollection(LinkedHashSet::new)); updateSelection(copy, new LinkedHashSet<>(getSelectedItems())); } /** * Adds a value change listener. The listener is called when the selection * set of this multi select is changed either by the user or * programmatically. * * @see #addSelectionListener(MultiSelectionListener) * * @param listener * the value change listener, not null * @return a registration for the listener */ @Override public Registration addValueChangeListener( HasValue.ValueChangeListener> listener) { return addSelectionListener( event -> listener.valueChange(new ValueChangeEvent<>(this, event.getOldValue(), event.isUserOriginated()))); } /** * Returns the item enabled provider for this multiselect. *

* Implementation note: Override this method and * {@link #setItemEnabledProvider(SerializablePredicate)} as {@code public} * and invoke {@code super} methods to support this feature in the * multiselect component. * * @return the item enabled provider, not {@code null} * @see #setItemEnabledProvider(SerializablePredicate) */ protected SerializablePredicate getItemEnabledProvider() { return itemEnabledProvider; } /** * Sets the item enabled predicate for this multiselect. The predicate is * applied to each item to determine whether the item should be enabled ( * {@code true}) or disabled ({@code false}). Disabled items are displayed * as grayed out and the user cannot select them. The default predicate * always returns {@code true} (all the items are enabled). *

* Implementation note: Override this method and * {@link #getItemEnabledProvider()} as {@code public} and invoke * {@code super} methods to support this feature in the multiselect * component. * * @param itemEnabledProvider * the item enabled provider to set, not {@code null} */ protected void setItemEnabledProvider( SerializablePredicate itemEnabledProvider) { Objects.requireNonNull(itemEnabledProvider); this.itemEnabledProvider = itemEnabledProvider; } @Override public void setRequiredIndicatorVisible(boolean visible) { super.setRequiredIndicatorVisible(visible); } @Override public boolean isRequiredIndicatorVisible() { return super.isRequiredIndicatorVisible(); } @Override protected AbstractMultiSelectState getState() { return (AbstractMultiSelectState) super.getState(); } @Override protected AbstractMultiSelectState getState(boolean markAsDirty) { return (AbstractMultiSelectState) super.getState(markAsDirty); } @Override public void setReadOnly(boolean readOnly) { super.setReadOnly(readOnly); } @Override public boolean isReadOnly() { return super.isReadOnly(); } @Override public void updateSelection(Set addedItems, Set removedItems) { updateSelection(addedItems, removedItems, false); } /** * Updates the selection by adding and removing the given items. * * @param addedItems * the items added to selection, not {@code} null * @param removedItems * the items removed from selection, not {@code} null * @param userOriginated * {@code true} if this was used originated, {@code false} if not */ protected void updateSelection(Set addedItems, Set removedItems, boolean userOriginated) { Objects.requireNonNull(addedItems); Objects.requireNonNull(removedItems); // if there are duplicates, some item is both added & removed, just // discard that and leave things as was before DataProvider dataProvider = internalGetDataProvider(); addedItems.removeIf(item -> { Object addedId = dataProvider.getId(item); return removedItems.stream().map(dataProvider::getId).anyMatch( addedId::equals) ? removedItems.remove(item) : false; }); if (isAllSelected(addedItems) && isNoneSelected(removedItems)) { return; } updateSelection(set -> { // order of add / remove does not matter since no duplicates set.removeIf(item -> { Object itemId = dataProvider.getId(item); return removedItems.stream().map(dataProvider::getId) .anyMatch(itemId::equals); }); set.addAll(addedItems); }, userOriginated); } @Override public Set getSelectedItems() { return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); } @Override public void deselectAll() { if (selection.isEmpty()) { return; } updateSelection(Collection::clear, false); } @Override public boolean isSelected(T item) { DataProvider dataProvider = internalGetDataProvider(); Object id = dataProvider.getId(item); return selection.stream().map(dataProvider::getId).anyMatch(id::equals); } private boolean isAllSelected(Collection items) { for (T item : items) { if (!isSelected(item)) { return false; } } return true; } private boolean isNoneSelected(Collection items) { for (T item : items) { if (isSelected(item)) { return false; } } return true; } /** * Deselects the given item. If the item is not currently selected, does * nothing. * * @param item * the item to deselect, not null * @param userOriginated * {@code true} if this was used originated, {@code false} if not */ protected void deselect(T item, boolean userOriginated) { if (!selection.contains(item)) { return; } updateSelection(set -> set.remove(item), userOriginated); } /** * Removes the given items. Any item that is not currently selected, is * ignored. If none of the items are selected, does nothing. * * @param items * the items to deselect, not {@code null} * @param userOriginated * {@code true} if this was used originated, {@code false} if not */ protected void deselect(Set items, boolean userOriginated) { Objects.requireNonNull(items); if (items.stream().noneMatch(i -> isSelected(i))) { return; } updateSelection(set -> set.removeAll(items), userOriginated); } /** * Selects the given item. Depending on the implementation, may cause other * items to be deselected. If the item is already selected, does nothing. * * @param item * the item to select, not null * @param userOriginated * {@code true} if this was used originated, {@code false} if not */ protected void select(T item, boolean userOriginated) { if (selection.contains(item)) { return; } updateSelection(set -> set.add(item), userOriginated); } @Override protected Collection getCustomAttributes() { Collection attributes = super.getCustomAttributes(); // "value" is not an attribute for the component. "selected" attribute // is used in "option"'s tag to mark selection which implies value for // multiselect component attributes.add("value"); return attributes; } @Override protected Element writeItem(Element design, T item, DesignContext context) { Element element = super.writeItem(design, item, context); if (isSelected(item)) { element.attr("selected", true); } return element; } @Override protected void readItems(Element design, DesignContext context) { Set selected = new HashSet<>(); List items = design.children().stream() .map(child -> readItem(child, selected, context)) .collect(Collectors.toList()); deselectAll(); if (!items.isEmpty()) { setItems(items); } selected.forEach(this::select); } /** * Reads an Item from a design and inserts it into the data source. * Hierarchical select components should override this method to recursively * recursively read any child items as well. * * @param child * a child element representing the item * @param selected * A set accumulating selected items. If the item that is read is * marked as selected, its item id should be added to this set. * @param context * the DesignContext instance used in parsing * @return the item id of the new item * * @throws DesignException * if the tag name of the {@code child} element is not * {@code option}. */ protected T readItem(Element child, Set selected, DesignContext context) { T item = readItem(child, context); if (child.hasAttr("selected")) { selected.add(item); } return item; } private void updateSelection(SerializableConsumer> handler, boolean userOriginated) { LinkedHashSet oldSelection = new LinkedHashSet<>(selection); handler.accept(selection); fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this, oldSelection, userOriginated)); getDataCommunicator().reset(); } private final void refreshSelectedItem(T item) { DataProvider dataProvider = internalGetDataProvider(); Object id = dataProvider.getId(item); for (int i = 0; i < selection.size(); ++i) { if (id.equals(dataProvider.getId(selection.get(i)))) { selection.set(i, item); return; } } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17895 Content-Disposition: inline; filename="AbstractOrderedLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "9870ed1a6f3fae6ac795c2d8c5b65e6a0dd9ccbf" /* * Copyright 2000-2022 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.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.logging.Logger; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.server.Sizeable; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState.ChildComponentData; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; @SuppressWarnings("serial") public abstract class AbstractOrderedLayout extends AbstractLayout implements Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier, Layout.MarginHandler { private final AbstractOrderedLayoutServerRpc rpc = ( MouseEventDetails mouseDetails, Connector clickedConnector) -> fireEvent( LayoutClickEvent.createEvent(AbstractOrderedLayout.this, mouseDetails, clickedConnector)); public static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT; /** * Custom layout slots containing the components. */ protected LinkedList components = new LinkedList<>(); private Alignment defaultComponentAlignment = Alignment.TOP_LEFT; /* Child component alignments */ /** * Constructs an empty AbstractOrderedLayout. */ public AbstractOrderedLayout() { registerRpc(rpc); } @Override protected AbstractOrderedLayoutState getState() { return (AbstractOrderedLayoutState) super.getState(); } @Override protected AbstractOrderedLayoutState getState(boolean markAsDirty) { return (AbstractOrderedLayoutState) super.getState(markAsDirty); } /** * 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) { // Add to components before calling super.addComponent // so that it is available to AttachListeners components.add(c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } componentAdded(c); } /** * Adds a component into this container. The component is added to the left * or on top of the other components. * * @param c * the component to be added. */ public void addComponentAsFirst(Component c) { // If c is already in this, we must remove it before proceeding // see ticket #7668 if (equals(c.getParent())) { removeComponent(c); } components.addFirst(c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } componentAdded(c); } /** * Adds a component into indexed position in this container. * * @param c * the component to be added. * @param index * the index of the component position. The components currently * in and after the position are shifted forwards. */ public void addComponent(Component c, int index) { // If c is already in this, we must remove it before proceeding // see ticket #7668 if (equals(c.getParent())) { // When c is removed, all components after it are shifted down if (index > getComponentIndex(c)) { index--; } removeComponent(c); } components.add(index, c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } componentAdded(c); } private void componentRemoved(Component c) { getState().childData.remove(c); } private void componentAdded(Component c) { ChildComponentData ccd = new ChildComponentData(); ccd.alignmentBitmask = getDefaultComponentAlignment().getBitMask(); getState().childData.put(c, ccd); } /** * Removes the component from this container. * * @param c * the component to be removed. */ @Override public void removeComponent(Component c) { components.remove(c); super.removeComponent(c); componentRemoved(c); } /** * Gets the component container iterator for going trough all the components * in the container. * * @return the Iterator of the components inside the container. */ @Override public Iterator iterator() { return Collections.unmodifiableCollection(components).iterator(); } /** * Gets the number of contained components. Consistent with the iterator * returned by {@link #getComponentIterator()}. * * @return the number of contained components */ @Override public int getComponentCount() { return components.size(); } /* Documented in superclass */ @Override public void replaceComponent(Component oldComponent, Component newComponent) { // Gets the locations int oldLocation = -1; int newLocation = -1; int location = 0; for (final Component component : components) { if (component == oldComponent) { oldLocation = location; } if (component == newComponent) { newLocation = location; } location++; } if (oldLocation == -1) { addComponent(newComponent); } else if (newLocation == -1) { Alignment alignment = getComponentAlignment(oldComponent); float expandRatio = getExpandRatio(oldComponent); removeComponent(oldComponent); addComponent(newComponent, oldLocation); applyLayoutSettings(newComponent, alignment, expandRatio); } else { // Both old and new are in the layout if (oldLocation > newLocation) { components.remove(oldComponent); components.add(newLocation, oldComponent); components.remove(newComponent); components.add(oldLocation, newComponent); } else { components.remove(newComponent); components.add(oldLocation, newComponent); components.remove(oldComponent); components.add(newLocation, oldComponent); } markAsDirty(); } } @Override public void setComponentAlignment(Component childComponent, Alignment alignment) { ChildComponentData childData = getState().childData.get(childComponent); if (childData != null) { // Alignments are bit masks childData.alignmentBitmask = alignment.getBitMask(); } else { throw new IllegalArgumentException( "Component must be added to layout before using setComponentAlignment()"); } } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.AlignmentHandler#getComponentAlignment(com * .vaadin.ui.Component) */ @Override public Alignment getComponentAlignment(Component childComponent) { ChildComponentData childData = getState().childData.get(childComponent); if (childData == null) { throw new IllegalArgumentException( "The given component is not a child of this layout"); } return new Alignment(childData.alignmentBitmask); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.SpacingHandler#setSpacing(boolean) */ @Override public void setSpacing(boolean spacing) { getState().spacing = spacing; } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.SpacingHandler#isSpacing() */ @Override public boolean isSpacing() { return getState(false).spacing; } /** *

* This method is used to control how excess space in layout is distributed * among components. Excess space may exist if layout is sized and contained * non relatively sized components don't consume all available space. * *

* Example how to distribute 1:3 (33%) for component1 and 2:3 (67%) for * component2 : * * * layout.setExpandRatio(component1, 1);
* layout.setExpandRatio(component2, 2); *
* *

* If no ratios have been set, the excess space is distributed evenly among * all components. * *

* Note, that width or height (depending on orientation) needs to be defined * for this method to have any effect. * * @see Sizeable * * @param component * the component in this layout which expand ratio is to be set * @param ratio * new expand ratio (greater or equal to 0) * @throws IllegalArgumentException * if the expand ratio is negative or the component is not a * direct child of the layout */ public void setExpandRatio(Component component, float ratio) { ChildComponentData childData = getState().childData.get(component); if (childData == null) { throw new IllegalArgumentException( "The given component is not a child of this layout"); } if (ratio < 0.0f) { throw new IllegalArgumentException( "Expand ratio can't be less than 0.0"); } childData.expandRatio = ratio; } /** * Returns the expand ratio of given component. * * @param component * which expand ratios is requested * @return expand ratio of given component, 0.0f by default. */ public float getExpandRatio(Component component) { ChildComponentData childData = getState(false).childData.get(component); if (childData == null) { throw new IllegalArgumentException( "The given component is not a child of this layout"); } return childData.expandRatio; } @Override public Registration addLayoutClickListener(LayoutClickListener listener) { return addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener, LayoutClickListener.clickMethod); } @Override @Deprecated public void removeLayoutClickListener(LayoutClickListener listener) { removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener); } /** * Returns the index of the given component. * * @param component * The component to look up. * @return The index of the component or -1 if the component is not a child. */ public int getComponentIndex(Component component) { return components.indexOf(component); } /** * Returns the component at the given position. * * @param index * The position of the component. * @return The component at the given index. * @throws IndexOutOfBoundsException * If the index is out of range. */ public Component getComponent(int index) throws IndexOutOfBoundsException { return components.get(index); } @Override public void setMargin(boolean enabled) { setMargin(new MarginInfo(enabled)); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.MarginHandler#getMargin() */ @Override public MarginInfo getMargin() { return new MarginInfo(getState(false).marginsBitmask); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.MarginHandler#setMargin(MarginInfo) */ @Override public void setMargin(MarginInfo marginInfo) { getState().marginsBitmask = marginInfo.getBitMask(); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.AlignmentHandler#getDefaultComponentAlignment() */ @Override public Alignment getDefaultComponentAlignment() { return defaultComponentAlignment; } /* * (non-Javadoc) * * @see * com.vaadin.ui.Layout.AlignmentHandler#setDefaultComponentAlignment(com * .vaadin.ui.Alignment) */ @Override public void setDefaultComponentAlignment(Alignment defaultAlignment) { defaultComponentAlignment = defaultAlignment; } private void applyLayoutSettings(Component target, Alignment alignment, float expandRatio) { setComponentAlignment(target, alignment); setExpandRatio(target, expandRatio); } /* * (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) { // process default attributes super.readDesign(design, designContext); setMargin(readMargin(design, getMargin(), designContext)); // handle children for (Element childComponent : design.children()) { Attributes attr = childComponent.attributes(); Component newChild = designContext.readDesign(childComponent); addComponent(newChild); // handle alignment setComponentAlignment(newChild, DesignAttributeHandler.readAlignment(attr)); // handle expand ratio if (attr.hasKey(":expand")) { String value = attr.get(":expand"); if (!value.isEmpty()) { try { float ratio = Float.valueOf(value); setExpandRatio(newChild, ratio); } catch (NumberFormatException nfe) { getLogger() .info("Failed to parse expand ratio " + value); } } else { setExpandRatio(newChild, 1.0f); } } } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element * , com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { // write default attributes super.writeDesign(design, designContext); AbstractOrderedLayout def = designContext.getDefaultInstance(this); writeMargin(design, getMargin(), def.getMargin(), designContext); // handle children if (!designContext.shouldWriteChildren(this, def)) { return; } for (Component child : this) { Element childElement = designContext.createElement(child); design.appendChild(childElement); // handle alignment Alignment alignment = getComponentAlignment(child); if (alignment.isMiddle()) { childElement.attr(":middle", true); } else if (alignment.isBottom()) { childElement.attr(":bottom", true); } if (alignment.isCenter()) { childElement.attr(":center", true); } else if (alignment.isRight()) { childElement.attr(":right", true); } // handle expand ratio float expandRatio = getExpandRatio(child); if (expandRatio == 1.0f) { childElement.attr(":expand", true); } else if (expandRatio > 0) { childElement.attr(":expand", DesignAttributeHandler .getFormatter().format(expandRatio)); } } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#getCustomAttributes() */ @Override protected Collection getCustomAttributes() { Collection customAttributes = super.getCustomAttributes(); customAttributes.add("margin"); customAttributes.add("margin-left"); customAttributes.add("margin-right"); customAttributes.add("margin-top"); customAttributes.add("margin-bottom"); return customAttributes; } private static Logger getLogger() { return Logger.getLogger(AbstractOrderedLayout.class.getName()); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 13383 Content-Disposition: inline; filename="AbstractSingleComponentContainer.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "c7a32adc09c6551d4dab59029f48d9cfb4ea89c3" /* * Copyright 2000-2022 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.util.Collections; import java.util.Iterator; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.vaadin.server.ComponentSizeValidator; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinSession; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.AbstractSingleComponentContainerState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; /** * Abstract base class for component containers that have only one child * component. * * For component containers that support multiple children, inherit * {@link AbstractComponentContainer} instead of this class. * * @since 7.0 */ public abstract class AbstractSingleComponentContainer extends AbstractComponent implements SingleComponentContainer { private Component content; @Override public int getComponentCount() { return (content != null) ? 1 : 0; } @Override public Iterator iterator() { if (content != null) { return Collections.singletonList(content).iterator(); } else { return Collections. emptyList().iterator(); } } /* documented in interface */ @Override public Registration addComponentAttachListener( ComponentAttachListener listener) { return addListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentAttachListener( ComponentAttachListener listener) { removeListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override public Registration addComponentDetachListener( ComponentDetachListener listener) { return addListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentDetachListener( ComponentDetachListener listener) { removeListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /** * Fires the component attached event. This is called by the * {@link #setContent(Component)} method after the component has been set as * the content. * * @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 is called by the * {@link #setContent(Component)} method after the content component has * been replaced by other content. * * @param component * the component that has been removed from this container. */ protected void fireComponentDetachEvent(Component component) { fireEvent(new ComponentDetachEvent(this, component)); } @Override public Component getContent() { return content; } /** * Sets the content of this container. The content is a component that * serves as the outermost item of the visual contents. * * The content must always be set, either with a constructor parameter or by * calling this method. * * Previous versions of Vaadin used a {@link VerticalLayout} with margins * enabled as the default content but that is no longer the case. * * @param content * a component (typically a layout) to use as content */ @Override public void setContent(Component content) { // Make sure we're not adding the component inside it's own content if (isOrHasAncestor(content)) { throw new IllegalArgumentException( "Component cannot be added inside it's own content"); } Component oldContent = getContent(); if (oldContent == content) { // do not set the same content twice return; } if (oldContent != null && equals(oldContent.getParent())) { oldContent.setParent(null); fireComponentDetachEvent(oldContent); } this.content = content; if (content != null) { removeFromParent(content); content.setParent(this); fireComponentAttachEvent(content); } markAsDirty(); } /** * Utility method for removing a component from its parent (if possible). * * @param content * component to remove */ // TODO move utility method elsewhere? public static void removeFromParent(Component content) throws IllegalArgumentException { // Verify the appropriate session is locked UI parentUI = content.getUI(); if (parentUI != null) { VaadinSession parentSession = parentUI.getSession(); if (parentSession != null && !parentSession.hasLock()) { String message = "Cannot remove from parent when the session is not locked."; if (VaadinService.isOtherSessionLocked(parentSession)) { message += " Furthermore, there is another locked session, indicating that the component might be about to be moved from one session to another."; } throw new IllegalStateException(message); } } HasComponents parent = content.getParent(); if (parent instanceof ComponentContainer) { // If the component already has a parent, try to remove it ComponentContainer oldParent = (ComponentContainer) parent; oldParent.removeComponent(content); } else if (parent instanceof SingleComponentContainer) { SingleComponentContainer oldParent = (SingleComponentContainer) parent; if (oldParent.getContent() == content) { oldParent.setContent(null); } } else if (parent != null) { throw new IllegalArgumentException( "Content is already attached to another parent"); } } // the setHeight()/setWidth() methods duplicated and simplified from // AbstractComponentContainer @Override public void setWidth(float width, Unit unit) { /* * child tree repaints may be needed, due to our fall back support for * invalid relative sizes */ boolean dirtyChild = false; boolean childrenMayBecomeUndefined = false; if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) { // children currently in invalid state may need repaint dirtyChild = getInvalidSizedChild(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; dirtyChild = getInvalidSizedChild(false); } super.setWidth(width, unit); repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, false); } private void repaintChangedChildTree(boolean invalidChild, boolean childrenMayBecomeUndefined, boolean vertical) { if (getContent() == null) { return; } boolean needRepaint = false; if (childrenMayBecomeUndefined) { // if became invalid now needRepaint = !invalidChild && getInvalidSizedChild(vertical); } else if (invalidChild) { // if not still invalid needRepaint = !getInvalidSizedChild(vertical); } if (needRepaint) { getContent().markAsDirtyRecursive(); } } private boolean getInvalidSizedChild(final boolean vertical) { Component content = getContent(); if (content == null) { return false; } if (vertical) { return !ComponentSizeValidator.checkHeights(content); } else { return !ComponentSizeValidator.checkWidths(content); } } @Override public void setHeight(float height, Unit unit) { /* * child tree repaints may be needed, due to our fall back support for * invalid relative sizes */ boolean dirtyChild = false; boolean childrenMayBecomeUndefined = false; if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) { // children currently in invalid state may need repaint dirtyChild = getInvalidSizedChild(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; dirtyChild = getInvalidSizedChild(true); } super.setHeight(height, unit); repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, true); } /* * (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) { // process default attributes super.readDesign(design, designContext); readDesignChildren(design.children(), designContext); } /** * Reads the content component from the list of child elements of a design. * The list must be empty or contain a single element; if the design * contains multiple child elements, a DesignException is thrown. This * method should be overridden by subclasses whose design may contain * non-content child elements. * * @since 7.5.0 * * @param children * the child elements of the design that is being read * @param context * the DesignContext instance used to parse the design * * @throws DesignException * if there are multiple child elements * @throws DesignException * if a child element could not be parsed as a Component */ protected void readDesignChildren(Elements children, DesignContext context) { if (children.size() > 1) { throw new DesignException("The container of type " + getClass() + " can have only one child component."); } else if (children.size() == 1) { setContent(context.readDesign(children.first())); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element * , com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { // write default attributes (also clears children and attributes) super.writeDesign(design, designContext); AbstractSingleComponentContainer def = designContext .getDefaultInstance(this); if (!designContext.shouldWriteChildren(this, def)) { return; } // handle child component Component child = getContent(); if (child != null) { Element childNode = designContext.createElement(child); design.appendChild(childNode); } } @Override protected AbstractSingleComponentContainerState getState() { return (AbstractSingleComponentContainerState) super.getState(); } @Override protected AbstractSingleComponentContainerState getState( boolean markAsDirty) { return (AbstractSingleComponentContainerState) super.getState( markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 11696 Content-Disposition: inline; filename="AbstractSingleSelect.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3d94361ce911b813c94c4679720300ef4dbac540" /* * Copyright 2000-2022 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.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.jsoup.nodes.Element; import com.vaadin.data.HasValue; import com.vaadin.data.SelectionModel.Single; import com.vaadin.data.provider.DataCommunicator; import com.vaadin.data.provider.DataGenerator; import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.shared.Registration; import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.shared.ui.AbstractSingleSelectState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; import elemental.json.Json; import elemental.json.JsonObject; /** * An abstract base class for listing components that only support single * selection and no lazy loading of data items. * * @author Vaadin Ltd. * * @param * the item date type * * @see com.vaadin.data.SelectionModel.Single * * @since 8.0 */ public abstract class AbstractSingleSelect extends AbstractListing implements SingleSelect { private T selectedItem = null; /** * Creates a new {@code AbstractListing} with a default data communicator. *

*/ protected AbstractSingleSelect() { init(); } /** * Creates a new {@code AbstractSingleSelect} with the given custom data * communicator. *

* Note: This method is for creating an * {@code AbstractSingleSelect} with a custom communicator. In the common * case {@link AbstractSingleSelect#AbstractSingleSelect()} should be used. *

* * @param dataCommunicator * the data communicator to use, not null */ protected AbstractSingleSelect(DataCommunicator dataCommunicator) { super(dataCommunicator); init(); } /** * Adds a selection listener to this select. The listener is called when the * selection is changed either by the user or programmatically. * * @param listener * the selection listener, not null * @return a registration for the listener */ public Registration addSelectionListener( SingleSelectionListener listener) { return addListener(SingleSelectionEvent.class, listener, SingleSelectionListener.SELECTION_CHANGE_METHOD); } /** * Returns the currently selected item, or an empty optional if no item is * selected. * * @return an optional of the selected item if any, an empty optional * otherwise */ public Optional getSelectedItem() { return Optional.ofNullable(selectedItem); } /** * Sets the current selection to the given item or clears selection if given * {@code null}. * * @param item * the item to select or {@code null} to clear selection */ public void setSelectedItem(T item) { setSelectedItem(item, false); } /** * Returns the current value of this object which is the currently selected * item. *

* The call is delegated to {@link #getSelectedItem()} * * @return the current selection, may be {@code null} * * @see #getSelectedItem() * @see Single#getSelectedItem */ @Override public T getValue() { return getSelectedItem().orElse(null); } /** * Sets the value of this object which is an item to select. If the new * value is not equal to {@code getValue()}, fires a value change event. If * value is {@code null} then it deselects currently selected item. *

* The call is delegated to {@link #setSelectedItem(Object)}. * * @see #setSelectedItem(Object) * @see Single#setSelectedItem(Object) * * @param value * the item to select or {@code null} to clear selection */ @Override public void setValue(T value) { setSelectedItem(value); } @Override public Registration addValueChangeListener( HasValue.ValueChangeListener listener) { return addSelectionListener( event -> listener.valueChange(new ValueChangeEvent<>(this, event.getOldValue(), event.isUserOriginated()))); } @Override protected AbstractSingleSelectState getState() { return (AbstractSingleSelectState) super.getState(); } @Override protected AbstractSingleSelectState getState(boolean markAsDirty) { return (AbstractSingleSelectState) super.getState(markAsDirty); } @Override public void setRequiredIndicatorVisible(boolean visible) { super.setRequiredIndicatorVisible(visible); } @Override public boolean isRequiredIndicatorVisible() { return super.isRequiredIndicatorVisible(); } @Override public void setReadOnly(boolean readOnly) { super.setReadOnly(readOnly); } @Override public boolean isReadOnly() { return super.isReadOnly(); } /** * Returns the item that the given key is assigned to, or {@code null} if * there is no such item. * * @param key * the key whose item to return * @return the associated item if any, {@code null} otherwise. */ protected T keyToItem(String key) { return getDataCommunicator().getKeyMapper().get(key); } /** * Returns whether the given item is currently selected. * * @param item * the item to check, not null * @return {@code true} if the item is selected, {@code false} otherwise */ public boolean isSelected(T item) { if (Objects.equals(selectedItem, item)) { return true; } if (item == null || selectedItem == null) { return false; } return Objects.equals(getDataProvider().getId(selectedItem), getDataProvider().getId(item)); } @Override protected Element writeItem(Element design, T item, DesignContext context) { Element element = super.writeItem(design, item, context); if (isSelected(item)) { element.attr("selected", true); } return element; } @Override protected void readItems(Element design, DesignContext context) { Set selected = new HashSet<>(); List items = design.children().stream() .map(child -> readItem(child, selected, context)) .collect(Collectors.toList()); if (!items.isEmpty()) { setItems(items); } selected.forEach(this::setValue); } /** * Reads an Item from a design and inserts it into the data source. * Hierarchical select components should override this method to recursively * recursively read any child items as well. * * @param child * a child element representing the item * @param selected * A set accumulating selected items. If the item that is read is * marked as selected, its item id should be added to this set. * @param context * the DesignContext instance used in parsing * @return the item id of the new item * * @throws DesignException * if the tag name of the {@code child} element is not * {@code option}. */ protected T readItem(Element child, Set selected, DesignContext context) { T item = readItem(child, context); if (child.hasAttr("selected")) { selected.add(item); } return item; } @Override protected Collection getCustomAttributes() { Collection attributes = super.getCustomAttributes(); // "value" is not an attribute for the component. "selected" attribute // is used in "option"'s tag to mark selection which implies value for // single select component attributes.add("value"); return attributes; } private void init() { registerRpc(new SelectionServerRpc() { @Override public void select(String key) { setSelectedItem(keyToItem(key), true); } @Override public void deselect(String key) { T item = keyToItem(key); if (isSelected(item)) { setSelectedItem(null, true); } } }); addDataGenerator(new DataGenerator() { @Override public void generateData(T item, JsonObject jsonObject) { if (isSelected(item)) { // Deferred update of state. updateSelectedItemState(item); } } @Override public void refreshData(T item) { if (isSelected(item)) { selectedItem = item; // Invalidate old data updateSelectedItemState(null); } } }); } /** * This method updates the internal selection state of the server-side of * {@code AbstractSingleSelect}. * * @param value * the value that should be selected * @param userOriginated * {@code true} if selection was done by user, {@code false} if * not * * @since 8.5 */ protected void setSelectedItem(T value, boolean userOriginated) { if (isSelected(value)) { return; } // Update selection T oldValue = selectedItem; selectedItem = value; // Re-generate selected item data if (oldValue != null) { getDataCommunicator().refresh(oldValue); } if (value != null) { getDataCommunicator().refresh(value); } // Deselection can be handled immediately updateSelectedItemState(value); // Update diffstate to make sure null can be selected later. updateDiffstate("selectedItemKey", Json.createObject()); fireEvent(new SingleSelectionEvent<>(AbstractSingleSelect.this, oldValue, userOriginated)); } /** * This method updates the shared selection state of the * {@code AbstractSingleSelect}. * * @param value * the value that is selected; may be {@code null} * * @since 8.5 */ protected void updateSelectedItemState(T value) { // FIXME: If selecting a value that does not exist, this will leave and // extra object in the key mapper that will not be dropped any time. getState().selectedItemKey = value != null ? getDataCommunicator().getKeyMapper().key(value) : null; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 27467 Content-Disposition: inline; filename="AbstractSplitPanel.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "833899ee8cb1be6962fb5f35de2b327ca95d01cf" /* * Copyright 2000-2022 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.HasUserOriginated; 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.Registration; 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. * * AbstractSplitPanel 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 final AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() { @Override public void splitterClick(MouseEventDetails mouseDetails) { fireEvent(new SplitterClickEvent(AbstractSplitPanel.this, mouseDetails)); } @Override public void setSplitterPosition(float position) { float oldPosition = getSplitPosition(); getSplitterState().position = position; fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, true, oldPosition, getSplitPositionUnit(), 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, 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; } } /** * Gets an iterator to the collection of contained components. Using this * iterator it is possible to step through all components contained in this * container and remove components from it. * * @return the component iterator. */ @Override public Iterator 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); } float oldPosition = getSplitPosition(); Unit oldUnit = getSplitPositionUnit(); SplitterState splitterState = getSplitterState(); splitterState.position = pos; splitterState.positionUnit = unit.getSymbol(); splitterState.positionReversed = reverse; posUnit = unit; fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, false, oldPosition, oldUnit, 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 true if locked, false otherwise. */ public void setLocked(boolean locked) { getSplitterState().locked = locked; } /** * Is the SplitPanel handle locked (user not allowed to change split * position by dragging). * * @return true if locked, false otherwise. */ public boolean isLocked() { return getSplitterState(false).locked; } /** * SplitterClickListener interface for listening for * SplitterClickEvent fired by a SplitPanel. * * @see SplitterClickEvent * @since 6.2 */ @FunctionalInterface 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 */ @FunctionalInterface 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 implements HasUserOriginated { private final float oldPosition; private final Unit oldUnit; private final float position; private final Unit unit; private final boolean userOriginated; /** * Creates a split position change event. * * @param source * split panel from which the event originates * @param userOriginated * true if the event is directly based on user actions * @param oldPosition * old split position * @param oldUnit * old unit of split position * @param position * new split position * @param unit * new split position unit * @since 8.1 */ public SplitPositionChangeEvent(final Component source, final boolean userOriginated, final float oldPosition, final Unit oldUnit, final float position, final Unit unit) { super(source); this.userOriginated = userOriginated; this.oldUnit = oldUnit; this.oldPosition = oldPosition; this.position = position; this.unit = unit; } /** * Returns the new split position that triggered this change event. * * @return the new value of split position */ public float getSplitPosition() { return position; } /** * Returns the new split position unit that triggered this change event. * * @return the new value of split position */ public Unit getSplitPositionUnit() { return unit; } /** * Returns the position of the split before this change event occurred. * * @since 8.1 * * @return the split position previously set to the source of this event */ public float getOldSplitPosition() { return oldPosition; } /** * Returns the position unit of the split before this change event * occurred. * * @since 8.1 * * @return the split position unit previously set to the source of this * event */ public Unit getOldSplitPositionUnit() { return oldUnit; } /** * {@inheritDoc} * * @since 8.1 */ @Override public boolean isUserOriginated() { return userOriginated; } } public Registration addSplitterClickListener( SplitterClickListener listener) { return addListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class, listener, SplitterClickListener.clickMethod); } @Deprecated public void removeSplitterClickListener(SplitterClickListener listener) { removeListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class, listener); } /** * Register a listener to handle {@link SplitPositionChangeEvent}s. * * @since 8.0 * @param listener * {@link SplitPositionChangeListener} to be registered. */ public Registration addSplitPositionChangeListener( SplitPositionChangeListener listener) { return addListener(SplitPositionChangeEvent.class, listener, SplitPositionChangeListener.moveMethod); } /** * Removes a {@link SplitPositionChangeListener}. * * @since 7.5.0 * @param listener * SplitPositionChangeListener to be removed. */ @Deprecated 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 getCustomAttributes() { Collection 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 = 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; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 8948 Content-Disposition: inline; filename="AbstractTextField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "fe17a3c17255c34d69b1324741742348b214ef9b" /* * Copyright 2000-2022 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.util.Collection; import java.util.Objects; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.shared.Registration; import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.ValueChangeMode; import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc; import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc; import com.vaadin.shared.ui.textfield.AbstractTextFieldState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import elemental.json.Json; /** * Abstract base class for text input components. * * @author Vaadin Ltd. * @since 8.0 */ public abstract class AbstractTextField extends AbstractField implements HasValueChangeMode, FieldEvents.FocusNotifier, FieldEvents.BlurNotifier { private final class AbstractTextFieldServerRpcImpl implements AbstractTextFieldServerRpc { @Override public void setText(String text, int cursorPosition) { updateDiffstate("text", Json.create(text)); lastKnownCursorPosition = cursorPosition; setValue(text, true); } } private final class AbstractTextFieldFocusAndBlurRpcImpl implements FocusAndBlurServerRpc { @Override public void blur() { fireEvent(new BlurEvent(AbstractTextField.this)); } @Override public void focus() { fireEvent(new FocusEvent(AbstractTextField.this)); } } private int lastKnownCursorPosition = -1; /** * Creates a new instance. */ protected AbstractTextField() { registerRpc(new AbstractTextFieldServerRpcImpl()); registerRpc(new AbstractTextFieldFocusAndBlurRpcImpl()); } /** * Sets the value of this text field. If the new value is not equal to * {@code getValue()}, fires a {@link ValueChangeEvent}. Throws * {@code NullPointerException} if the value is null. * * @param value * the new value, not {@code null} * @throws NullPointerException * if {@code value} is {@code null} */ @Override public void setValue(String value) { Objects.requireNonNull(value, "value cannot be null"); setValue(value, false); } /** * Returns the maximum number of characters in the field. Value -1 is * considered unlimited. Terminal may however have some technical limits. * * @return the maxLength */ public int getMaxLength() { return getState(false).maxLength; } /** * Sets the maximum number of characters in the field. Value -1 is * considered unlimited. Terminal may however have some technical limits. * * @param maxLength * the maxLength to set */ public void setMaxLength(int maxLength) { getState().maxLength = maxLength; } /** * Returns the current placeholder text. * * @see #setPlaceholder(String) * @return the placeholder text */ public String getPlaceholder() { return getState(false).placeholder; } /** * Sets the placeholder text. The placeholder is text that is displayed when * the field would otherwise be empty, to prompt the user for input. * * @param placeholder * the placeholder text to set * @since 8.0 */ public void setPlaceholder(String placeholder) { getState().placeholder = placeholder; } @Override public String getValue() { return getState(false).text; } /** * Selects all text in the field. *

* As a side effect the field will become focused. */ public void selectAll() { getRpcProxy(AbstractTextFieldClientRpc.class).selectAll(); focus(); } /** * Sets the range of text to be selected. *

* As a side effect the field will become focused. * * @param start * the position of the first character to be selected * @param length * the number of characters to be selected */ public void setSelection(int start, int length) { getRpcProxy(AbstractTextFieldClientRpc.class).selectRange(start, length); focus(); } /** * Sets the cursor position in the field. As a side effect the field will * become focused. * * @param pos * the position for the cursor */ public void setCursorPosition(int pos) { setSelection(pos, 0); } /** * Returns the last known cursor position of the field. * * @return the last known cursor position */ public int getCursorPosition() { return lastKnownCursorPosition; } /** * Adds a {@link FocusListener} to this component, which gets fired when * this component receives keyboard focus. * * @param listener * the focus listener * @return a registration for the listener * * @see Registration */ @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } /** * Adds a {@link BlurListener} to this component, which gets fired when this * component loses keyboard focus. * * @param listener * the blur listener * @return a registration for the listener * * @see Registration */ @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } @Override public void setValueChangeMode(ValueChangeMode mode) { getState().valueChangeMode = mode; } @Override public ValueChangeMode getValueChangeMode() { return getState(false).valueChangeMode; } @Override public void setValueChangeTimeout(int timeout) { if (timeout < 0) { throw new IllegalArgumentException( "Timeout must be greater than 0"); } getState().valueChangeTimeout = timeout; } @Override public int getValueChangeTimeout() { return getState(false).valueChangeTimeout; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); Attributes attr = design.attributes(); if (attr.hasKey("maxlength")) { setMaxLength(DesignAttributeHandler.readAttribute("maxlength", attr, Integer.class)); } } @Override protected AbstractTextFieldState getState() { return (AbstractTextFieldState) super.getState(); } @Override protected AbstractTextFieldState getState(boolean markAsDirty) { return (AbstractTextFieldState) super.getState(markAsDirty); } @Override protected void doSetValue(String value) { getState().text = value; } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); AbstractTextField def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("maxlength", attr, getMaxLength(), def.getMaxLength(), Integer.class, designContext); } @Override protected Collection getCustomAttributes() { Collection customAttributes = super.getCustomAttributes(); customAttributes.add("maxlength"); // to prevent this appearing in output customAttributes.add("max-length"); customAttributes.add("cursor-position"); return customAttributes; } @Override public String getEmptyValue() { return ""; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1890 Content-Disposition: inline; filename="Accordion.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "7837a52299b6f214cfc12c6529bf267db5b5e8ab" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.accordion.AccordionState; /** * An accordion is a component similar to a {@link TabSheet}, but with a * vertical orientation and the selected component presented between tabs. * * Closable tabs are not supported by the accordion. * * The {@link Accordion} can be styled with the .v-accordion, .v-accordion-item, * .v-accordion-item-first and .v-accordion-item-caption styles. * * @see TabSheet */ public class Accordion extends TabSheet { /** * Creates an empty accordion. */ public Accordion() { super(); } /** * Constructs a new accordion containing the given components. * * @param components * The components to add to the accordion. Each component will be * added to a separate tab. */ public Accordion(Component... components) { this(); addComponents(components); } /* * (non-Javadoc) * * @see com.vaadin.ui.TabSheet#getState() */ @Override protected AccordionState getState() { return (AccordionState) super.getState(); } @Override protected AccordionState getState(boolean markAsDirty) { return (AccordionState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5114 Content-Disposition: inline; filename="Alignment.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3cd2686ac609b92fb9db716e930c68fbbdcb72c0" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.AlignmentInfo.Bits; /** * Class containing information about alignment of a component. Use the * pre-instantiated classes. */ @SuppressWarnings("serial") public final class Alignment implements Serializable { public static final Alignment TOP_RIGHT = new Alignment( Bits.ALIGNMENT_TOP + Bits.ALIGNMENT_RIGHT); public static final Alignment TOP_LEFT = new Alignment( Bits.ALIGNMENT_TOP + Bits.ALIGNMENT_LEFT); public static final Alignment TOP_CENTER = new Alignment( Bits.ALIGNMENT_TOP + Bits.ALIGNMENT_HORIZONTAL_CENTER); public static final Alignment MIDDLE_RIGHT = new Alignment( Bits.ALIGNMENT_VERTICAL_CENTER + Bits.ALIGNMENT_RIGHT); public static final Alignment MIDDLE_LEFT = new Alignment( Bits.ALIGNMENT_VERTICAL_CENTER + Bits.ALIGNMENT_LEFT); public static final Alignment MIDDLE_CENTER = new Alignment( Bits.ALIGNMENT_VERTICAL_CENTER + Bits.ALIGNMENT_HORIZONTAL_CENTER); public static final Alignment BOTTOM_RIGHT = new Alignment( Bits.ALIGNMENT_BOTTOM + Bits.ALIGNMENT_RIGHT); public static final Alignment BOTTOM_LEFT = new Alignment( Bits.ALIGNMENT_BOTTOM + Bits.ALIGNMENT_LEFT); public static final Alignment BOTTOM_CENTER = new Alignment( Bits.ALIGNMENT_BOTTOM + Bits.ALIGNMENT_HORIZONTAL_CENTER); private final int bitMask; public Alignment(int bitMask) { this.bitMask = bitMask; } /** * Returns a bitmask representation of the alignment value. Used internally * by terminal. * * @return the bitmask representation of the alignment value */ public int getBitMask() { return bitMask; } /** * Checks if component is aligned to the top of the available space. * * @return true if aligned top */ public boolean isTop() { return (bitMask & Bits.ALIGNMENT_TOP) == Bits.ALIGNMENT_TOP; } /** * Checks if component is aligned to the bottom of the available space. * * @return true if aligned bottom */ public boolean isBottom() { return (bitMask & Bits.ALIGNMENT_BOTTOM) == Bits.ALIGNMENT_BOTTOM; } /** * Checks if component is aligned to the left of the available space. * * @return true if aligned left */ public boolean isLeft() { return (bitMask & Bits.ALIGNMENT_LEFT) == Bits.ALIGNMENT_LEFT; } /** * Checks if component is aligned to the right of the available space. * * @return true if aligned right */ public boolean isRight() { return (bitMask & Bits.ALIGNMENT_RIGHT) == Bits.ALIGNMENT_RIGHT; } /** * Checks if component is aligned middle (vertically center) of the * available space. * * @return true if aligned bottom */ public boolean isMiddle() { return (bitMask & Bits.ALIGNMENT_VERTICAL_CENTER) == Bits.ALIGNMENT_VERTICAL_CENTER; } /** * Checks if component is aligned center (horizontally) of the available * space. * * @return true if aligned center */ public boolean isCenter() { return (bitMask & Bits.ALIGNMENT_HORIZONTAL_CENTER) == Bits.ALIGNMENT_HORIZONTAL_CENTER; } /** * Returns string representation of vertical alignment. * * @return vertical alignment as CSS value */ public String getVerticalAlignment() { if (isBottom()) { return "bottom"; } else if (isMiddle()) { return "middle"; } return "top"; } /** * Returns string representation of horizontal alignment. * * @return horizontal alignment as CSS value */ public String getHorizontalAlignment() { if (isRight()) { return "right"; } else if (isCenter()) { return "center"; } return "left"; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if ((obj == null) || (obj.getClass() != this.getClass())) { return false; } Alignment a = (Alignment) obj; return bitMask == a.bitMask; } @Override public int hashCode() { return bitMask; } @Override public String toString() { return String.valueOf(bitMask); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2470 Content-Disposition: inline; filename="Audio.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "2538f543d87f63a832f8fa14a7c120c4c1041e17" /* * Copyright 2000-2022 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 com.vaadin.server.Resource; import com.vaadin.shared.ui.audio.AudioState; /** * The Audio component translates into an HTML5 <audio> element and as * such is only supported in browsers that support HTML5 media markup. Browsers * that do not support HTML5 display the text or HTML set by calling * {@link #setAltText(String)}. * * A flash-player fallback can be implemented by setting HTML content allowed ( * {@link #setHtmlContentAllowed(boolean)} and calling * {@link #setAltText(String)} with the flash player markup. An example of flash * fallback can be found at the Mozilla Developer Network. * * Multiple sources can be specified. Which of the sources is used is selected * by the browser depending on which file formats it supports. See * wikipedia for a * table of formats supported by different browsers. * * @author Vaadin Ltd * @since 6.7.0 */ public class Audio extends AbstractMedia { public Audio() { this("", null); } /** * @param caption * The caption of the audio component. */ public Audio(String caption) { this(caption, null); } /** * @param caption * The caption of the audio component * @param source * The audio file to play. */ public Audio(String caption, Resource source) { setCaption(caption); setSource(source); setShowControls(true); } @Override protected AudioState getState() { return (AudioState) super.getState(); } @Override protected AudioState getState(boolean markAsDirty) { return (AudioState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1902 Content-Disposition: inline; filename="BrowserFrame.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "f909522024b58b0603d01d0a1c190f128642381a" /* * Copyright 2000-2022 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 com.vaadin.server.Resource; import com.vaadin.shared.ui.browserframe.BrowserFrameState; /** * A component displaying an embedded web page. Implemented as a HTML * iframe element. * * @author Vaadin Ltd. * @since 7.0 */ public class BrowserFrame extends AbstractEmbedded { /** * Creates a new empty browser frame. */ public BrowserFrame() { } /** * Creates a new empty browser frame with the given caption. * * @param caption * The caption for the component */ public BrowserFrame(String caption) { setCaption(caption); } /** * Creates a new browser frame with the given caption and content. * * @param caption * The caption for the component. * @param source * A Resource representing the Web page that should be displayed. */ public BrowserFrame(String caption, Resource source) { this(caption); setSource(source); } @Override protected BrowserFrameState getState() { return (BrowserFrameState) super.getState(); } @Override protected BrowserFrameState getState(boolean markAsDirty) { return (BrowserFrameState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 20781 Content-Disposition: inline; filename="Button.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "79e24a8e2e68c6aa9a35f8f9a6d2632d0effbe4d" /* * Copyright 2000-2022 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.lang.reflect.Method; import java.util.Collection; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.Action; import com.vaadin.event.SerializableEventListener; import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; import com.vaadin.event.ShortcutListener; import com.vaadin.server.Resource; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.button.ButtonServerRpc; import com.vaadin.shared.ui.button.ButtonState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignFormatter; import com.vaadin.util.ReflectTools; import elemental.json.Json; /** * A generic button component. * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class Button extends AbstractFocusable implements Action.ShortcutNotifier { private ButtonServerRpc rpc = new ButtonServerRpc() { @Override public void click(MouseEventDetails mouseEventDetails) { fireClick(mouseEventDetails); } @Override public void disableOnClick() throws RuntimeException { setEnabled(false); // Makes sure the enabled=false state is noticed at once - otherwise // a following setEnabled(true) call might have no effect. see // ticket #10030 updateDiffstate("enabled", Json.create(false)); } }; /** * Creates a new push button. */ public Button() { registerRpc(rpc); } /** * Creates a new push button with the given caption. * * @param caption * the Button caption. */ public Button(String caption) { this(); setCaption(caption); } /** * Creates a new push button with the given icon. * * @param icon * the icon */ public Button(Resource icon) { this(); setIcon(icon); } /** * Creates a new push button with the given caption and icon. * * @param caption * the caption * @param icon * the icon */ public Button(String caption, Resource icon) { this(); setCaption(caption); setIcon(icon); } /** * Creates a new push button with a click listener. * * @param caption * the Button caption. * @param listener * the Button click listener. */ public Button(String caption, ClickListener listener) { this(caption); addClickListener(listener); } /** * Creates a new push button with a click listener. * * @param icon * the Button icon. * @param listener * the Button click listener. * @since 8.2 */ public Button(Resource icon, ClickListener listener) { this(icon); addClickListener(listener); } /** * Click event. This event is thrown, when the button is clicked. * * @author Vaadin Ltd. * @since 3.0 */ public static class ClickEvent extends Component.Event { private final MouseEventDetails details; /** * New instance of text change event. * * @param source * the Source of the event. */ public ClickEvent(Component source) { super(source); details = null; } /** * Constructor with mouse details. * * @param source * The source where the click took place * @param details * Details about the mouse click */ public ClickEvent(Component source, MouseEventDetails details) { super(source); this.details = details; } /** * Gets the Button where the event occurred. * * @return the Source of the event. */ public Button getButton() { return (Button) getSource(); } /** * Returns the mouse position (x coordinate) when the click took place. * The position is relative to the browser client area. * * @return The mouse cursor x position or -1 if unknown */ public int getClientX() { if (null != details) { return details.getClientX(); } else { return -1; } } /** * Returns the mouse position (y coordinate) when the click took place. * The position is relative to the browser client area. * * @return The mouse cursor y position or -1 if unknown */ public int getClientY() { if (null != details) { return details.getClientY(); } else { return -1; } } /** * Returns the relative mouse position (x coordinate) when the click * took place. The position is relative to the clicked component. * * @return The mouse cursor x position relative to the clicked layout * component or -1 if no x coordinate available */ public int getRelativeX() { if (null != details) { return details.getRelativeX(); } else { return -1; } } /** * Returns the relative mouse position (y coordinate) when the click * took place. The position is relative to the clicked component. * * @return The mouse cursor y position relative to the clicked layout * component or -1 if no y coordinate available */ public int getRelativeY() { if (null != details) { return details.getRelativeY(); } else { return -1; } } /** * Checks if the Alt key was down when the mouse event took place. * * @return true if Alt was down when the event occurred, false otherwise * or if unknown */ public boolean isAltKey() { if (null != details) { return details.isAltKey(); } else { return false; } } /** * Checks if the Ctrl key was down when the mouse event took place. * * @return true if Ctrl was pressed when the event occurred, false * otherwise or if unknown */ public boolean isCtrlKey() { if (null != details) { return details.isCtrlKey(); } else { return false; } } /** * Checks if the Meta key was down when the mouse event took place. * * @return true if Meta was pressed when the event occurred, false * otherwise or if unknown */ public boolean isMetaKey() { if (null != details) { return details.isMetaKey(); } else { return false; } } /** * Checks if the Shift key was down when the mouse event took place. * * @return true if Shift was pressed when the event occurred, false * otherwise or if unknown */ public boolean isShiftKey() { if (null != details) { return details.isShiftKey(); } else { return false; } } } /** * Interface for listening for a {@link ClickEvent} fired by a * {@link Component}. * * @author Vaadin Ltd. * @since 3.0 */ @FunctionalInterface public interface ClickListener extends SerializableEventListener { public static final Method BUTTON_CLICK_METHOD = ReflectTools .findMethod(ClickListener.class, "buttonClick", ClickEvent.class); /** * Called when a {@link Button} has been clicked. A reference to the * button is given by {@link ClickEvent#getButton()}. * * @param event * An event containing information about the click. */ public void buttonClick(ClickEvent event); } /** * Adds the button click listener. * * @see Registration * * @param listener * the Listener to be added. * @return a registration object for removing the listener * @since 8.0 */ public Registration addClickListener(ClickListener listener) { return addListener(ClickEvent.class, listener, ClickListener.BUTTON_CLICK_METHOD); } /** * Removes the button click listener. * * @param listener * the Listener to be removed. * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #addClickListener(ClickListener)}. */ @Deprecated public void removeClickListener(ClickListener listener) { removeListener(ClickEvent.class, listener, ClickListener.BUTTON_CLICK_METHOD); } /** * Simulates a button click, notifying all server-side listeners. *

* No action is taken if the button is disabled. */ public void click() { if (isEnabled()) { fireClick(); } } /** * Fires a click event to all listeners without any event details. *

* In subclasses, override {@link #fireClick(MouseEventDetails)} instead of * this method. */ protected void fireClick() { fireEvent(new Button.ClickEvent(this)); } /** * Fires a click event to all listeners. * * @param details * MouseEventDetails from which keyboard modifiers and other * information about the mouse click can be obtained. If the * button was clicked by a keyboard event, some of the fields may * be empty/undefined. */ protected void fireClick(MouseEventDetails details) { fireEvent(new Button.ClickEvent(this, details)); } /* * Actions */ protected ClickShortcut clickShortcut; /** * Makes it possible to invoke a click on this button by pressing the given * {@link KeyCode} and (optional) {@link ModifierKey}s.
* The shortcut is global (bound to the containing Window). * * @param keyCode * the keycode for invoking the shortcut * @param modifiers * the (optional) modifiers for invoking the shortcut, null for * none */ public void setClickShortcut(int keyCode, int... modifiers) { if (clickShortcut != null) { removeShortcutListener(clickShortcut); } clickShortcut = new ClickShortcut(this, keyCode, modifiers); addShortcutListener(clickShortcut); getState().clickShortcutKeyCode = clickShortcut.getKeyCode(); } /** * Removes the keyboard shortcut previously set with * {@link #setClickShortcut(int, int...)}. */ public void removeClickShortcut() { if (clickShortcut != null) { removeShortcutListener(clickShortcut); clickShortcut = null; getState().clickShortcutKeyCode = 0; } } /** * A {@link ShortcutListener} specifically made to define a keyboard * shortcut that invokes a click on the given button. */ public static class ClickShortcut extends ShortcutListener { protected Button button; /** * Creates a keyboard shortcut for clicking the given button using the * shorthand notation defined in {@link ShortcutAction}. * * @param button * to be clicked when the shortcut is invoked * @param shorthandCaption * the caption with shortcut keycode and modifiers indicated */ public ClickShortcut(Button button, String shorthandCaption) { super(shorthandCaption); this.button = button; } /** * Creates a keyboard shortcut for clicking the given button using the * given {@link KeyCode} and {@link ModifierKey}s. * * @param button * to be clicked when the shortcut is invoked * @param keyCode * KeyCode to react to * @param modifiers * optional modifiers for shortcut */ public ClickShortcut(Button button, int keyCode, int... modifiers) { super(null, keyCode, modifiers); this.button = button; } /** * Creates a keyboard shortcut for clicking the given button using the * given {@link KeyCode}. * * @param button * to be clicked when the shortcut is invoked * @param keyCode * KeyCode to react to */ public ClickShortcut(Button button, int keyCode) { this(button, keyCode, null); } @Override public void handleAction(Object sender, Object target) { button.click(); } } /** * Determines if a button is automatically disabled when clicked. See * {@link #setDisableOnClick(boolean)} for details. * * @return true if the button is disabled when clicked, false otherwise */ public boolean isDisableOnClick() { return getState(false).disableOnClick; } /** * Determines if a button is automatically disabled when clicked. If this is * set to true the button will be automatically disabled when clicked, * typically to prevent (accidental) extra clicks on a button. *

* Note that this is only used when the click comes from the user, not when * calling {@link #click()} method programmatically. Also, if developer * wants to re-enable the button, it needs to be done programmatically. *

* * @param disableOnClick * true to disable button when it is clicked, false otherwise */ public void setDisableOnClick(boolean disableOnClick) { getState().disableOnClick = disableOnClick; } @Override protected ButtonState getState() { return (ButtonState) super.getState(); } @Override protected ButtonState getState(boolean markAsDirty) { return (ButtonState) super.getState(markAsDirty); } /** * Sets the component's icon and alt text. *

* An alt text is shown when an image could not be loaded, and read by * assistive devices. * * @param icon * the icon to be shown with the component's caption. * @param iconAltText * String to use as alt text */ public void setIcon(Resource icon, String iconAltText) { super.setIcon(icon); getState().iconAltText = iconAltText == null ? "" : iconAltText; } /** * Returns the icon's alt text. * * @return String with the alt text */ public String getIconAlternateText() { return getState(false).iconAltText; } public void setIconAlternateText(String iconAltText) { getState().iconAltText = iconAltText; } /** * Set whether the caption text is rendered as HTML or not. You might need * to re-theme button to allow higher content than the original text style. * * If set to true, the captions are passed to the browser as html and the * developer is responsible for ensuring no harmful html is used. If set to * false, the content is passed to the browser as plain text. * * @param htmlContentAllowed * true if caption is rendered as HTML, * false otherwise * * @deprecated as of 8.0.0, use {@link #setCaptionAsHtml(boolean)} instead. */ @Deprecated public void setHtmlContentAllowed(boolean htmlContentAllowed) { getState().captionAsHtml = htmlContentAllowed; } /** * Return HTML rendering setting. * * @return true if the caption text is to be rendered as HTML, * false otherwise * * @deprecated as of 8.0.0, use {@link #isCaptionAsHtml()} instead. */ @Deprecated public boolean isHtmlContentAllowed() { return getState(false).captionAsHtml; } /* * (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) { super.readDesign(design, designContext); Attributes attr = design.attributes(); String content; // plain-text (default is html) Boolean plain = DesignAttributeHandler .readAttribute(DESIGN_ATTR_PLAIN_TEXT, attr, Boolean.class); if (plain == null || !plain) { setCaptionAsHtml(true); content = design.html(); } else { // content is not intended to be interpreted as HTML, // so html entities need to be decoded content = DesignFormatter.decodeFromTextNode(design.html()); } setCaption(content); if (attr.hasKey("icon-alt")) { setIconAlternateText(DesignAttributeHandler .readAttribute("icon-alt", attr, String.class)); } // click-shortcut removeClickShortcut(); ShortcutAction action = DesignAttributeHandler .readAttribute("click-shortcut", attr, ShortcutAction.class); if (action != null) { setClickShortcut(action.getKeyCode(), action.getModifiers()); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#getCustomAttributes() */ @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add(DESIGN_ATTR_PLAIN_TEXT); result.add("caption"); result.add("icon-alt"); result.add("icon-alternate-text"); result.add("click-shortcut"); result.add("html-content-allowed"); result.add("caption-as-html"); return result; } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element * , com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); Attributes attr = design.attributes(); Button def = designContext.getDefaultInstance(this); String content = getCaption(); if (content != null) { design.html(content); } // plain-text (default is html) if (!isHtmlContentAllowed()) { design.attr(DESIGN_ATTR_PLAIN_TEXT, true); // encode HTML entities if (content != null) { design.html(DesignFormatter.encodeForTextNode(content)); } } // icon-alt DesignAttributeHandler.writeAttribute("icon-alt", attr, getIconAlternateText(), def.getIconAlternateText(), String.class, designContext); // click-shortcut if (clickShortcut != null) { DesignAttributeHandler.writeAttribute("click-shortcut", attr, clickShortcut, null, ShortcutAction.class, designContext); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 14394 Content-Disposition: inline; filename="CheckBox.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "ec14553cbdd19e4bb355c2d7bd60abccb8241216" /* * Copyright 2000-2022 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.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.StringTokenizer; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc; import com.vaadin.shared.ui.checkbox.CheckBoxState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; public class CheckBox extends AbstractField implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { private CheckBoxServerRpc rpc = (boolean checked, MouseEventDetails mouseEventDetails) -> { if (isReadOnly()) { return; } /* * Client side updates the state before sending the event so we need to * make sure the cached state is updated to match the client. If we do * not do this, a reverting setValue() call in a listener will not cause * the new state to be sent to the client. * * See #11028, #10030. */ getUI().getConnectorTracker().getDiffState(CheckBox.this).put("checked", checked); final Boolean oldValue = getValue(); final Boolean newValue = checked; if (!newValue.equals(oldValue)) { // The event is only sent if the switch state is changed setValue(newValue, true); } }; private CheckBoxInputElement checkBoxInputElement = null; private CheckBoxLabelElement checkBoxLabelElement = null; /** * The inner input element of the CheckBox. */ public static class CheckBoxInputElement implements HasStyleNames { private final CheckBox checkBox; private CheckBoxInputElement(CheckBox checkBox) { this.checkBox = checkBox; } @Override // Implementation copied from AbstractComponent public String getStyleName() { // replaced String with StringBuilder StringBuilder s = new StringBuilder(); if (ComponentStateUtil .hasStyles(checkBox.getState(false).inputStyles)) { for (final Iterator it = checkBox .getState(false).inputStyles.iterator(); it .hasNext();) { s.append(it.next()); if (it.hasNext()) { s.append(" "); } } } return s.toString(); } @Override // Implementation copied from AbstractComponent public void setStyleName(String style) { if (style == null || style.isEmpty()) { checkBox.getState().inputStyles = null; return; } if (checkBox.getState().inputStyles == null) { checkBox.getState().inputStyles = new ArrayList<>(); } List styles = checkBox.getState().inputStyles; styles.clear(); StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { styles.add(tokenizer.nextToken()); } } @Override // Implementation copied from AbstractComponent public void addStyleName(String style) { if (style == null || style.isEmpty()) { return; } if (checkBox.getState().inputStyles != null && checkBox.getState().inputStyles.contains(style)) { return; } if (style.contains(" ")) { // Split space separated style names and add them one by one. StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { addStyleName(tokenizer.nextToken()); } return; } if (checkBox.getState().inputStyles == null) { checkBox.getState().inputStyles = new ArrayList<>(); } List styles = checkBox.getState().inputStyles; styles.add(style); } @Override // Implementation copied from AbstractComponent public void removeStyleName(String style) { if (ComponentStateUtil.hasStyles(checkBox.getState().inputStyles)) { StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { checkBox.getState().inputStyles .remove(tokenizer.nextToken()); } } } } /** * The inner label element of the CheckBox. */ public static class CheckBoxLabelElement implements HasStyleNames { private final CheckBox checkBox; private CheckBoxLabelElement(CheckBox checkBox) { this.checkBox = checkBox; } @Override // Implementation copied from AbstractComponent public String getStyleName() { // replaced String with StringBuilder StringBuilder s = new StringBuilder(); if (ComponentStateUtil .hasStyles(checkBox.getState(false).labelStyles)) { for (final Iterator it = checkBox .getState(false).labelStyles.iterator(); it .hasNext();) { s.append(it.next()); if (it.hasNext()) { s.append(" "); } } } return s.toString(); } @Override // Implementation copied from AbstractComponent public void setStyleName(String style) { if (style == null || style.isEmpty()) { checkBox.getState().labelStyles = null; return; } if (checkBox.getState().labelStyles == null) { checkBox.getState().labelStyles = new ArrayList<>(); } List styles = checkBox.getState().labelStyles; styles.clear(); StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { styles.add(tokenizer.nextToken()); } } @Override // Implementation copied from AbstractComponent public void addStyleName(String style) { if (style == null || style.isEmpty()) { return; } if (checkBox.getState().labelStyles != null && checkBox.getState().labelStyles.contains(style)) { return; } if (style.contains(" ")) { // Split space separated style names and add them one by one. StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { addStyleName(tokenizer.nextToken()); } return; } if (checkBox.getState().labelStyles == null) { checkBox.getState().labelStyles = new ArrayList<>(); } List styles = checkBox.getState().labelStyles; styles.add(style); } @Override // Implementation copied from AbstractComponent public void removeStyleName(String style) { if (ComponentStateUtil.hasStyles(checkBox.getState().labelStyles)) { StringTokenizer tokenizer = new StringTokenizer(style, " "); while (tokenizer.hasMoreTokens()) { checkBox.getState().labelStyles .remove(tokenizer.nextToken()); } } } } /** * Creates a new checkbox. */ public CheckBox() { registerRpc(rpc); registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); setValue(Boolean.FALSE); } /** * Creates a new checkbox with a set caption. * * @param caption * the Checkbox caption. */ public CheckBox(String caption) { this(); setCaption(caption); } /** * Creates a new checkbox with a caption and a set initial state. * * @param caption * the caption of the checkbox * @param initialState * the initial state of the checkbox */ public CheckBox(String caption, boolean initialState) { this(caption); setValue(initialState); } @Override public Boolean getValue() { return getState(false).checked; } /** * Sets the value of this CheckBox. If the new value is not equal to * {@code getValue()}, fires a {@link ValueChangeEvent}. Throws * {@code NullPointerException} if the value is null. * * @param value * the new value, not {@code null} * @throws NullPointerException * if {@code value} is {@code null} */ @Override public void setValue(Boolean value) { Objects.requireNonNull(value, "CheckBox value must not be null"); super.setValue(value); } /** * Sets the value of this CheckBox. If the new value is not equal to * {@code getValue()}, fires a {@link ValueChangeEvent}. Throws * {@code NullPointerException} if the value is null. * * @param value * the new value, not {@code null} * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. * @throws NullPointerException * if {@code value} is {@code null} */ @Override protected boolean setValue(Boolean value, boolean userOriginated) { Objects.requireNonNull(value, "CheckBox value must not be null"); return super.setValue(value, userOriginated); } @Override public Boolean getEmptyValue() { return false; } @Override protected CheckBoxState getState() { return (CheckBoxState) super.getState(); } @Override protected CheckBoxState getState(boolean markAsDirty) { return (CheckBoxState) super.getState(markAsDirty); } @Override protected void doSetValue(Boolean value) { getState().checked = value; } @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element, * com.vaadin.ui.declarative.DesignContext) */ @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); if (design.hasAttr("checked")) { this.setValue(DesignAttributeHandler.readAttribute("checked", design.attributes(), Boolean.class), false); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractField#getCustomAttributes() */ @Override protected Collection getCustomAttributes() { Collection attributes = super.getCustomAttributes(); attributes.add("checked"); return attributes; } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractField#writeDesign(org.jsoup.nodes.Element, * com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); CheckBox def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("checked", attr, getValue(), def.getValue(), Boolean.class, designContext); } /** * Returns the {@link CheckBoxInputElement} element to manipulate the style * name of the {@code input} element of the {@link CheckBox}. * * @since 8.7 * @return the current {@link CheckBoxInputElement}, not {@code null}. */ public CheckBoxInputElement getInputElement() { if (checkBoxInputElement == null) { checkBoxInputElement = new CheckBoxInputElement(this); } return checkBoxInputElement; } /** * Returns the {@link CheckBoxLabelElement} element to manipulate the style * name of the {@code label} element of the {@link CheckBox}. * * @since 8.7 * @return the current {@link CheckBoxLabelElement}, not {@code null}. */ public CheckBoxLabelElement getLabelElement() { if (checkBoxLabelElement == null) { checkBoxLabelElement = new CheckBoxLabelElement(this); } return checkBoxLabelElement; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 8635 Content-Disposition: inline; filename="CheckBoxGroup.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "79d5d47768bb22868f56b80ff6791b46ac8138bd" /* * Copyright 2000-2022 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.util.Collection; import java.util.Objects; import java.util.Set; import org.jsoup.nodes.Element; import com.vaadin.data.HasDataProvider; import com.vaadin.data.provider.DataProvider; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.server.SerializablePredicate; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.ListingJsonConstants; import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignFormatter; /** * A group of Checkboxes. Individual checkboxes are made from items supplied by * a {@link DataProvider}. Checkboxes may have captions and icons. * * @param * item type * @author Vaadin Ltd * @since 8.0 */ public class CheckBoxGroup extends AbstractMultiSelect implements FocusNotifier, BlurNotifier, HasDataProvider { private DescriptionGenerator descriptionGenerator = item -> null; /** * Constructs a new CheckBoxGroup with caption. * * @param caption * caption text */ public CheckBoxGroup(String caption) { this(); setCaption(caption); } /** * Constructs a new CheckBoxGroup with caption and DataProvider. * * @param caption * the caption text * @param dataProvider * the data provider, not null * @see HasDataProvider#setDataProvider(DataProvider) */ public CheckBoxGroup(String caption, DataProvider dataProvider) { this(caption); setDataProvider(dataProvider); } /** * Constructs a new CheckBoxGroup with caption and DataProvider containing * given items. * * @param caption * the caption text * @param items * the data items to use, not null * @see #setItems(Collection) */ public CheckBoxGroup(String caption, Collection items) { this(caption, DataProvider.ofCollection(items)); } /** * Constructs a new CheckBoxGroup. */ public CheckBoxGroup() { registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); addDataGenerator((item, jsonObject) -> { String description = getItemDescriptionGenerator().apply(item); if (description != null) { jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION, description); } }); } /** * Sets whether html is allowed in the item captions. If set to true, the * captions are passed to the browser as html and the developer is * responsible for ensuring no harmful html is used. If set to false, the * content is passed to the browser as plain text. * * @param htmlContentAllowed * true if the captions are used as html, false if used as plain * text */ public void setHtmlContentAllowed(boolean htmlContentAllowed) { getState().htmlContentAllowed = htmlContentAllowed; } /** * Checks whether captions are interpreted as html or plain text. * * @return true if the captions are used as html, false if used as plain * text * @see #setHtmlContentAllowed(boolean) */ public boolean isHtmlContentAllowed() { return getState(false).htmlContentAllowed; } @Override protected CheckBoxGroupState getState() { return (CheckBoxGroupState) super.getState(); } @Override protected CheckBoxGroupState getState(boolean markAsDirty) { return (CheckBoxGroupState) super.getState(markAsDirty); } @Override public IconGenerator getItemIconGenerator() { return super.getItemIconGenerator(); } @Override public void setItemIconGenerator(IconGenerator itemIconGenerator) { super.setItemIconGenerator(itemIconGenerator); } @Override public SerializablePredicate getItemEnabledProvider() { return super.getItemEnabledProvider(); } @Override public void setItemEnabledProvider( SerializablePredicate itemEnabledProvider) { super.setItemEnabledProvider(itemEnabledProvider); } @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } /** * Sets the description generator that is used for generating descriptions * for items. Description is shown as a tooltip when hovering on * corresponding element. If the generator returns {@code null}, no tooltip * is shown. * * * @param descriptionGenerator * the item description generator to set, not {@code null} * * @since 8.2 */ public void setItemDescriptionGenerator( DescriptionGenerator descriptionGenerator) { Objects.requireNonNull(descriptionGenerator); if (this.descriptionGenerator != descriptionGenerator) { this.descriptionGenerator = descriptionGenerator; getDataProvider().refreshAll(); } } /** * Gets the item description generator. * * @return the item description generator * * @since 8.2 */ public DescriptionGenerator getItemDescriptionGenerator() { return descriptionGenerator; } @Override protected void readItems(Element design, DesignContext context) { setItemEnabledProvider(new DeclarativeItemEnabledProvider<>()); super.readItems(design, context); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected T readItem(Element child, Set selected, DesignContext context) { T item = super.readItem(child, selected, context); SerializablePredicate provider = getItemEnabledProvider(); if (provider instanceof DeclarativeItemEnabledProvider) { if (child.hasAttr("disabled")) { ((DeclarativeItemEnabledProvider) provider).addDisabled(item); } } else { throw new IllegalStateException(String.format("Don't know how " + "to disable item using current item enabled provider '%s'", provider.getClass().getName())); } return item; } @Override protected Element writeItem(Element design, T item, DesignContext context) { Element elem = super.writeItem(design, item, context); if (!getItemEnabledProvider().test(item)) { elem.attr("disabled", true); } if (isHtmlContentAllowed()) { // need to unencode HTML entities. AbstractMultiSelect.writeDesign // can't check if HTML content is allowed, so it always encodes // entities like '>', '<' and '&'; in case HTML content is allowed // this is undesirable so we need to unencode entities. Entities // other than '<' and '>' will be taken care by Jsoup. elem.html(DesignFormatter.decodeFromTextNode(elem.html())); } return elem; } @Override public DataProvider getDataProvider() { return internalGetDataProvider(); } @Override public void setDataProvider(DataProvider dataProvider) { internalSetDataProvider(dataProvider); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2039 Content-Disposition: inline; filename="ColorPicker.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "b50383cbdafb54eb58ce85e47de4ddd49dd1f9dd" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.colorpicker.Color; import com.vaadin.shared.ui.colorpicker.ColorPickerState; /** * A class that defines default (button-like) implementation for a color picker * component. * * @since 7.0.0 * * @see ColorPickerArea * */ public class ColorPicker extends AbstractColorPicker { /** * Instantiates a new color picker. */ public ColorPicker() { super(); } /** * Instantiates a new color picker. * * @param popupCaption * caption of the color select popup */ public ColorPicker(String popupCaption) { super(popupCaption); } /** * Instantiates a new color picker. * * @param popupCaption * caption of the color select popup * @param initialColor * the initial color */ public ColorPicker(String popupCaption, Color initialColor) { super(popupCaption, initialColor); setDefaultCaptionEnabled(true); } @Override protected void setDefaultStyles() { setPrimaryStyleName(STYLENAME_BUTTON); addStyleName(STYLENAME_DEFAULT); } @Override protected ColorPickerState getState() { return (ColorPickerState) super.getState(); } @Override protected ColorPickerState getState(boolean markAsDirty) { return (ColorPickerState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2323 Content-Disposition: inline; filename="ColorPickerArea.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "ee9e28cc787c9784ea97aa6e39c6d3fb04871e2d" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.colorpicker.Color; import com.vaadin.shared.ui.colorpicker.ColorPickerAreaState; /** * A class that defines area-like implementation for a color picker component. * * @since 7.0.0 * * @see ColorPicker * */ public class ColorPickerArea extends AbstractColorPicker { /** * Instantiates a new color picker. */ public ColorPickerArea() { super(); } /** * Instantiates a new color picker. * * @param popupCaption * caption of the color select popup */ public ColorPickerArea(String popupCaption) { super(popupCaption); } /** * Instantiates a new color picker. * * @param popupCaption * caption of the color select popup * @param initialColor * the initial color */ public ColorPickerArea(String popupCaption, Color initialColor) { super(popupCaption, initialColor); setDefaultCaptionEnabled(false); } @Override protected void setDefaultStyles() { // state already has correct default } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); if ("".equals(getState().height)) { getState().height = "30px"; } if ("".equals(getState().width)) { getState().width = "30px"; } } @Override protected ColorPickerAreaState getState() { return (ColorPickerAreaState) super.getState(); } @Override protected ColorPickerAreaState getState(boolean markAsDirty) { return (ColorPickerAreaState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 37600 Content-Disposition: inline; filename="ComboBox.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "04bc27f37ca553d0cadf442f30f88977bea5d781" /* * Copyright 2000-2022 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.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; import com.vaadin.data.provider.CallbackDataProvider; import com.vaadin.data.provider.DataChangeEvent; import com.vaadin.data.provider.DataCommunicator; import com.vaadin.data.provider.DataGenerator; import com.vaadin.data.provider.DataKeyMapper; import com.vaadin.data.provider.DataProvider; import com.vaadin.data.provider.InMemoryDataProvider; import com.vaadin.data.provider.ListDataProvider; import org.jsoup.nodes.Element; import com.vaadin.data.HasFilterableDataProvider; import com.vaadin.data.HasValue; import com.vaadin.data.ValueProvider; import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.server.ConnectorResource; import com.vaadin.server.KeyMapper; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.SerializableBiPredicate; import com.vaadin.server.SerializableConsumer; import com.vaadin.server.SerializableFunction; import com.vaadin.server.SerializableToIntFunction; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.ui.combobox.ComboBoxClientRpc; import com.vaadin.shared.ui.combobox.ComboBoxConstants; import com.vaadin.shared.ui.combobox.ComboBoxServerRpc; import com.vaadin.shared.ui.combobox.ComboBoxState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignFormatter; import elemental.json.JsonObject; /** * A filtering dropdown single-select. Items are filtered based on user input. * Supports the creation of new items when a handler is set by the user. * * @param * item (bean) type in ComboBox * @author Vaadin Ltd */ @SuppressWarnings("serial") public class ComboBox extends AbstractSingleSelect implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, HasFilterableDataProvider { /** * A callback method for fetching items. The callback is provided with a * non-null string filter, offset index and limit. * * @param * item (bean) type in ComboBox * @since 8.0 */ @FunctionalInterface public interface FetchItemsCallback extends Serializable { /** * Returns a stream of items that match the given filter, limiting the * results with given offset and limit. *

* This method is called after the size of the data set is asked from a * related size callback. The offset and limit are promised to be within * the size of the data set. * * @param filter * a non-null filter string * @param offset * the first index to fetch * @param limit * the fetched item count * @return stream of items */ public Stream fetchItems(String filter, int offset, int limit); } /** * Handler that adds a new item based on user input when the new items * allowed mode is active. *

* NOTE 1: If the new item is rejected the client must be notified of the * fact via ComboBoxClientRpc or selection handling won't complete. *

*

* NOTE 2: Selection handling must be completed separately if filtering the * data source with the same value won't include the new item in the initial * list of suggestions. Failing to do so will lead to selection handling * never completing and previous selection remaining on the server. *

* * @since 8.0 * @deprecated Since 8.4 replaced by {@link NewItemProvider}. */ @Deprecated @FunctionalInterface public interface NewItemHandler extends SerializableConsumer { } /** * Provider function that adds a new item based on user input when the new * items allowed mode is active. After the new item handling is complete, * this function should return {@code Optional.of(text)} for the completion * of automatic selection handling. If automatic selection is not wished * for, always return {@code Optional.isEmpty()}. * * @since 8.4 */ @FunctionalInterface public interface NewItemProvider extends SerializableFunction> { } /** * Item style generator class for declarative support. *

* Provides a straightforward mapping between an item and its style. * * @param * item type * @since 8.0 */ protected static class DeclarativeStyleGenerator implements StyleGenerator { private StyleGenerator fallback; private Map styles = new HashMap<>(); public DeclarativeStyleGenerator(StyleGenerator fallback) { this.fallback = fallback; } @Override public String apply(T item) { return styles.containsKey(item) ? styles.get(item) : fallback.apply(item); } /** * Sets a {@code style} for the {@code item}. * * @param item * a data item * @param style * a style for the {@code item} */ protected void setStyle(T item, String style) { styles.put(item, style); } } private ComboBoxServerRpc rpc = new ComboBoxServerRpc() { @Override public void createNewItem(String itemValue) { // New option entered boolean added = false; if (itemValue != null && !itemValue.isEmpty()) { if (getNewItemProvider() != null) { Optional item = getNewItemProvider().apply(itemValue); added = item.isPresent(); // Fixes issue // https://github.com/vaadin/framework/issues/11343 // Update the internal selection state immediately to avoid // client side hanging. This is needed for cases that user // interaction fires multi events (like adding and deleting) // on a new item during the same round trip. item.ifPresent(value -> { setSelectedItem(value, true); getDataCommunicator().reset(); }); } else if (getNewItemHandler() != null) { getNewItemHandler().accept(itemValue); // Up to the user to tell if no item was added. added = true; } } if (!added) { // New item was not handled. getRpcProxy(ComboBoxClientRpc.class).newItemNotAdded(itemValue); } } @Override public void setFilter(String filterText) { getState().currentFilterText = filterText; filterSlot.accept(filterText); } @Override public void resetForceDataSourceUpdate() { getState().forceDataSourceUpdate = false; } }; /** * Handler for new items entered by the user. */ @Deprecated private NewItemHandler newItemHandler; /** * Provider function for new items entered by the user. */ private NewItemProvider newItemProvider; private StyleGenerator itemStyleGenerator = item -> null; private SerializableConsumer filterSlot = filter -> { // Just ignore when neither setDataProvider nor setItems has been called }; private Registration dataProviderListener = null; /** * Constructs an empty combo box without a caption. The content of the combo * box can be set with {@link #setDataProvider(DataProvider)} or * {@link #setItems(Collection)} */ public ComboBox() { this(new DataCommunicator() { @Override protected DataKeyMapper createKeyMapper( ValueProvider identifierGetter) { return new KeyMapper(identifierGetter) { @Override public void remove(T removeobj) { // never remove keys from ComboBox to support selection // of items that are not currently visible } }; } }); } /** * Constructs an empty combo box, whose content can be set with * {@link #setDataProvider(DataProvider)} or {@link #setItems(Collection)}. * * @param caption * the caption to show in the containing layout, null for no * caption */ public ComboBox(String caption) { this(); setCaption(caption); } /** * Constructs a combo box with a static in-memory data provider with the * given options. * * @param caption * the caption to show in the containing layout, null for no * caption * @param options * collection of options, not null */ public ComboBox(String caption, Collection options) { this(caption); setItems(options); } /** * Constructs and initializes an empty combo box. * * @param dataCommunicator * the data comnunicator to use with this ComboBox * @since 8.5 */ protected ComboBox(DataCommunicator dataCommunicator) { super(dataCommunicator); init(); } /** * Initialize the ComboBox with default settings and register client to * server RPC implementation. */ private void init() { registerRpc(rpc); registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); addDataGenerator(new DataGenerator() { /** * Map for storing names for icons. */ private Map resourceKeyMap = new HashMap<>(); private int counter = 0; @Override public void generateData(T item, JsonObject jsonObject) { String caption = getItemCaptionGenerator().apply(item); if (caption == null) { caption = ""; } jsonObject.put(DataCommunicatorConstants.NAME, caption); String style = itemStyleGenerator.apply(item); if (style != null) { jsonObject.put(ComboBoxConstants.STYLE, style); } Resource icon = getItemIcon(item); if (icon != null) { String iconKey = resourceKeyMap .get(getDataProvider().getId(item)); String iconUrl = ResourceReference .create(icon, ComboBox.this, iconKey).getURL(); jsonObject.put(ComboBoxConstants.ICON, iconUrl); } } @Override public void destroyData(T item) { Object itemId = getDataProvider().getId(item); if (resourceKeyMap.containsKey(itemId)) { setResource(resourceKeyMap.get(itemId), null); resourceKeyMap.remove(itemId); } } private Resource getItemIcon(T item) { Resource icon = getItemIconGenerator().apply(item); if (icon == null || !(icon instanceof ConnectorResource)) { return icon; } Object itemId = getDataProvider().getId(item); if (!resourceKeyMap.containsKey(itemId)) { resourceKeyMap.put(itemId, "icon" + (counter++)); } setResource(resourceKeyMap.get(itemId), icon); return icon; } }); } /** * {@inheritDoc} *

* Filtering will use a case insensitive match to show all items where the * filter text is a substring of the caption displayed for that item. */ @Override public void setItems(Collection items) { ListDataProvider listDataProvider = DataProvider.ofCollection(items); setDataProvider(listDataProvider); } /** * {@inheritDoc} *

* Filtering will use a case insensitive match to show all items where the * filter text is a substring of the caption displayed for that item. */ @Override public void setItems(Stream streamOfItems) { // Overridden only to add clarification to javadocs super.setItems(streamOfItems); } /** * {@inheritDoc} *

* Filtering will use a case insensitive match to show all items where the * filter text is a substring of the caption displayed for that item. */ @Override public void setItems(T... items) { // Overridden only to add clarification to javadocs super.setItems(items); } /** * Sets a list data provider as the data provider of this combo box. * Filtering will use a case insensitive match to show all items where the * filter text is a substring of the caption displayed for that item. *

* Note that this is a shorthand that calls * {@link #setDataProvider(DataProvider)} with a wrapper of the provided * list data provider. This means that {@link #getDataProvider()} will * return the wrapper instead of the original list data provider. * * @param listDataProvider * the list data provider to use, not null * @since 8.0 */ public void setDataProvider(ListDataProvider listDataProvider) { // Cannot use the case insensitive contains shorthand from // ListDataProvider since it wouldn't react to locale changes CaptionFilter defaultCaptionFilter = (itemText, filterText) -> itemText .toLowerCase(getLocale()) .contains(filterText.toLowerCase(getLocale())); setDataProvider(defaultCaptionFilter, listDataProvider); } /** * Sets the data items of this listing and a simple string filter with which * the item string and the text the user has input are compared. *

* Note that unlike {@link #setItems(Collection)}, no automatic case * conversion is performed before the comparison. * * @param captionFilter * filter to check if an item is shown when user typed some text * into the ComboBox * @param items * the data items to display * @since 8.0 */ public void setItems(CaptionFilter captionFilter, Collection items) { ListDataProvider listDataProvider = DataProvider.ofCollection(items); setDataProvider(captionFilter, listDataProvider); } /** * Sets a list data provider with an item caption filter as the data * provider of this combo box. The caption filter is used to compare the * displayed caption of each item to the filter text entered by the user. * * @param captionFilter * filter to check if an item is shown when user typed some text * into the ComboBox * @param listDataProvider * the list data provider to use, not null * @since 8.0 */ public void setDataProvider(CaptionFilter captionFilter, ListDataProvider listDataProvider) { Objects.requireNonNull(listDataProvider, "List data provider cannot be null"); // Must do getItemCaptionGenerator() for each operation since it might // not be the same as when this method was invoked setDataProvider(listDataProvider, filterText -> item -> captionFilter .test(getItemCaptionOfItem(item), filterText)); } // Helper method for the above to make lambda more readable private String getItemCaptionOfItem(T item) { String caption = getItemCaptionGenerator().apply(item); if (caption == null) { caption = ""; } return caption; } /** * Sets the data items of this listing and a simple string filter with which * the item string and the text the user has input are compared. *

* Note that unlike {@link #setItems(Collection)}, no automatic case * conversion is performed before the comparison. * * @param captionFilter * filter to check if an item is shown when user typed some text * into the ComboBox * @param items * the data items to display * @since 8.0 */ public void setItems(CaptionFilter captionFilter, @SuppressWarnings("unchecked") T... items) { setItems(captionFilter, Arrays.asList(items)); } /** * Gets the current placeholder text shown when the combo box would be * empty. * * @see #setPlaceholder(String) * @return the current placeholder string, or null if not enabled * @since 8.0 */ public String getPlaceholder() { return getState(false).placeholder; } /** * Sets the placeholder string - a textual prompt that is displayed when the * select would otherwise be empty, to prompt the user for input. * * @param placeholder * the desired placeholder, or null to disable * @since 8.0 */ public void setPlaceholder(String placeholder) { getState().placeholder = placeholder; } /** * Sets whether it is possible to input text into the field or whether the * field area of the component is just used to show what is selected. By * disabling text input, the comboBox will work in the same way as a * {@link NativeSelect} * * @see #isTextInputAllowed() * * @param textInputAllowed * true to allow entering text, false to just show the current * selection */ public void setTextInputAllowed(boolean textInputAllowed) { getState().textInputAllowed = textInputAllowed; } /** * Returns true if the user can enter text into the field to either filter * the selections or enter a new value if new item provider or handler is * set (see {@link #setNewItemProvider(NewItemProvider)} (recommended) and * {@link #setNewItemHandler(NewItemHandler)} (deprecated)). If text input * is disabled, the comboBox will work in the same way as a * {@link NativeSelect} * * @return true if text input is allowed */ public boolean isTextInputAllowed() { return getState(false).textInputAllowed; } @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } /** * Returns the page length of the suggestion popup. * * @return the pageLength */ public int getPageLength() { return getState(false).pageLength; } /** * Returns the suggestion pop-up's width as a CSS string. By default this * width is set to "100%". * * @see #setPopupWidth * @since 7.7 * @return explicitly set popup width as CSS size string or null if not set */ public String getPopupWidth() { return getState(false).suggestionPopupWidth; } /** * Sets the page length for the suggestion popup. Setting the page length to * 0 will disable suggestion popup paging (all items visible). * * @param pageLength * the pageLength to set */ public void setPageLength(int pageLength) { getState().pageLength = pageLength; } /** * Returns whether the user is allowed to select nothing in the combo box. * * @return true if empty selection is allowed, false otherwise * @since 8.0 */ public boolean isEmptySelectionAllowed() { return getState(false).emptySelectionAllowed; } /** * Sets whether the user is allowed to select nothing in the combo box. When * true, a special empty item is shown to the user. * * @param emptySelectionAllowed * true to allow not selecting anything, false to require * selection * @since 8.0 */ public void setEmptySelectionAllowed(boolean emptySelectionAllowed) { getState().emptySelectionAllowed = emptySelectionAllowed; } /** * Returns the empty selection caption. *

* The empty string {@code ""} is the default empty selection caption. * * @return the empty selection caption, not {@code null} * @see #setEmptySelectionAllowed(boolean) * @see #isEmptySelectionAllowed() * @see #setEmptySelectionCaption(String) * @see #isSelected(Object) * @since 8.0 */ public String getEmptySelectionCaption() { return getState(false).emptySelectionCaption; } /** * Sets the empty selection caption. *

* The empty string {@code ""} is the default empty selection caption. *

* If empty selection is allowed via the * {@link #setEmptySelectionAllowed(boolean)} method (it is by default) then * the empty item will be shown with the given caption. * * @param caption * the caption to set, not {@code null} * @see #isSelected(Object) * @since 8.0 */ public void setEmptySelectionCaption(String caption) { Objects.nonNull(caption); getState().emptySelectionCaption = caption; } /** * Sets the suggestion pop-up's width as a CSS string. By using relative * units (e.g. "50%") it's possible to set the popup's width relative to the * ComboBox itself. *

* By default this width is set to "100%" so that the pop-up's width is * equal to the width of the combobox. By setting width to null the pop-up's * width will automatically expand beyond 100% relative width to fit the * content of all displayed items. * * @see #getPopupWidth() * @since 7.7 * @param width * the width */ public void setPopupWidth(String width) { getState().suggestionPopupWidth = width; } /** * Sets whether to scroll the selected item visible (directly open the page * on which it is) when opening the combo box popup or not. *

* This requires finding the index of the item, which can be expensive in * many large lazy loading containers. * * @param scrollToSelectedItem * true to find the page with the selected item when opening the * selection popup */ public void setScrollToSelectedItem(boolean scrollToSelectedItem) { getState().scrollToSelectedItem = scrollToSelectedItem; } /** * Returns true if the select should find the page with the selected item * when opening the popup. * * @see #setScrollToSelectedItem(boolean) * * @return true if the page with the selected item will be shown when * opening the popup */ public boolean isScrollToSelectedItem() { return getState(false).scrollToSelectedItem; } @Override public ItemCaptionGenerator getItemCaptionGenerator() { return super.getItemCaptionGenerator(); } @Override public void setItemCaptionGenerator( ItemCaptionGenerator itemCaptionGenerator) { super.setItemCaptionGenerator(itemCaptionGenerator); } /** * Sets the style generator that is used to produce custom class names for * items visible in the popup. The CSS class name that will be added to the * item is v-filterselect-item-[style name]. Returning null from * the generator results in no custom style name being set. * * @see StyleGenerator * * @param itemStyleGenerator * the item style generator to set, not null * @throws NullPointerException * if {@code itemStyleGenerator} is {@code null} * @since 8.0 */ public void setStyleGenerator(StyleGenerator itemStyleGenerator) { Objects.requireNonNull(itemStyleGenerator, "Item style generator must not be null"); this.itemStyleGenerator = itemStyleGenerator; getDataCommunicator().reset(); } /** * Gets the currently used style generator that is used to generate CSS * class names for items. The default item style provider returns null for * all items, resulting in no custom item class names being set. * * @see StyleGenerator * @see #setStyleGenerator(StyleGenerator) * * @return the currently used item style generator, not null * @since 8.0 */ public StyleGenerator getStyleGenerator() { return itemStyleGenerator; } @Override public void setItemIconGenerator(IconGenerator itemIconGenerator) { super.setItemIconGenerator(itemIconGenerator); } @Override public IconGenerator getItemIconGenerator() { return super.getItemIconGenerator(); } /** * Sets the handler that is called when user types a new item. The creation * of new items is allowed when a new item handler has been set. If new item * provider is also set, the new item handler is ignored. * * @param newItemHandler * handler called for new items, null to only permit the * selection of existing items, all options ignored if new item * provider is set * @since 8.0 * @deprecated Since 8.4 use {@link #setNewItemProvider(NewItemProvider)} * instead. */ @Deprecated public void setNewItemHandler(NewItemHandler newItemHandler) { getLogger().log(Level.WARNING, "NewItemHandler is deprecated. Please use NewItemProvider instead."); this.newItemHandler = newItemHandler; getState(true).allowNewItems = newItemProvider != null || newItemHandler != null; } /** * Sets the provider function that is called when user types a new item. The * creation of new items is allowed when a new item provider has been set. * If a deprecated new item handler is also set it is ignored in favor of * new item provider. * * @param newItemProvider * provider function that is called for new items, null to only * permit the selection of existing items or to use a deprecated * new item handler if set * @since 8.4 */ public void setNewItemProvider(NewItemProvider newItemProvider) { this.newItemProvider = newItemProvider; getState(true).allowNewItems = newItemProvider != null || newItemHandler != null; } /** * Returns the handler called when the user enters a new item (not present * in the data provider). * * @return new item handler or null if none specified * @deprecated Since 8.4 use {@link #getNewItemProvider()} instead. */ @Deprecated public NewItemHandler getNewItemHandler() { return newItemHandler; } /** * Returns the provider function that is called when the user enters a new * item (not present in the data provider). * * @since 8.4 * @return new item provider or null if none specified */ public NewItemProvider getNewItemProvider() { return newItemProvider; } // HasValue methods delegated to the selection model @Override public Registration addValueChangeListener( HasValue.ValueChangeListener listener) { return addSelectionListener(event -> listener .valueChange(new ValueChangeEvent<>(event.getComponent(), this, event.getOldValue(), event.isUserOriginated()))); } @Override protected ComboBoxState getState() { return (ComboBoxState) super.getState(); } @Override protected ComboBoxState getState(boolean markAsDirty) { return (ComboBoxState) super.getState(markAsDirty); } @Override protected void updateSelectedItemState(T value) { super.updateSelectedItemState(value); updateSelectedItemCaption(value); updateSelectedItemIcon(value); } private void updateSelectedItemCaption(T value) { String selectedCaption = null; if (value != null) { selectedCaption = getItemCaptionGenerator().apply(value); } getState().selectedItemCaption = selectedCaption; } private void updateSelectedItemIcon(T value) { String selectedItemIcon = null; if (value != null) { Resource icon = getItemIconGenerator().apply(value); if (icon != null) { if (icon instanceof ConnectorResource) { if (!isAttached()) { // Deferred resource generation. return; } setResource("selected", icon); } selectedItemIcon = ResourceReference .create(icon, ComboBox.this, "selected").getURL(); } } getState().selectedItemIcon = selectedItemIcon; } @Override public void attach() { super.attach(); // Update icon for ConnectorResource updateSelectedItemIcon(getValue()); DataProvider dataProvider = getDataProvider(); if (dataProvider != null && dataProviderListener == null) { setupDataProviderListener(dataProvider); } } @Override public void detach() { if (dataProviderListener != null) { dataProviderListener.remove(); dataProviderListener = null; } super.detach(); } @Override protected Element writeItem(Element design, T item, DesignContext context) { Element element = design.appendElement("option"); String caption = getItemCaptionGenerator().apply(item); if (caption != null) { element.html(DesignFormatter.encodeForTextNode(caption)); } else { element.html(DesignFormatter.encodeForTextNode(item.toString())); } element.attr("item", item.toString()); Resource icon = getItemIconGenerator().apply(item); if (icon != null) { DesignAttributeHandler.writeAttribute("icon", element.attributes(), icon, null, Resource.class, context); } String style = getStyleGenerator().apply(item); if (style != null) { element.attr("style", style); } if (isSelected(item)) { element.attr("selected", true); } return element; } @Override protected void readItems(Element design, DesignContext context) { setStyleGenerator(new DeclarativeStyleGenerator<>(getStyleGenerator())); super.readItems(design, context); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected T readItem(Element child, Set selected, DesignContext context) { T item = super.readItem(child, selected, context); if (child.hasAttr("style")) { StyleGenerator styleGenerator = getStyleGenerator(); if (styleGenerator instanceof DeclarativeStyleGenerator) { ((DeclarativeStyleGenerator) styleGenerator).setStyle(item, child.attr("style")); } else { throw new IllegalStateException(String.format("Don't know how " + "to set style using current style generator '%s'", styleGenerator.getClass().getName())); } } return item; } @Override public DataProvider getDataProvider() { if (this.getDataCommunicator() != null) { return internalGetDataProvider(); } return null; } @Override public void setDataProvider(DataProvider dataProvider, SerializableFunction filterConverter) { Objects.requireNonNull(dataProvider, "dataProvider cannot be null"); Objects.requireNonNull(filterConverter, "filterConverter cannot be null"); SerializableFunction convertOrNull = filterText -> { if (filterText == null || filterText.isEmpty()) { return null; } return filterConverter.apply(filterText); }; SerializableConsumer providerFilterSlot = internalSetDataProvider( dataProvider, convertOrNull.apply(getState(false).currentFilterText)); filterSlot = filter -> providerFilterSlot .accept(convertOrNull.apply(filter)); setupDataProviderListener(dataProvider); } private void setupDataProviderListener( DataProvider dataProvider) { // This workaround is done to fix issue #11642 for unpaged comboboxes. // Data sources for on the client need to be updated after data provider // refreshAll so that serverside selection works even before the // dropdown // is opened. Only done for in-memory data providers for performance // reasons. if (dataProvider instanceof InMemoryDataProvider) { if (dataProviderListener != null) { dataProviderListener.remove(); } dataProviderListener = dataProvider .addDataProviderListener(event -> { if ((!(event instanceof DataChangeEvent.DataRefreshEvent)) && (getPageLength() == 0)) { getState().forceDataSourceUpdate = true; } }); } } /** * Sets a CallbackDataProvider using the given fetch items callback and a * size callback. *

* This method is a shorthand for making a {@link CallbackDataProvider} that * handles a partial {@link com.vaadin.data.provider.Query Query} object. * * @param fetchItems * a callback for fetching items * @param sizeCallback * a callback for getting the count of items * * @see CallbackDataProvider * @see #setDataProvider(DataProvider) */ public void setDataProvider(FetchItemsCallback fetchItems, SerializableToIntFunction sizeCallback) { setDataProvider(new CallbackDataProvider<>( q -> fetchItems.fetchItems(q.getFilter().orElse(""), q.getOffset(), q.getLimit()), q -> sizeCallback.applyAsInt(q.getFilter().orElse("")))); } /** * Predicate to check {@link ComboBox} item captions against user typed * strings. * * @see ComboBox#setItems(CaptionFilter, Collection) * @see ComboBox#setItems(CaptionFilter, Object[]) * @since 8.0 */ @FunctionalInterface public interface CaptionFilter extends SerializableBiPredicate { /** * Check item caption against entered text. * * @param itemCaption * the caption of the item to filter, not {@code null} * @param filterText * user entered filter, not {@code null} * @return {@code true} if item passes the filter and should be listed, * {@code false} otherwise */ @Override public boolean test(String itemCaption, String filterText); } private static Logger getLogger() { return Logger.getLogger(ComboBox.class.getName()); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 40935 Content-Disposition: inline; filename="Component.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "8e766e36744273807d31b98b9deede916178d64e" /* * Copyright 2000-2022 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.util.Locale; import org.jsoup.nodes.Element; import com.vaadin.event.ConnectorEvent; import com.vaadin.event.ConnectorEventListener; import com.vaadin.event.FieldEvents; import com.vaadin.server.ClientConnector; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Resource; import com.vaadin.server.Sizeable; import com.vaadin.server.VariableOwner; import com.vaadin.shared.Registration; import com.vaadin.ui.declarative.DesignContext; /** * {@code Component} is the top-level interface that is and must be implemented * by all Vaadin components. {@code Component} is paired with * {@link AbstractComponent}, which provides a default implementation for all * the methods defined in this interface. * *

* Components are laid out in the user interface hierarchically. The layout is * managed by layout components, or more generally by components that implement * the {@link ComponentContainer} interface. Such a container is the * parent of the contained components. *

* *

* The {@link #getParent()} method allows retrieving the parent component of a * component. While there is a {@link #setParent(HasComponents)}, you rarely * need it as you normally add components with the * {@link ComponentContainer#addComponent(Component) addComponent()} method of * the layout or other {@code ComponentContainer}, which automatically sets the * parent. *

* *

* A component becomes attached to an application (and the * {@link #attach()} is called) when it or one of its parents is attached to the * main window of the application through its containment hierarchy. *

* * @author Vaadin Ltd. * @since 3.0 */ public interface Component extends ClientConnector, Sizeable { /** * Gets all user-defined CSS style names of a component. If the component * has multiple style names defined, the return string is a space-separated * list of style names. Built-in style names defined in Vaadin or GWT are * not returned. * *

* The style names are returned only in the basic form in which they were * added; each user-defined style name shows as two CSS style class names in * the rendered HTML: one as it was given and one prefixed with the * component-specific style name. Only the former is returned. *

* * @return the style name or a space-separated list of user-defined style * names of the component * @see #setStyleName(String) * @see #addStyleName(String) * @see #removeStyleName(String) */ public String getStyleName(); /** * Sets one or more user-defined style names of the component, replacing any * previous user-defined styles. Multiple styles can be specified as a * space-separated list of style names. The style names must be valid CSS * class names and should not conflict with any built-in style names in * Vaadin or GWT. * *
     * Label label = new Label("This text has a lot of style");
     * label.setStyleName("myonestyle myotherstyle");
     * 
* *

* Each style name will occur in two versions: one as specified and one that * is prefixed with the style name of the component. For example, if you * have a {@code Button} component and give it "{@code mystyle}" style, the * component will have both "{@code mystyle}" and "{@code v-button-mystyle}" * styles. You could then style the component either with: *

* *
     * .myonestyle {background: blue;}
     * 
* *

* or *

* *
     * .v-button-myonestyle {background: blue;}
     * 
* *

* It is normally a good practice to use {@link #addStyleName(String) * addStyleName()} rather than this setter, as different software * abstraction layers can then add their own styles without accidentally * removing those defined in other layers. *

* * @param style * the new style or styles of the component as a space-separated * list * @see #getStyleName() * @see #addStyleName(String) * @see #removeStyleName(String) */ public void setStyleName(String style); /** * Adds or removes a style name. Multiple styles can be specified as a * space-separated list of style names. * * If the {@code add} parameter is true, the style name is added to the * component. If the {@code add} parameter is false, the style name is * removed from the component. *

* Functionally this is equivalent to using {@link #addStyleName(String)} or * {@link #removeStyleName(String)} * * @since 8.5 * @param style * the style name to be added or removed * @param add * true to add the given style, false * to remove it * @see #addStyleName(String) * @see #removeStyleName(String) */ public default void setStyleName(String style, boolean add) { if (add) { addStyleName(style); } else { removeStyleName(style); } } /** * Adds one or more style names to this component. Multiple styles can be * specified as a space-separated list of style names. The style name will * be rendered as a HTML class name, which can be used in a CSS definition. * *

     * Label label = new Label("This text has style");
     * label.addStyleName("mystyle");
     * 
* *

* Each style name will occur in two versions: one as specified and one that * is prefixed with the style name of the component. For example, if you * have a {@code Button} component and give it "{@code mystyle}" style, the * component will have both "{@code mystyle}" and "{@code v-button-mystyle}" * styles. You could then style the component either with: *

* *
     * .mystyle {font-style: italic;}
     * 
* *

* or *

* *
     * .v-button-mystyle {font-style: italic;}
     * 
* * @param style * the new style to be added to the component * @see #getStyleName() * @see #setStyleName(String) * @see #removeStyleName(String) */ public void addStyleName(String style); /** * Adds one or more style names to this component by using one or multiple * parameters. * * @param styles * the style name or style names to be added to the component * @see #addStyleName(String) * @see #setStyleName(String) * @see #removeStyleName(String) * @since 8.1 */ public default void addStyleNames(String... styles) { for (String style : styles) { addStyleName(style); } } /** * Removes one or more style names from component. Multiple styles can be * specified as a space-separated list of style names. * *

* The parameter must be a valid CSS style name. Only user-defined style * names added with {@link #addStyleName(String) addStyleName()} or * {@link #setStyleName(String) setStyleName()} can be removed; built-in * style names defined in Vaadin or GWT can not be removed. *

* * @param style * the style name or style names to be removed * @see #getStyleName() * @see #setStyleName(String) * @see #addStyleName(String) */ public void removeStyleName(String style); /** * Removes one or more style names from component. Multiple styles can be * specified by using multiple parameters. * * @param styles * the style name or style names to be removed * @see #removeStyleName(String) * @see #setStyleName(String) * @see #addStyleName(String) * @since 8.1 */ public default void removeStyleNames(String... styles) { for (String style : styles) { removeStyleName(style); } } /** * Gets the primary style name of the component. See * {@link Component#setPrimaryStyleName(String)} for a better description of * the primary stylename. */ public String getPrimaryStyleName(); /** * Changes the primary style name of the component. * *

* The primary style name identifies the component when applying the CSS * theme to the Component. By changing the style name all CSS rules targeted * for that style name will no longer apply, and might result in the * component not working as intended. *

* *

* To preserve the original style of the component when changing to a new * primary style you should make your new primary style inherit the old * primary style using the SASS @include directive. See more in the SASS * tutorials. *

* * @param style * The new primary style name */ public void setPrimaryStyleName(String style); /** * Tests whether the component is enabled or not. A user can not interact * with disabled components. Disabled components are rendered in a style * that indicates the status, usually in gray color. Children of a disabled * component are also disabled. Components are enabled by default. * *

* As a security feature, all updates for disabled components are blocked on * the server-side. *

* *

* 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. *

* * @return true if the component and its parent are enabled, * false otherwise. * @see VariableOwner#isEnabled() */ public boolean isEnabled(); /** * Enables or disables the component. The user can not interact with * disabled components, which are shown with a style that indicates the * status, usually shaded in light gray color. Components are enabled by * default. * *
     * Button enabled = new Button("Enabled");
     * enabled.setEnabled(true); // The default
     * layout.addComponent(enabled);
     *
     * Button disabled = new Button("Disabled");
     * disabled.setEnabled(false);
     * layout.addComponent(disabled);
     * 
* * @param enabled * a boolean value specifying if the component should be enabled * or not */ public void setEnabled(boolean enabled); /** * Tests the visibility property of the component. * *

* Visible components are drawn in the user interface, while invisible ones * 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. *

* *

* 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. *

* * @return true if the component has been set to be visible in * the user interface, false if not * @see #setVisible(boolean) * @see #attach() */ public boolean isVisible(); /** * Sets the visibility of the component. * *

* Visible components are drawn in the user interface, while invisible ones * 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. *

* *
     * TextField readonly = new TextField("Read-Only");
     * readonly.setValue("You can't see this!");
     * readonly.setVisible(false);
     * layout.addComponent(readonly);
     * 
* *

* A component is visible only if all of its parents are also visible. If a * component is explicitly set to be invisible, changes in the visibility of * its parents will not change the visibility of the component. *

* * @param visible * the boolean value specifying if the component should be * visible after the call or not. * @see #isVisible() */ public void setVisible(boolean visible); /** * Sets the parent connector of the component. * *

* This method automatically calls {@link #attach()} if the component * becomes attached to the session, regardless of whether it was attached * previously. Conversely, if the component currently is attached to the * session, {@link #detach()} is called for the connector before attaching * it to a new parent. *

*

* This method is rarely called directly. * {@link ComponentContainer#addComponent(Component)} or a * {@link HasComponents} specific method is normally used for adding * components to a parent and the used method will call this method * implicitly. *

* * @param parent * the parent connector * @throws IllegalStateException * if a parent is given even though the connector already has a * parent */ public void setParent(HasComponents parent); /** * Gets the parent component of the component. * *

* Components can be nested but a component can have only one parent. A * component that contains other components, that is, can be a parent, * should usually inherit the {@link ComponentContainer} interface. *

* * @return the parent component */ @Override public HasComponents getParent(); /** * Gets the caption of the component. * *

* See {@link #setCaption(String)} for a detailed description of the * caption. *

* * @return the caption of the component or {@code null} if the caption is * not set. * @see #setCaption(String) */ public String getCaption(); /** * Sets the caption of the component. * *

* A caption is an explanatory textual label accompanying a user * interface component, usually shown above, left of, or inside the * component. Icon (see {@link #setIcon(Resource) setIcon()} is * closely related to caption and is usually displayed horizontally before * or after it, depending on the component and the containing layout. *

* *

* The caption can usually also be given as the first parameter to a * constructor, though some components do not support it. *

* *
     * RichTextArea area = new RichTextArea();
     * area.setCaption("You can edit stuff here");
     * area.setValue("<h1>Helpful Heading</h1>"
     *         + "<p>All this is for you to edit.</p>");
     * 
* *

* The contents of a caption are automatically quoted, so no raw HTML can be * rendered in a caption. The validity of the used character encoding, * usually UTF-8, is not checked. *

* *

* The caption of a component is, by default, managed and displayed by the * layout component or component container in which the component is placed. * For example, the {@link VerticalLayout} component shows the captions * left-aligned above the contained components, while the {@link FormLayout} * component shows the captions on the left side of the vertically laid * components, with the captions and their associated components * left-aligned in their own columns. The {@link CustomComponent} does not * manage the caption of its composition root, so if the root component has * a caption, it will not be rendered. Some components, such as * {@link Button} and {@link Panel}, manage the caption themselves and * display it inside the component. *

* * @param caption * the new caption for the component. If the caption is * {@code null}, no caption is shown and it does not normally * take any space */ public void setCaption(String caption); /** * Gets the icon resource of the component. * *

* See {@link #setIcon(Resource)} for a detailed description of the icon. *

* * @return the icon resource of the component or {@code null} if the * component has no icon * @see #setIcon(Resource) */ public Resource getIcon(); /** * Sets the icon of the component. * *

* An icon is an explanatory graphical label accompanying a user interface * component, usually shown above, left of, or inside the component. Icon is * closely related to caption (see {@link #setCaption(String) setCaption()}) * and is usually displayed horizontally before or after it, depending on * the component and the containing layout. *

* *

* The image is loaded by the browser from a resource, typically a * {@link com.vaadin.server.ThemeResource}. *

* *
     * // Component with an icon from a custom theme
     * TextField name = new TextField("Name");
     * name.setIcon(new ThemeResource("icons/user.png"));
     * layout.addComponent(name);
     *
     * // Component with an icon from another theme ('runo')
     * Button ok = new Button("OK");
     * ok.setIcon(new ThemeResource("../runo/icons/16/ok.png"));
     * layout.addComponent(ok);
     * 
* *

* The icon of a component is, by default, managed and displayed by the * layout component or component container in which the component is placed. * For example, the {@link VerticalLayout} component shows the icons * left-aligned above the contained components, while the {@link FormLayout} * component shows the icons on the left side of the vertically laid * components, with the icons and their associated components left-aligned * in their own columns. The {@link CustomComponent} does not manage the * icon of its composition root, so if the root component has an icon, it * will not be rendered. *

* *

* An icon will be rendered inside an HTML element that has the * {@code v-icon} CSS style class. The containing layout may enclose an icon * and a caption inside elements related to the caption, such as * {@code v-caption} . *

* * @param icon * the icon of the component. If null, no icon is shown and it * does not normally take any space. * @see #getIcon() * @see #setCaption(String) */ public void setIcon(Resource icon); /** * Gets the UI the component is attached to. * *

* If the component is not attached to a UI through a component containment * hierarchy, null is returned. *

* * @return the UI of the component or null if it is not * attached to a UI */ @Override public UI getUI(); /** * {@inheritDoc} * *

* Reimplementing the {@code attach()} method is useful for tasks that need * to get a reference to the parent, window, or application object with the * {@link #getParent()} and {@link #getUI()} methods. A component does not * yet know these objects in the constructor, so in such case, the methods * will return {@code null}. For example, the following is invalid: *

* *
     * public class AttachExample extends CustomComponent {
     *     public AttachExample() {
     *         // ERROR: We can't access the application object yet.
     *         ClassResource r = new ClassResource("smiley.jpg",
     *                 getApplication());
     *         Embedded image = new Embedded("Image:", r);
     *         setCompositionRoot(image);
     *     }
     * }
     * 
* *

* Adding a component to an application triggers calling the * {@link #attach()} method for the component. Correspondingly, removing a * component from a container triggers calling the {@link #detach()} method. * If the parent of an added component is already connected to the * application, the {@code attach()} is called immediately from * {@link #setParent(HasComponents)}. *

* *
     * public class AttachExample extends CustomComponent {
     *     public AttachExample() {
     *     }
     *
     *     @Override
     *     public void attach() {
     *         super.attach(); // Must call.
     *
     *         // Now we know who ultimately owns us.
     *         ClassResource r = new ClassResource("smiley.jpg",
     *                 getApplication());
     *         Embedded image = new Embedded("Image:", r);
     *         setCompositionRoot(image);
     *     }
     * }
     * 
*/ @Override public void attach(); /** * Gets the locale of the component. * *

* If a component does not have a locale set, the locale of its parent is * returned, and so on. Eventually, if no parent has locale set, the locale * of the application is returned. If the application does not have a locale * set, it is determined by Locale.getDefault(). *

* *

* As the component must be attached before its locale can be acquired, * using this method in the internationalization of component captions, etc. * is generally not feasible. For such use case, we recommend using an * otherwise acquired reference to the application locale. *

* * @return Locale of this component or {@code null} if the component and * none of its parents has a locale set and the component is not yet * attached to an application. */ public Locale getLocale(); /** * Adds an unique id for component that is used in the client-side for * testing purposes. Keeping identifiers unique is the responsibility of the * programmer. * * @param id * An alphanumeric id */ public void setId(String id); /** * Gets currently set debug identifier. * * @return current id, null if not set */ public String getId(); /** *

* Gets the components description, used in tooltips and can be displayed * directly in certain other components such as forms. The description can * be used to briefly describe the state of the component to the user. The * description string may contain certain XML tags: *

* *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
TagDescriptionExample
<b>boldbold text
<i>italicitalic text
<u>underlinedunderlined text
<br>linebreakN/A
<ul>
* <li>item1
* <li>item1
* </ul>
item list *
    *
  • item1 *
  • item2 *
*
*

* *

* These tags may be nested. *

* * @return component's description String */ public String getDescription(); /* Declarative support */ /** * Reads the component state from the given design. *

* The component is responsible not only for updating its own state but also * for ensuring that its children update their state based on the design. *

* It is assumed that the component is in its default state when this method * is called. Reading should only take into consideration attributes * specified in the design and not reset any unspecified attributes to their * defaults. *

* This method must not modify the design. * * @since 7.4 * @param design * The element to obtain the state from * @param designContext * The DesignContext instance used for parsing the design */ public void readDesign(Element design, DesignContext designContext); /** * Writes the component state to the given design. *

* The component is responsible not only for writing its own state but also * for ensuring that its children write their state to the design. *

* This method must not modify the component state. * * @since 7.4 * @param design * The element to write the component state to. Any previous * attributes or child nodes are not cleared. * @param designContext * The DesignContext instance used for writing the design * */ public void writeDesign(Element design, DesignContext designContext); /* Component event framework */ /** * Superclass of all component originated events. * *

* Events are the basis of all user interaction handling in Vaadin. To * handle events, you provide a listener object that receives the events of * the particular event type. *

* *
     * Button button = new Button("Click Me!");
     * button.addListener(new Button.ClickListener() {
     *     public void buttonClick(ClickEvent event) {
     *         getWindow().showNotification("Thank You!");
     *     }
     * });
     * layout.addComponent(button);
     * 
* *

* Notice that while each of the event types have their corresponding * listener types; the listener interfaces are not required to inherit the * {@code Component.Listener} interface. *

* * @see Component.Listener */ @SuppressWarnings("serial") public static class Event extends ConnectorEvent { /** * Constructs a new event with the specified source component. * * @param source * the source component of the event */ public Event(Component source) { super(source); } /** * Gets the component where the event occurred. * * @return the source component of the event */ public Component getComponent() { return (Component) getSource(); } } /** * Listener interface for receiving Component.Events. * *

* Listener interfaces are the basis of all user interaction handling in * Vaadin. You have or create a listener object that receives the events. * All event types have their corresponding listener types; they are not, * however, required to inherit the {@code Component.Listener} interface, * and they rarely do so. *

* *

* This generic listener interface is useful typically when you wish to * handle events from different component types in a single listener method * ({@code componentEvent()}. If you handle component events in an anonymous * listener class, you normally use the component specific listener class, * such as {@link com.vaadin.ui.Button.ClickEvent}. *

* *
     * class Listening extends CustomComponent implements Listener {
     *     Button ok; // Stored for determining the source of an event
     *
     *     Label status; // For displaying info about the event
     *
     *     public Listening() {
     *         VerticalLayout layout = new VerticalLayout();
     *
     *         // Some miscellaneous component
     *         TextField name = new TextField("Say it all here");
     *         name.addListener(this);
     *         layout.addComponent(name);
     *
     *         // Handle button clicks as generic events instead
     *         // of Button.ClickEvent events
     *         ok = new Button("OK");
     *         ok.addListener(this);
     *         layout.addComponent(ok);
     *
     *         // For displaying information about an event
     *         status = new Label("");
     *         layout.addComponent(status);
     *
     *         setCompositionRoot(layout);
     *     }
     *
     *     public void componentEvent(Event event) {
     *         // Act according to the source of the event
     *         if (event.getSource() == ok
     *                 && event.getClass() == Button.ClickEvent.class)
     *             getWindow().showNotification("Click!");
     *
     *         // Display source component and event class names
     *         status.setValue(
     *                 "Event from " + event.getSource().getClass().getName()
     *                         + ": " + event.getClass().getName());
     *     }
     * }
     *
     * Listening listening = new Listening();
     * layout.addComponent(listening);
     * 
* * @see Component#addListener(Listener) */ public interface Listener extends ConnectorEventListener { /** * Notifies the listener of a component event. * *

* As the event can typically come from one of many source components, * you may need to differentiate between the event source by component * reference, class, etc. *

* *
         * public void componentEvent(Event event) {
         *     // Act according to the source of the event
         *     if (event.getSource() == ok
         *             && event.getClass() == Button.ClickEvent.class)
         *         getWindow().showNotification("Click!");
         *
         *     // Display source component and event class names
         *     status.setValue(
         *             "Event from " + event.getSource().getClass().getName() + ": "
         *                     + event.getClass().getName());
         * }
         * 
* * @param event * the event that has occurred. */ public void componentEvent(Component.Event event); } /** * Registers a new (generic) component event listener for the component. * *
     * class Listening extends CustomComponent implements Listener {
     *     // Stored for determining the source of an event
     *     Button ok;
     *
     *     Label status; // For displaying info about the event
     *
     *     public Listening() {
     *         VerticalLayout layout = new VerticalLayout();
     *
     *         // Some miscellaneous component
     *         TextField name = new TextField("Say it all here");
     *         name.addListener(this);
     *         layout.addComponent(name);
     *
     *         // Handle button clicks as generic events instead
     *         // of Button.ClickEvent events
     *         ok = new Button("OK");
     *         ok.addListener(this);
     *         layout.addComponent(ok);
     *
     *         // For displaying information about an event
     *         status = new Label("");
     *         layout.addComponent(status);
     *
     *         setCompositionRoot(layout);
     *     }
     *
     *     public void componentEvent(Event event) {
     *         // Act according to the source of the event
     *         if (event.getSource() == ok)
     *             getWindow().showNotification("Click!");
     *
     *         status.setValue(
     *                 "Event from " + event.getSource().getClass().getName()
     *                         + ": " + event.getClass().getName());
     *     }
     * }
     *
     * Listening listening = new Listening();
     * layout.addComponent(listening);
     * 
* * @param listener * the new Listener to be registered. * @return a registration object for removing this listener * @see Component.Event * @see Registration * @since 8.0 */ public Registration addListener(Component.Listener listener); /** * Removes a previously registered component event listener from this * component. * * @param listener * the listener to be removed. * @see #addListener(Listener) * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #addListener(Component.Listener)}. */ @Deprecated public void removeListener(Component.Listener listener); /** * Class of all component originated error events. * *

* The component error event is normally fired by * {@link AbstractComponent#setComponentError(ErrorMessage)}. The component * errors are set by the framework in some situations and can be set by user * code. They are indicated in a component with an error indicator. *

*/ @SuppressWarnings("serial") public static class ErrorEvent extends Event { private final ErrorMessage message; /** * Constructs a new event with a specified source component. * * @param message * the error message. * @param component * the source component. */ public ErrorEvent(ErrorMessage message, Component component) { super(component); this.message = message; } /** * Gets the error message. * * @return the error message. */ public ErrorMessage getErrorMessage() { return message; } } /** * A sub-interface implemented by components that can obtain input focus. * This includes all {@code LegacyField} components as well as some other * components, such as {@code Upload}. * *

* Focus can be set with {@link #focus()}. This interface does not provide * an accessor that would allow finding out the currently focused component; * focus information can be acquired for some (but not all) * {@code LegacyField} components through the * {@link com.vaadin.event.FieldEvents.FocusListener} and * {@link com.vaadin.event.FieldEvents.BlurListener} interfaces. *

* * @see FieldEvents */ public interface Focusable extends Component { /** * Sets the focus to this component. * *
         * FormLayout loginBox = new FormLayout();
         * loginBox.setCaption("Login");
         * layout.addComponent(loginBox);
         *
         * // Create the first field which will be focused
         * TextField username = new TextField("User name");
         * loginBox.addComponent(username);
         *
         * // Set focus to the user name
         * username.focus();
         *
         * TextField password = new TextField("Password");
         * loginBox.addComponent(password);
         *
         * Button login = new Button("Login");
         * loginBox.addComponent(login);
         * 
* *

* Notice that this interface does not provide an accessor that would * allow finding out the currently focused component. Focus information * can be acquired for some (but not all) components through the * {@link com.vaadin.event.FieldEvents.FocusListener} and * {@link com.vaadin.event.FieldEvents.BlurListener} interfaces. *

* * @see com.vaadin.event.FieldEvents * @see com.vaadin.event.FieldEvents.FocusEvent * @see com.vaadin.event.FieldEvents.FocusListener * @see com.vaadin.event.FieldEvents.BlurEvent * @see com.vaadin.event.FieldEvents.BlurListener */ public void focus(); /** * Gets the tabulator index of the {@code Focusable} component. * * @return tab index set for the {@code Focusable} component * @see #setTabIndex(int) */ public int getTabIndex(); /** * Sets the tabulator index of the {@code Focusable} component. * The tab index property is used to specify the order in which the * fields are focused when the user presses the Tab key. Components with * a defined tab index are focused sequentially first, and then the * components with no tab index. * *
         * Form loginBox = new Form();
         * loginBox.setCaption("Login");
         * layout.addComponent(loginBox);
         *
         * // Create the first field which will be focused
         * TextField username = new TextField("User name");
         * loginBox.addField("username", username);
         *
         * // Set focus to the user name
         * username.focus();
         *
         * TextField password = new TextField("Password");
         * loginBox.addField("password", password);
         *
         * Button login = new Button("Login");
         * loginBox.getFooter().addComponent(login);
         *
         * // An additional component which natural focus order would
         * // be after the button.
         * CheckBox remember = new CheckBox("Remember me");
         * loginBox.getFooter().addComponent(remember);
         *
         * username.setTabIndex(1);
         * password.setTabIndex(2);
         * remember.setTabIndex(3); // Different than natural place
         * login.setTabIndex(4);
         * 
* *

* After all focusable user interface components are done, the browser * can begin again from the component with the smallest tab index, or it * can take the focus out of the page, for example, to the location bar. *

* *

* If the tab index is not set (is set to zero), the default tab order * is used. The order is somewhat browser-dependent, but generally * follows the HTML structure of the page. *

* *

* A negative value means that the component is completely removed from * the tabulation order and can not be reached by pressing the Tab key * at all. *

* * @param tabIndex * the tab order of this component. Indexes usually start * from 1. Zero means that default tab order should be used. * A negative value means that the field should not be * included in the tabbing sequence. * @see #getTabIndex() */ public void setTabIndex(int tabIndex); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 3601 Content-Disposition: inline; filename="ComponentContainer.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3a9334c1576474212dafd98fd30bee9ba9386bdf" /* * Copyright 2000-2022 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.util.Iterator; import com.vaadin.ui.HasComponents.ComponentAttachDetachNotifier; /** * A special type of parent which allows the user to add and remove components * to it. Typically does not have any restrictions on the number of children it * can contain. * * @author Vaadin Ltd. * @since 3.0 */ public interface ComponentContainer extends HasComponents, ComponentAttachDetachNotifier { /** * Adds the component into this container. * * @param c * the component to be added. */ public void addComponent(Component c); /** * Adds the components in the given order to this component container. * * @param components * The components to add. */ public void addComponents(Component... components); /** * Removes the component from this container. * * @param c * the component to be removed. */ public void removeComponent(Component c); /** * Removes all components from this container. */ public void removeAllComponents(); /** * Replaces the component in the container with another one without changing * position. * *

* This method replaces component with another one is such way that the new * component overtakes the position of the old component. If the old * component is not in the container, the new component is added to the * container. If the both component are already in the container, their * positions are swapped. Component attach and detach events should be taken * care as with add and remove. *

* * @param oldComponent * the old component that will be replaced. * @param newComponent * the new component to be replaced. */ public void replaceComponent(Component oldComponent, Component newComponent); /** * Gets an iterator to the collection of contained components. Using this * iterator it is possible to step through all components contained in this * container. * * @return the component iterator. * * @deprecated As of 7.0, use {@link #iterator()} instead. */ @Deprecated public Iterator getComponentIterator(); /** * Gets the number of children this {@link ComponentContainer} has. This * must be symmetric with what {@link #getComponentIterator()} returns. * * @return The number of child components this container has. * @since 7.0.0 */ public int getComponentCount(); /** * Moves all components from an another container into this container. The * components are removed from source. * * @param source * the container which contains the components that are to be * moved to this container. */ public void moveComponentsFrom(ComponentContainer source); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2336 Content-Disposition: inline; filename="ComponentRootSetter.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "01edafc4d093385f85e62d49d824d1e36a1f57c8" /* * Copyright 2000-2022 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; /** * Internal utility class. * * @since 8.1 * @author Vaadin Ltd */ public class ComponentRootSetter implements Serializable { private ComponentRootSetter() { // Util methods only } /** * Sets the composition root for the given custom component or composite. *

* For internal use only. * * @param customComponent * the custom component or composite * @param component * the component to assign as composition root */ public static void setRoot(Component customComponent, Component component) { if (customComponent instanceof CustomComponent) { ((CustomComponent) customComponent).setCompositionRoot(component); } else if (customComponent instanceof Composite) { ((Composite) customComponent).setCompositionRoot(component); } else { throw new IllegalArgumentException( "Parameter is of an unsupported type: " + customComponent.getClass().getName()); } } /** * Checks if the given custom component or composite may accept a root * component. *

* For internal use only. * * @param customComponent * the custom component or composite * @return * @since 8.4 * */ public static boolean canSetRoot(Component customComponent) { if (customComponent instanceof CustomComponent) { return true; } if (customComponent instanceof Composite) { return ((Composite) customComponent).getCompositionRoot() == null; } return false; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 10214 Content-Disposition: inline; filename="Composite.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "7836eb9e60045d3c7b87d9bde6ca96a5630c2b41" /* * Copyright 2000-2022 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.util.Collections; import java.util.Iterator; import java.util.Objects; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Resource; import com.vaadin.server.SerializableFunction; import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.composite.CompositeState; /** * Composite allows creating new UI components by composition of existing * server-side components. *

* A composite is created by extending the Composite class and setting the * composition root component using {@link #setCompositionRoot(Component)}. *

*

* The composition root itself can contain more components. The advantage of * wrapping it in a composite is that the details of the composition root, such * as its public API, are hidden from the users of the composite. *

*

* A composite itself does not contribute to the DOM in any way (contrary to * {@link CustomComponent} which adds a {@code

} to the DOM. *

* * @author Vaadin Ltd. * @since 8.1 */ public class Composite extends AbstractComponent implements HasComponents { /** * The contained component. */ private Component root = null; /** * Constructs a new empty composite. *

* Use {@link #setCompositionRoot(Component)} to define the contents of the * composite. */ public Composite() { } /** * Constructs a new composite containing the given component. * * @param compositionRoot * the root of the composition component tree. It must not be * null. */ public Composite(AbstractComponent compositionRoot) { this(); Objects.requireNonNull(compositionRoot); setCompositionRoot(compositionRoot); } /** * Returns the composition root. * * @return the Component Composition root. */ protected Component getCompositionRoot() { return root; } /** * Sets the component contained in the composite. *

* You must set the composition root to a non-null value before the * component can be used. It cannot be changed. *

* * @param compositionRoot * the root of the composition component tree. */ protected void setCompositionRoot(Component compositionRoot) { if (root != null) { throw new IllegalStateException( "Composition root cannot be changed"); } if (compositionRoot == null) { throw new IllegalArgumentException( "Composition root cannot be null"); } // set new component if (compositionRoot.getParent() != null) { // If the component already has a parent, try to remove it AbstractSingleComponentContainer.removeFromParent(compositionRoot); } compositionRoot.setParent(this); root = compositionRoot; markAsDirty(); } /* Basic component features ------------------------------------------ */ @Override public Iterator iterator() { if (getCompositionRoot() != null) { return Collections.singletonList(getCompositionRoot()).iterator(); } else { return Collections. emptyList().iterator(); } } @Override protected CompositeState getState() { return (CompositeState) super.getState(); } @Override protected CompositeState getState(boolean markAsDirty) { return (CompositeState) super.getState(markAsDirty); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); if (getCompositionRoot() == null) { throw new IllegalStateException( "A composite must always have a composition root"); } } @Override public String getStyleName() { Component root = getCompositionRoot(); return root == null ? "" : root.getStyleName(); } @Override public void setStyleName(String style) { getRootOrThrow().setStyleName(style); } @Override public void setStyleName(String style, boolean add) { getRootAbstractComponentOrThrow().setStyleName(style, add); } @Override public void addStyleName(String style) { getRootOrThrow().addStyleName(style); } @Override public void removeStyleName(String style) { getRootOrThrow().removeStyleName(style); } @Override public String getPrimaryStyleName() { return getRootAbstractComponentPropertyOrNull( AbstractComponent::getPrimaryStyleName); } @Override public void setPrimaryStyleName(String style) { getRootOrThrow().setPrimaryStyleName(style); } private Component getRootOrThrow() { Component root = getCompositionRoot(); if (root == null) { throw new IllegalStateException( "Composition root has not been set"); } return root; } private AbstractComponent getRootAbstractComponentOrThrow() { Component root = getRootOrThrow(); if (!(root instanceof AbstractComponent)) { throw new IllegalStateException( "Composition root is not AbstractComponent"); } return (AbstractComponent) root; } private T getRootPropertyOrNull( SerializableFunction getter) { Component root = getCompositionRoot(); return root == null ? null : getter.apply(root); } private T getRootAbstractComponentPropertyOrNull( SerializableFunction getter) { Component root = getCompositionRoot(); if (root instanceof AbstractComponent) { return getter.apply((AbstractComponent) root); } return null; } @Override public void setEnabled(boolean enabled) { getRootOrThrow().setEnabled(enabled); } @Override public boolean isEnabled() { return getRootOrThrow().isEnabled(); } @Override public float getWidth() { return getRootOrThrow().getWidth(); } @Override public float getHeight() { return getRootOrThrow().getHeight(); } @Override public Unit getWidthUnits() { return getRootOrThrow().getWidthUnits(); } @Override public Unit getHeightUnits() { return getRootOrThrow().getHeightUnits(); } @Override public void setHeight(String height) { getRootOrThrow().setHeight(height); } @Override public void setWidth(float width, Unit unit) { getRootOrThrow().setWidth(width, unit); } @Override public void setHeight(float height, Unit unit) { getRootOrThrow().setHeight(height, unit); } @Override public void setWidth(String width) { getRootOrThrow().setWidth(width); } @Override public void setSizeFull() { getRootOrThrow().setSizeFull(); } @Override public void setSizeUndefined() { getRootOrThrow().setSizeUndefined(); } @Override public void setWidthUndefined() { getRootOrThrow().setWidthUndefined(); } @Override public void setHeightUndefined() { getRootOrThrow().setHeightUndefined(); } @Override public void setId(String id) { getRootOrThrow().setId(id); } @Override public String getId() { return getRootPropertyOrNull(Component::getId); } @Override public void setDebugId(String id) { getRootAbstractComponentOrThrow().setDebugId(id); } @Override public String getDebugId() { return getRootAbstractComponentPropertyOrNull( AbstractComponent::getDebugId); } @Override public String getCaption() { return getRootPropertyOrNull(Component::getCaption); } @Override public void setCaption(String caption) { getRootOrThrow().setCaption(caption); } @Override public void setCaptionAsHtml(boolean captionAsHtml) { getRootAbstractComponentOrThrow().setCaptionAsHtml(captionAsHtml); } @Override public boolean isCaptionAsHtml() { return getRootAbstractComponentPropertyOrNull( AbstractComponent::isCaptionAsHtml); } @Override public Resource getIcon() { return getRootPropertyOrNull(Component::getIcon); } @Override public void setIcon(Resource icon) { getRootOrThrow().setIcon(icon); } @Override public String getDescription() { return getRootOrThrow().getDescription(); } @Override public void setDescription(String description) { getRootAbstractComponentOrThrow().setDescription(description); } @Override public void setDescription(String description, ContentMode mode) { getRootAbstractComponentOrThrow().setDescription(description, mode); } @Override public ErrorMessage getErrorMessage() { return getRootAbstractComponentPropertyOrNull( AbstractComponent::getErrorMessage); } @Override public ErrorMessage getComponentError() { return getRootAbstractComponentPropertyOrNull( AbstractComponent::getComponentError); } @Override public void setComponentError(ErrorMessage componentError) { getRootAbstractComponentOrThrow().setComponentError(componentError); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 35968 Content-Disposition: inline; filename="ConnectorTracker.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "f555deeedcebb5f83204ca52491f0b1d56f91455" /* * Copyright 2000-2022 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.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.event.MarkedAsDirtyConnectorEvent; import com.vaadin.event.MarkedAsDirtyListener; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.ClientConnector; import com.vaadin.server.DragAndDropService; import com.vaadin.server.GlobalResourceHandler; import com.vaadin.server.LegacyCommunicationManager; import com.vaadin.server.StreamVariable; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; import com.vaadin.server.communication.ConnectorHierarchyWriter; import com.vaadin.shared.Registration; import elemental.json.Json; import elemental.json.JsonException; import elemental.json.JsonObject; /** * A class which takes care of book keeping of {@link ClientConnector}s for a * UI. *

* Provides {@link #getConnector(String)} which can be used to lookup a * connector from its id. This is for framework use only and should not be * needed in applications. *

*

* Tracks which {@link ClientConnector}s are dirty so they can be updated to the * client when the following response is sent. A connector is dirty when an * operation has been performed on it on the server and as a result of this * operation new information needs to be sent to its * {@link com.vaadin.client.ServerConnector}. *

* * @author Vaadin Ltd * @since 7.0.0 * */ public class ConnectorTracker implements Serializable { /** * Cache whether FINE messages are loggable. This is done to avoid * excessively calling getLogger() which might be slightly slow in some * specific environments. Please note that we're not caching the logger * instance itself because of * https://github.com/vaadin/framework/issues/2092. */ private static final boolean fineLogging = getLogger() .isLoggable(Level.FINE); private final Map connectorIdToConnector = new HashMap<>(); private final Set dirtyConnectors = new HashSet<>(); private final Set uninitializedConnectors = new HashSet<>(); private List markedDirtyListeners = new ArrayList<>( 0); /** * Connectors that have been unregistered and should be cleaned up the next * time {@link #cleanConnectorMap(boolean)} is invoked unless they have been * registered again. */ private final Set unregisteredConnectors = new HashSet<>(); private boolean writingResponse = false; private final UI uI; private transient Map diffStates = new HashMap<>(); /** Maps connectorIds to a map of named StreamVariables */ private Map> pidToNameToStreamVariable; private Map streamVariableToSeckey; private int currentSyncId = 0; /** * Gets a logger for this class * * @return A logger instance for logging within this class * */ private static Logger getLogger() { return Logger.getLogger(ConnectorTracker.class.getName()); } /** * Creates a new ConnectorTracker for the given uI. A tracker is always * attached to a uI and the uI cannot be changed during the lifetime of a * {@link ConnectorTracker}. * * @param uI * The uI to attach to. Cannot be null. */ public ConnectorTracker(UI uI) { this.uI = uI; } /** * Register the given connector. *

* The lookup method {@link #getConnector(String)} only returns registered * connectors. *

* * @param connector * The connector to register. */ public void registerConnector(ClientConnector connector) { boolean wasUnregistered = unregisteredConnectors.remove(connector); String connectorId = connector.getConnectorId(); ClientConnector previouslyRegistered = connectorIdToConnector .get(connectorId); if (previouslyRegistered == null) { connectorIdToConnector.put(connectorId, connector); uninitializedConnectors.add(connector); if (fineLogging) { getLogger().log(Level.FINE, "Registered {0} ({1})", new Object[] { connector.getClass().getSimpleName(), connectorId }); } } else if (previouslyRegistered != connector) { throw new RuntimeException("A connector with id " + connectorId + " is already registered!"); } else if (!wasUnregistered) { getLogger().log(Level.WARNING, "An already registered connector was registered again: {0} ({1})", new Object[] { connector.getClass().getSimpleName(), connectorId }); } dirtyConnectors.add(connector); } /** * Unregister the given connector. * *

* The lookup method {@link #getConnector(String)} only returns registered * connectors. *

* * @param connector * The connector to unregister */ public void unregisterConnector(ClientConnector connector) { String connectorId = connector.getConnectorId(); if (!connectorIdToConnector.containsKey(connectorId)) { getLogger().log(Level.WARNING, "Tried to unregister {0} ({1}) which is not registered", new Object[] { connector.getClass().getSimpleName(), connectorId }); return; } if (connectorIdToConnector.get(connectorId) != connector) { throw new RuntimeException("The given connector with id " + connectorId + " is not the one that was registered for that id"); } dirtyConnectors.remove(connector); if (!isClientSideInitialized(connector)) { // Client side has never known about this connector so there is no // point in tracking it removeUnregisteredConnector(connector, uI.getSession().getGlobalResourceHandler(false)); } else if (unregisteredConnectors.add(connector)) { // Client side knows about the connector, track it for a while if it // becomes reattached if (fineLogging) { getLogger().log(Level.FINE, "Unregistered {0} ({1})", new Object[] { connector.getClass().getSimpleName(), connectorId }); } } else { getLogger().log(Level.WARNING, "Unregistered {0} ({1}) that was already unregistered.", new Object[] { connector.getClass().getSimpleName(), connectorId }); } } /** * Checks whether the given connector has already been initialized in the * browser. The given connector should be registered with this connector * tracker. * * @param connector * the client connector to check * @return true if the initial state has previously been sent * to the browser, false if the client-side doesn't * already know anything about the connector. */ public boolean isClientSideInitialized(ClientConnector connector) { assert connectorIdToConnector.get(connector .getConnectorId()) == connector : "Connector should be registered with this ConnectorTracker"; return !uninitializedConnectors.contains(connector); } /** * Marks the given connector as initialized, meaning that the client-side * state has been initialized for the connector. * * @see #isClientSideInitialized(ClientConnector) * * @param connector * the connector that should be marked as initialized */ public void markClientSideInitialized(ClientConnector connector) { uninitializedConnectors.remove(connector); } /** * Marks all currently registered connectors as uninitialized. This should * be done when the client-side has been reset but the server-side state is * retained. * * @see #isClientSideInitialized(ClientConnector) */ public void markAllClientSidesUninitialized() { uninitializedConnectors.addAll(connectorIdToConnector.values()); diffStates.clear(); } /** * Gets a connector by its id. * * @param connectorId * The connector id to look for * @return The connector with the given id or null if no connector has the * given id */ public ClientConnector getConnector(String connectorId) { ClientConnector connector = connectorIdToConnector.get(connectorId); // Ignore connectors that have been unregistered but not yet cleaned up if (unregisteredConnectors.contains(connector)) { return null; } else if (connector != null) { return connector; } else { DragAndDropService service = uI.getSession() .getDragAndDropService(); if (connectorId.equals(service.getConnectorId())) { return service; } } return null; } /** * Cleans the connector map from all connectors that are no longer attached * to the application if there are dirty connectors or the force flag is * true. This should only be called by the framework. * * @param force * {@code true} to force cleaning * @since 8.2 */ public void cleanConnectorMap(boolean force) { if (force || !dirtyConnectors.isEmpty()) { cleanConnectorMap(); } } /** * Cleans the connector map from all connectors that are no longer attached * to the application. This should only be called by the framework. * * @deprecated use {@link #cleanConnectorMap(boolean)} instead */ @Deprecated public void cleanConnectorMap() { removeUnregisteredConnectors(); cleanStreamVariables(); // Do this expensive check only with assertions enabled assert isHierarchyComplete() : "The connector hierarchy is corrupted. " + "Check for missing calls to super.setParent(), super.attach() and super.detach() " + "and that all custom component containers call child.setParent(this) when a child is added and child.setParent(null) when the child is no longer used. " + "See previous log messages for details."; Iterator iterator = connectorIdToConnector.values() .iterator(); GlobalResourceHandler globalResourceHandler = uI.getSession() .getGlobalResourceHandler(false); while (iterator.hasNext()) { ClientConnector connector = iterator.next(); assert connector != null; if (connector.getUI() != uI) { // If connector is no longer part of this uI, // remove it from the map. If it is re-attached to the // application at some point it will be re-added through // registerConnector(connector) // This code should never be called as cleanup should take place // in detach() getLogger().log(Level.WARNING, "cleanConnectorMap unregistered connector {0}. This should have been done when the connector was detached.", getConnectorAndParentInfo(connector)); if (globalResourceHandler != null) { globalResourceHandler.unregisterConnector(connector); } uninitializedConnectors.remove(connector); diffStates.remove(connector); iterator.remove(); } else if (!uninitializedConnectors.contains(connector) && !LegacyCommunicationManager .isConnectorVisibleToClient(connector)) { // Connector was visible to the client but is no longer (e.g. // setVisible(false) has been called or SelectiveRenderer tells // it's no longer shown) -> make sure that the full state is // sent again when/if made visible uninitializedConnectors.add(connector); diffStates.remove(connector); assert isRemovalSentToClient(connector) : "Connector " + connector + " (id = " + connector.getConnectorId() + ") is no longer visible to the client, but no corresponding hierarchy change was sent."; if (fineLogging) { getLogger().log(Level.FINE, "cleanConnectorMap removed state for {0} as it is not visible", getConnectorAndParentInfo(connector)); } } } } private boolean isRemovalSentToClient(ClientConnector connector) { VaadinRequest request = VaadinService.getCurrentRequest(); if (request == null) { // Probably run from a unit test without normal request handling return true; } String attributeName = ConnectorHierarchyWriter.class.getName() + ".hierarchyInfo"; Object hierarchyInfoObj = request.getAttribute(attributeName); if (hierarchyInfoObj instanceof JsonObject) { JsonObject hierachyInfo = (JsonObject) hierarchyInfoObj; ClientConnector firstVisibleParent = findFirstVisibleParent( connector); if (firstVisibleParent == null) { // Connector is detached, not our business return true; } if (!hierachyInfo.hasKey(firstVisibleParent.getConnectorId())) { /* * No hierarchy change about to be sent, but this might be * because of an optimization that omits explicit hierarchy * changes for empty connectors that have state changes. */ if (hasVisibleChild(firstVisibleParent)) { // Not the optimization case if the parent has visible // children return false; } attributeName = ConnectorHierarchyWriter.class.getName() + ".stateUpdateConnectors"; Object stateUpdateConnectorsObj = request .getAttribute(attributeName); if (stateUpdateConnectorsObj instanceof Set) { Set stateUpdateConnectors = (Set) stateUpdateConnectorsObj; if (!stateUpdateConnectors .contains(firstVisibleParent.getConnectorId())) { // Not the optimization case if the parent is not marked // as dirty return false; } } else { getLogger().warning("Request attribute " + attributeName + " is not a Set"); } } } else { getLogger().warning("Request attribute " + attributeName + " is not a JsonObject"); } return true; } private static boolean hasVisibleChild(ClientConnector parent) { Iterable iterable = AbstractClientConnector .getAllChildrenIterable(parent); for (ClientConnector child : iterable) { if (LegacyCommunicationManager.isConnectorVisibleToClient(child)) { return true; } } return false; } private ClientConnector findFirstVisibleParent(ClientConnector connector) { while (connector != null) { connector = connector.getParent(); if (LegacyCommunicationManager .isConnectorVisibleToClient(connector)) { return connector; } } return null; } /** * Removes all references and information about connectors marked as * unregistered. * */ private void removeUnregisteredConnectors() { GlobalResourceHandler globalResourceHandler = uI.getSession() .getGlobalResourceHandler(false); for (ClientConnector connector : unregisteredConnectors) { removeUnregisteredConnector(connector, globalResourceHandler); } unregisteredConnectors.clear(); } /** * Removes all references and information about the given connector, which * must not be registered. * * @param connector * @param globalResourceHandler */ private void removeUnregisteredConnector(ClientConnector connector, GlobalResourceHandler globalResourceHandler) { ClientConnector removedConnector = connectorIdToConnector .remove(connector.getConnectorId()); assert removedConnector == connector; if (globalResourceHandler != null) { globalResourceHandler.unregisterConnector(connector); } uninitializedConnectors.remove(connector); diffStates.remove(connector); } /** * Checks that the connector hierarchy is consistent. * * @return true if the hierarchy is consistent, * false otherwise * @since 8.1 */ private boolean isHierarchyComplete() { boolean noErrors = true; Set danglingConnectors = new HashSet<>( connectorIdToConnector.values()); LinkedList stack = new LinkedList<>(); stack.add(uI); while (!stack.isEmpty()) { ClientConnector connector = stack.pop(); danglingConnectors.remove(connector); Iterable children = AbstractClientConnector .getAllChildrenIterable(connector); for (ClientConnector child : children) { stack.add(child); if (!connector.equals(child.getParent())) { noErrors = false; getLogger().log(Level.WARNING, "{0} claims that {1} is its child, but the child claims {2} is its parent.", new Object[] { getConnectorString(connector), getConnectorString(child), getConnectorString(child.getParent()) }); } } } for (ClientConnector dangling : danglingConnectors) { noErrors = false; getLogger().log(Level.WARNING, "{0} claims that {1} is its parent, but the parent does not acknowledge the parenthood.", new Object[] { getConnectorString(dangling), getConnectorString(dangling.getParent()) }); } return noErrors; } /** * Mark the connector as dirty and notifies any marked as dirty listeners. * This should not be done while the response is being written. * * @see #getDirtyConnectors() * @see #isWritingResponse() * * @param connector * The connector that should be marked clean. */ public void markDirty(ClientConnector connector) { if (isWritingResponse()) { throw new IllegalStateException( "A connector should not be marked as dirty while a response is being written."); } if (fineLogging && !isDirty(connector)) { getLogger().log(Level.FINE, "{0} is now dirty", getConnectorAndParentInfo(connector)); } if (!isDirty(connector)) { notifyMarkedAsDirtyListeners(connector); } dirtyConnectors.add(connector); } /** * Mark the connector as clean. * * @param connector * The connector that should be marked clean. */ public void markClean(ClientConnector connector) { if (fineLogging && dirtyConnectors.contains(connector)) { getLogger().log(Level.FINE, "{0} is no longer dirty", getConnectorAndParentInfo(connector)); } dirtyConnectors.remove(connector); } /** * Returns {@link #getConnectorString(ClientConnector)} for the connector * and its parent (if it has a parent). * * @param connector * The connector * @return A string describing the connector and its parent */ private String getConnectorAndParentInfo(ClientConnector connector) { String message = getConnectorString(connector); if (connector.getParent() != null) { message += " (parent: " + getConnectorString(connector.getParent()) + ")"; } return message; } /** * Returns a string with the connector name and id. Useful mostly for * debugging and logging. * * @param connector * The connector * @return A string that describes the connector */ private String getConnectorString(ClientConnector connector) { if (connector == null) { return "(null)"; } String connectorId; try { connectorId = connector.getConnectorId(); } catch (RuntimeException e) { // This happens if the connector is not attached to the application. // SHOULD not happen in this case but theoretically can. connectorId = "@" + Integer.toHexString(connector.hashCode()); } return connector.getClass().getName() + "(" + connectorId + ")"; } /** * Mark all connectors in this uI as dirty. */ public void markAllConnectorsDirty() { markConnectorsDirtyRecursively(uI); if (fineLogging) { getLogger().fine("All connectors are now dirty"); } } /** * Mark all connectors in this uI as clean. */ public void markAllConnectorsClean() { dirtyConnectors.clear(); if (fineLogging) { getLogger().fine("All connectors are now clean"); } } /** * Marks all visible connectors dirty, starting from the given connector and * going downwards in the hierarchy. * * @param c * The component to start iterating downwards from */ private void markConnectorsDirtyRecursively(ClientConnector c) { if (c instanceof Component && !((Component) c).isVisible()) { return; } markDirty(c); for (ClientConnector child : AbstractClientConnector .getAllChildrenIterable(c)) { markConnectorsDirtyRecursively(child); } } /** * Returns a collection of all connectors which have been marked as dirty. *

* The state and pending RPC calls for dirty connectors are sent to the * client in the following request. *

* * @return A collection of all dirty connectors for this uI. This list may * contain invisible connectors. */ public Collection getDirtyConnectors() { return dirtyConnectors; } /** * Checks if there a dirty connectors. * * @return true if there are dirty connectors, false otherwise */ public boolean hasDirtyConnectors() { return !getDirtyConnectors().isEmpty(); } /** * Returns a collection of those {@link #getDirtyConnectors() dirty * connectors} that are actually visible to the client. * * @return A list of dirty and visible connectors. */ public ArrayList getDirtyVisibleConnectors() { Collection dirtyConnectors = getDirtyConnectors(); ArrayList dirtyVisibleConnectors = new ArrayList<>( dirtyConnectors.size()); for (ClientConnector c : dirtyConnectors) { if (LegacyCommunicationManager.isConnectorVisibleToClient(c)) { dirtyVisibleConnectors.add(c); } } return dirtyVisibleConnectors; } public JsonObject getDiffState(ClientConnector connector) { assert getConnector(connector.getConnectorId()) == connector; return diffStates.get(connector); } public void setDiffState(ClientConnector connector, JsonObject diffState) { assert getConnector(connector.getConnectorId()) == connector; diffStates.put(connector, diffState); } public boolean isDirty(ClientConnector connector) { return dirtyConnectors.contains(connector); } /** * Checks whether the response is currently being written. Connectors can * not be marked as dirty when a response is being written. * * @see #setWritingResponse(boolean) * @see #markDirty(ClientConnector) * * @return true if the response is currently being written, * false if outside the response writing phase. */ public boolean isWritingResponse() { return writingResponse; } /** * Sets the current response write status. Connectors can not be marked as * dirty when the response is written. *

* This method has a side-effect of incrementing the sync id by one (see * {@link #getCurrentSyncId()}), if {@link #isWritingResponse()} returns * true and writingResponse is set to * false. * * @param writingResponse * the new response status. * * @see #markDirty(ClientConnector) * @see #isWritingResponse() * @see #getCurrentSyncId() * * @throws IllegalArgumentException * if the new response status is the same as the previous value. * This is done to help detecting problems caused by missed * invocations of this method. */ public void setWritingResponse(boolean writingResponse) { if (this.writingResponse == writingResponse) { throw new IllegalArgumentException( "The old value is same as the new value"); } /* * the right hand side of the && is unnecessary here because of the * if-clause above, but rigorous coding is always rigorous coding. */ if (!writingResponse && this.writingResponse) { // Bump sync id when done writing - the client is not expected to // know about anything happening after this moment. currentSyncId++; } this.writingResponse = writingResponse; } /* Special serialization to JsonObjects which are not serializable */ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // Convert JsonObjects in diff state to String representation as // JsonObject is not serializable Map stringDiffStates = new HashMap<>( diffStates.size() * 2); for (ClientConnector key : diffStates.keySet()) { stringDiffStates.put(key, diffStates.get(key).toString()); } out.writeObject(stringDiffStates); } /* Special serialization to JsonObjects which are not serializable */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // Read String versions of JsonObjects and parse into JsonObjects as // JsonObject is not serializable diffStates = new HashMap<>(); @SuppressWarnings("unchecked") Map stringDiffStates = (HashMap) in .readObject(); diffStates = new HashMap<>(stringDiffStates.size() * 2); for (ClientConnector key : stringDiffStates.keySet()) { try { diffStates.put(key, Json.parse(stringDiffStates.get(key))); } catch (JsonException e) { throw new IOException(e); } } } /** * Checks if the indicated connector has a StreamVariable of the given name * and returns the variable if one is found. * * @param connectorId * @param variableName * @return variable if a matching one exists, otherwise null */ public StreamVariable getStreamVariable(String connectorId, String variableName) { if (pidToNameToStreamVariable == null) { return null; } Map map = pidToNameToStreamVariable .get(connectorId); if (map == null) { return null; } StreamVariable streamVariable = map.get(variableName); return streamVariable; } /** * Adds a StreamVariable of the given name to the indicated connector. * * @param connectorId * @param variableName * @param variable */ public void addStreamVariable(String connectorId, String variableName, StreamVariable variable) { assert getConnector(connectorId) != null; if (pidToNameToStreamVariable == null) { pidToNameToStreamVariable = new HashMap<>(); } Map nameToStreamVariable = pidToNameToStreamVariable .get(connectorId); if (nameToStreamVariable == null) { nameToStreamVariable = new HashMap<>(); pidToNameToStreamVariable.put(connectorId, nameToStreamVariable); } nameToStreamVariable.put(variableName, variable); if (streamVariableToSeckey == null) { streamVariableToSeckey = new HashMap<>(); } String seckey = streamVariableToSeckey.get(variable); if (seckey == null) { /* * Despite section 6 of RFC 4122, this particular use of UUID *is* * adequate for security capabilities. Type 4 UUIDs contain 122 bits * of random data, and UUID.randomUUID() is defined to use a * cryptographically secure random generator. */ seckey = UUID.randomUUID().toString(); streamVariableToSeckey.put(variable, seckey); } } /** * Removes StreamVariables that belong to connectors that are no longer * attached to the session. */ private void cleanStreamVariables() { if (pidToNameToStreamVariable != null) { ConnectorTracker connectorTracker = uI.getConnectorTracker(); Iterator iterator = pidToNameToStreamVariable.keySet() .iterator(); while (iterator.hasNext()) { String connectorId = iterator.next(); if (connectorTracker.getConnector(connectorId) == null) { // Owner is no longer attached to the session Map removed = pidToNameToStreamVariable .get(connectorId); for (String key : removed.keySet()) { streamVariableToSeckey.remove(removed.get(key)); } iterator.remove(); } } } } /** * Removes any StreamVariable of the given name from the indicated * connector. * * @param connectorId * @param variableName */ public void cleanStreamVariable(String connectorId, String variableName) { if (pidToNameToStreamVariable == null) { return; } Map nameToStreamVar = pidToNameToStreamVariable .get(connectorId); if (nameToStreamVar != null) { StreamVariable streamVar = nameToStreamVar.remove(variableName); streamVariableToSeckey.remove(streamVar); if (nameToStreamVar.isEmpty()) { pidToNameToStreamVariable.remove(connectorId); } } } /** * Returns the security key associated with the given StreamVariable. * * @param variable * @return matching security key if one exists, null otherwise */ public String getSeckey(StreamVariable variable) { if (streamVariableToSeckey == null) { return null; } return streamVariableToSeckey.get(variable); } /** * Gets the most recently generated server sync id. *

* The sync id is incremented by one whenever a new response is being * written. This id is then sent over to the client. The client then adds * the most recent sync id to each communication packet it sends back to the * server. This way, the server knows at what state the client is when the * packet is sent. If the state has changed on the server side since that, * the server can try to adjust the way it handles the actions from the * client side. *

* The sync id value -1 is ignored to facilitate testing with * pre-recorded requests. * * @see #setWritingResponse(boolean) * @see #connectorWasPresentAsRequestWasSent(String, long) * @since 7.2 * @return the current sync id */ public int getCurrentSyncId() { return currentSyncId; } /** * Add a marked as dirty listener that will be called when a client * connector is marked as dirty. * * @param listener * listener to add * @since 8.4 * @return registration for removing listener registration */ public Registration addMarkedAsDirtyListener( MarkedAsDirtyListener listener) { markedDirtyListeners.add(listener); return () -> markedDirtyListeners.remove(listener); } /** * Notify all registered MarkedAsDirtyListeners the given client connector * has been marked as dirty. * * @param connector * client connector marked as dirty * @since 8.4 */ public void notifyMarkedAsDirtyListeners(ClientConnector connector) { MarkedAsDirtyConnectorEvent event = new MarkedAsDirtyConnectorEvent( connector, uI); new ArrayList<>(markedDirtyListeners).forEach(listener -> { listener.connectorMarkedAsDirty(event); }); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 12693 Content-Disposition: inline; filename="CssLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "dc7ae07f31ed67d2348fee1fd78498085d9a3258" /* * Copyright 2000-2022 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.util.Collections; import java.util.Iterator; import java.util.LinkedList; import org.jsoup.nodes.Element; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.csslayout.CssLayoutServerRpc; import com.vaadin.shared.ui.csslayout.CssLayoutState; import com.vaadin.ui.declarative.DesignContext; /** * CssLayout is a layout component that can be used in browser environment only. * It simply renders components and their captions into a same div element. * Component layout can then be adjusted with css. *

* In comparison to {@link HorizontalLayout} and {@link VerticalLayout} *

    *
  • rather similar server side api *
  • no spacing, alignment or expand ratios *
  • much simpler DOM that can be styled by skilled web developer *
  • no abstraction of browser differences (developer must ensure that the * result works properly on each browser) *
  • different kind of handling for relative sizes (that are set from server * side) (*) *
  • noticeably faster rendering time in some situations as we rely more on * the browser's rendering engine. *
*

* With {@link CustomLayout} one can often achieve similar results (good looking * layouts with web technologies), but with CustomLayout developer needs to work * with fixed templates. *

* By extending CssLayout one can also inject some css rules straight to child * components using {@link #getCss(Component)}. * *

* (*) Relative sizes (set from server side) are treated bit differently than in * other layouts in Vaadin. In cssLayout the size is calculated relatively to * CSS layouts content area which is pretty much as in html and css. In other * layouts the size of component is calculated relatively to the "slot" given by * layout. *

* Also note that client side framework in Vaadin modifies inline style * properties width and height. This happens on each update to component. If one * wants to set component sizes with CSS, component must have undefined size on * server side (which is not the default for all components) and the size must * be defined with class styles - not by directly injecting width and height. * * @since 6.1 brought in from "FastLayouts" incubator project * */ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { private CssLayoutServerRpc rpc = (MouseEventDetails mouseDetails, Connector clickedConnector) -> fireEvent( LayoutClickEvent.createEvent(CssLayout.this, mouseDetails, clickedConnector)); /** * Custom layout slots containing the components. */ protected LinkedList components = new LinkedList<>(); /** * Constructs an empty CssLayout. */ public CssLayout() { registerRpc(rpc); } /** * Constructs a CssLayout with the given components in the given order. * * @see #addComponents(Component...) * * @param children * Components to add to the container. */ public CssLayout(Component... children) { this(); addComponents(children); } /** * Add a component into this container. The component is added to the right * or below the previous component. * * @param c * the component to be added. */ @Override public void addComponent(Component c) { // Add to components before calling super.addComponent // so that it is available to AttachListeners components.add(c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } } /** * Adds a component into this container. The component is added to the left * or on top of the other components. * * @param c * the component to be added. */ public void addComponentAsFirst(Component c) { // If c is already in this, we must remove it before proceeding // see ticket #7668 if (equals(c.getParent())) { removeComponent(c); } components.addFirst(c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } } /** * Adds a component into indexed position in this container. * * @param c * the component to be added. * @param index * the index of the component position. The components currently * in and after the position are shifted forwards. */ public void addComponent(Component c, int index) { // If c is already in this, we must remove it before proceeding // see ticket #7668 if (equals(c.getParent())) { // When c is removed, all components after it are shifted down if (index > getComponentIndex(c)) { index--; } removeComponent(c); } components.add(index, c); try { super.addComponent(c); } catch (IllegalArgumentException e) { components.remove(c); throw e; } } /** * Removes the component from this container. * * @param c * the component to be removed. */ @Override public void removeComponent(Component c) { components.remove(c); super.removeComponent(c); } /** * Gets the component container iterator for going trough all the components * in the container. * * @return the Iterator of the components inside the container. */ @Override public Iterator iterator() { return Collections.unmodifiableCollection(components).iterator(); } /** * Gets the number of contained components. Consistent with the iterator * returned by {@link #getComponentIterator()}. * * @return the number of contained components */ @Override public int getComponentCount() { return components.size(); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); // This is an obsolete hack that was required before Map // was supported. The workaround is to instead use a Map with // the connector id as the key, but that can only be used once the // connector has been attached. getState().childCss.clear(); for (Iterator ci = getComponentIterator(); ci.hasNext();) { Component child = ci.next(); String componentCssString = getCss(child); if (componentCssString != null) { getState().childCss.put(child, componentCssString); } } } @Override protected CssLayoutState getState() { return (CssLayoutState) super.getState(); } @Override protected CssLayoutState getState(boolean markAsDirty) { return (CssLayoutState) super.getState(markAsDirty); } /** * Returns styles to be applied to given component. Override this method to * inject custom style rules to components. * *

* Note that styles are injected over previous styles before actual child * rendering. Previous styles are not cleared, but overridden. * *

* Note that one most often achieves better code style, by separating * styling to theme (with custom theme and {@link #addStyleName(String)}. * With own custom styles it is also very easy to break browser * compatibility. * * @param c * the component * @return css rules to be applied to component */ protected String getCss(Component c) { return null; } /* Documented in superclass */ @Override public void replaceComponent(Component oldComponent, Component newComponent) { // Gets the locations int oldLocation = -1; int newLocation = -1; int location = 0; for (final Component component : components) { if (component == oldComponent) { oldLocation = location; } if (component == newComponent) { newLocation = location; } location++; } if (oldLocation == -1) { addComponent(newComponent); } else if (newLocation == -1) { removeComponent(oldComponent); addComponent(newComponent, oldLocation); } else { if (oldLocation > newLocation) { components.remove(oldComponent); components.add(newLocation, oldComponent); components.remove(newComponent); components.add(oldLocation, newComponent); } else { components.remove(newComponent); components.add(oldLocation, newComponent); components.remove(oldComponent); components.add(newLocation, oldComponent); } markAsDirty(); } } @Override public Registration addLayoutClickListener(LayoutClickListener listener) { return addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener, LayoutClickListener.clickMethod); } @Override @Deprecated public void removeLayoutClickListener(LayoutClickListener listener) { removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener); } /** * Returns the index of the given component. * * @param component * The component to look up. * @return The index of the component or -1 if the component is not a child. */ public int getComponentIndex(Component component) { return components.indexOf(component); } /** * Returns the component at the given position. * * @param index * The position of the component. * @return The component at the given index. * @throws IndexOutOfBoundsException * If the index is out of range. */ public Component getComponent(int index) throws IndexOutOfBoundsException { return components.get(index); } /* * (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) { // process default attributes super.readDesign(design, designContext); // handle children for (Element childComponent : design.children()) { Component newChild = designContext.readDesign(childComponent); addComponent(newChild); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element * , com.vaadin.ui.declarative.DesignContext) */ @Override public void writeDesign(Element design, DesignContext designContext) { // write default attributes super.writeDesign(design, designContext); CssLayout def = designContext.getDefaultInstance(this); // handle children if (!designContext.shouldWriteChildren(this, def)) { return; } Element designElement = design; for (Component child : this) { Element childNode = designContext.createElement(child); designElement.appendChild(childNode); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 4913 Content-Disposition: inline; filename="CustomComponent.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "0bd3fa27607793fe472866ba1961d086cc4210d1" /* * Copyright 2000-2022 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.util.Collections; import java.util.Iterator; import com.vaadin.shared.customcomponent.CustomComponentState; /** * Custom component provides a simple implementation of the {@link Component} * interface to allow creating new UI components by composition of existing * server-side components. * *

* The component is used by inheriting the CustomComponent class and setting the * composition root component. The composition root must be set with * {@link #setCompositionRoot(Component)} before the CustomComponent is used, * such as by adding it to a layout, so it is preferable to set it in the * constructor. *

* *

* The composition root itself can contain more components. The advantage of * wrapping it in a CustomComponent is that its details, such as interfaces, are * hidden from the users of the component, thereby contributing to information * hiding. *

* *

* The CustomComponent does not display the caption of the composition root, so * if you want to have it shown in the layout where the custom component is * contained, you need to set it as caption of the CustomComponent. *

* *

* The component expands horizontally and has undefined height by default. *

* * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class CustomComponent extends AbstractComponent implements HasComponents { /** * The root component implementing the custom component. */ private Component root = null; /** * Constructs a new custom component. * *

* Note that you must set the composition root before the component can be * used, preferably in the constructor. *

*/ public CustomComponent() { // Expand horizontally by default setWidth(100, Unit.PERCENTAGE); } /** * Constructs a new custom component. * * @param compositionRoot * the root of the composition component tree. It must not be * null. */ public CustomComponent(Component compositionRoot) { this(); setCompositionRoot(compositionRoot); } /** * Returns the composition root. * * @return the Component Composition root. */ protected Component getCompositionRoot() { return root; } /** * Sets the composition root for the component. * *

* You must set the composition root to a non-null value before the * component can be used. You can change it later. *

* * @param compositionRoot * the root of the composition component tree. */ protected void setCompositionRoot(Component compositionRoot) { if (compositionRoot != root) { if (root != null && equals(root.getParent())) { // remove old component root.setParent(null); } if (compositionRoot != null) { // set new component if (compositionRoot.getParent() != null) { // If the component already has a parent, try to remove it AbstractSingleComponentContainer .removeFromParent(compositionRoot); } compositionRoot.setParent(this); } root = compositionRoot; markAsDirty(); } } /* Basic component features ------------------------------------------ */ @Override public Iterator iterator() { if (getCompositionRoot() != null) { return Collections.singletonList(getCompositionRoot()).iterator(); } else { return Collections. emptyList().iterator(); } } /** * Gets the number of contained components. * * @return the number of contained components (zero or one) */ public int getComponentCount() { return (root != null ? 1 : 0); } @Override protected CustomComponentState getState() { return (CustomComponentState) super.getState(); } @Override protected CustomComponentState getState(boolean markAsDirty) { return (CustomComponentState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5762 Content-Disposition: inline; filename="CustomField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "9aec5e3c716ed31e0001255a7c7a1cff4557d6a3" /* * Copyright 2000-2022 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.util.Collections; import java.util.Iterator; import com.vaadin.data.HasValue; import com.vaadin.shared.ui.customfield.CustomFieldState; /** * A {@link HasValue} whose UI content can be constructed by the user, enabling * the creation of e.g. form fields by composing Vaadin components. * Customization of both the visual presentation and the logic of the field is * possible. *

* Subclasses must implement {@link #initContent()}. *

* Most custom fields can simply compose a user interface that calls the methods * {@link #doSetValue(Object)} and {@link #getValue()} when necessary. * * @param * field value type * * @since 8.0 */ public abstract class CustomField extends AbstractField implements HasComponents { /** * The root component implementing the custom component. */ private Component root = null; /** * Constructs a new custom field. * *

* The component is implemented by wrapping the methods of the composition * root component given as parameter. The composition root must be set * before the component can be used. *

*/ public CustomField() { // expand horizontally by default setWidth(100, Unit.PERCENTAGE); } /** * Constructs the content and notifies it that the {@link CustomField} is * attached to a window. * * @see com.vaadin.ui.Component#attach() */ @Override public void attach() { // First call super attach to notify all children (none if content has // not yet been created) super.attach(); // If the content has not yet been created, create and attach it at // this point by calling getContent() getContent(); } /** * Returns the content (UI) of the custom component. * * @return Component */ protected Component getContent() { if (null == root) { root = initContent(); root.setParent(this); } return root; } /** * Create the content component or layout for the field. Subclasses of * {@link CustomField} should implement this method. * * Note that this method is called when the CustomField is attached to a * layout or when {@link #getContent()} is called explicitly for the first * time. It is only called once for a {@link CustomField}. * * @return {@link Component} representing the UI of the CustomField */ protected abstract Component initContent(); // Size related methods // TODO might not be necessary to override but following the pattern from // AbstractComponentContainer @Override public void setHeight(float height, Unit unit) { super.setHeight(height, unit); markAsDirtyRecursive(); } @Override public void setWidth(float width, Unit unit) { super.setWidth(width, unit); markAsDirtyRecursive(); } @Override protected CustomFieldState getState() { return (CustomFieldState) super.getState(); } @Override protected CustomFieldState getState(boolean markAsDirty) { return (CustomFieldState) super.getState(markAsDirty); } // ComponentContainer methods @Override public Iterator iterator() { // Can't use getContent() here as this will cause an infinite loop if // initContent happens to all iterator(). This happens if you do // setWidth... if (root != null) { return Collections.singletonList(root).iterator(); } else { return Collections. emptyList().iterator(); } } /** * Sets the component to which all methods from the {@link Focusable} * interface should be delegated. *

* Set this to a wrapped field to include that field in the tabbing order, * to make it receive focus when {@link #focus()} is called and to make it * be correctly focused when used as a Grid editor component. *

* By default, {@link Focusable} events are handled by the super class and * ultimately ignored. * * @param focusDelegate * the focusable component to which focus events are redirected */ public void setFocusDelegate(Focusable focusDelegate) { getState().focusDelegate = focusDelegate; } private Focusable getFocusable() { return (Focusable) getState(false).focusDelegate; } @Override public void focus() { if (getFocusable() != null) { getFocusable().focus(); } else { super.focus(); } } @Override public int getTabIndex() { if (getFocusable() != null) { return getFocusable().getTabIndex(); } else { return super.getTabIndex(); } } @Override public void setTabIndex(int tabIndex) { if (getFocusable() != null) { getFocusable().setTabIndex(tabIndex); } else { super.setTabIndex(tabIndex); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 11325 Content-Disposition: inline; filename="CustomLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "17fa00fca576c25b228983c2c915e8c7e00b45e7" /* * Copyright 2000-2022 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 static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jsoup.nodes.Element; import com.vaadin.server.JsonPaintTarget; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.ui.customlayout.CustomLayoutState; import com.vaadin.ui.declarative.DesignContext; /** *

* A container component with freely designed layout and style. The layout * consists of items with textually represented locations. Each item contains * one sub-component, which can be any Vaadin component, such as a layout. The * adapter and theme are responsible for rendering the layout with a given style * by placing the items in the defined locations. *

* *

* The placement of the locations is not fixed - different themes can define the * locations in a way that is suitable for them. One typical example would be to * create visual design for a web site as a custom layout: the visual design * would define locations for "menu", "body", and "title", for example. The * layout would then be implemented as an HTML template for each theme. *

* *

* A location is identified with the attribute "data-location" or "location" * which has the location name as its value. *

* *

* The default theme handles the styles that are not defined by drawing the * subcomponents just as in OrderedLayout. *

* * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class CustomLayout extends AbstractLayout implements LegacyComponent { private static final int BUFFER_SIZE = 10000; /** * Custom layout slots containing the components. */ private final Map slots = new HashMap<>(); /** * Default constructor only used by subclasses. Subclasses are responsible * for setting the appropriate fields. Either * {@link #setTemplateName(String)}, that makes layout fetch the template * from theme, or {@link #setTemplateContents(String)}. * * @since 7.5.0 */ public CustomLayout() { setWidth(100, Unit.PERCENTAGE); } /** * Constructs a custom layout with the template given in the stream. * * @param templateStream * Stream containing template data. Must be using UTF-8 encoding. * To use a String as a template use for instance new * ByteArrayInputStream("<template>".getBytes()). * @throws IOException */ public CustomLayout(InputStream templateStream) throws IOException { this(); initTemplateContentsFromInputStream(templateStream); } /** * Constructor for custom layout with given template name. Template file is * fetched from "<theme>/layouts/<templateName>". */ public CustomLayout(String template) { this(); setTemplateName(template); } protected void initTemplateContentsFromInputStream( InputStream templateStream) throws IOException { BufferedReader reader = new BufferedReader( new InputStreamReader(templateStream, UTF_8)); StringBuilder builder = new StringBuilder(BUFFER_SIZE); try { char[] cbuf = new char[BUFFER_SIZE]; int nRead; while ((nRead = reader.read(cbuf, 0, BUFFER_SIZE)) > 0) { builder.append(cbuf, 0, nRead); } } finally { reader.close(); } setTemplateContents(builder.toString()); } @Override protected CustomLayoutState getState() { return (CustomLayoutState) super.getState(); } @Override protected CustomLayoutState getState(boolean markAsDirty) { return (CustomLayoutState) super.getState(markAsDirty); } /** * Adds the component into this container to given location. If the location * is already populated, the old component is removed. * * @param c * the component to be added. * @param location * the location of the component. */ public void addComponent(Component c, String location) { final Component old = slots.get(location); if (old != null) { removeComponent(old); } slots.put(location, c); getState().childLocations.put(c, location); super.addComponent(c); } /** * Adds the component into this container. The component is added without * specifying the location (empty string is then used as location). Only one * component can be added to the default "" location and adding more * components into that location overwrites the old components. * * @param c * the component to be added. */ @Override public void addComponent(Component c) { this.addComponent(c, ""); } /** * Removes the component from this container. * * @param c * the component to be removed. */ @Override public void removeComponent(Component c) { if (c == null) { return; } slots.values().remove(c); getState().childLocations.remove(c); super.removeComponent(c); } /** * Removes the component from this container from given location. * * @param location * the Location identifier of the component. */ public void removeComponent(String location) { this.removeComponent(slots.get(location)); } /** * Gets the component container iterator for going trough all the components * in the container. * * @return the Iterator of the components inside the container. */ @Override public Iterator iterator() { return Collections.unmodifiableCollection(slots.values()).iterator(); } /** * Gets the number of contained components. Consistent with the iterator * returned by {@link #getComponentIterator()}. * * @return the number of contained components */ @Override public int getComponentCount() { return slots.values().size(); } /** * Gets the child-component by its location. * * @param location * the name of the location where the requested component * resides. * @return the Component in the given location or null if not found. */ public Component getComponent(String location) { return slots.get(location); } /* Documented in superclass */ @Override public void replaceComponent(Component oldComponent, Component newComponent) { // Gets the locations String oldLocation = null; String newLocation = null; for (final String location : slots.keySet()) { final Component component = slots.get(location); if (component == oldComponent) { oldLocation = location; } if (component == newComponent) { newLocation = location; } } if (oldLocation == null) { addComponent(newComponent); } else if (newLocation == null) { removeComponent(oldLocation); addComponent(newComponent, oldLocation); } else { slots.put(newLocation, oldComponent); slots.put(oldLocation, newComponent); getState().childLocations.put(newComponent, oldLocation); getState().childLocations.put(oldComponent, newLocation); } } /** Get the name of the template. */ public String getTemplateName() { return getState(false).templateName; } /** Get the contents of the template. */ public String getTemplateContents() { return getState(false).templateContents; } /** * Set the name of the template used to draw custom layout. * * With GWT-adapter, the template with name 'templatename' is loaded from * VAADIN/themes/themename/layouts/templatename.html. If the theme has not * been set (with Application.setTheme()), themename is 'default'. * * @param templateName */ public void setTemplateName(String templateName) { getState().templateName = templateName; getState().templateContents = null; } /** * Set the contents of the template used to draw the custom layout. * * Note: setTemplateContents can be applied only before CustomLayout * instance has been attached. * * @param templateContents */ public void setTemplateContents(String templateContents) { getState().templateContents = templateContents; getState().templateName = null; } @Override public void changeVariables(Object source, Map variables) { // Nothing to see here } @Override public void paintContent(PaintTarget target) throws PaintException { // Workaround to make the CommunicationManager read the template file // and send it to the client String templateName = getState(false).templateName; if (templateName != null && !templateName.isEmpty()) { Set usedResources = ((JsonPaintTarget) target) .getUsedResources(); String resourceName = "layouts/" + templateName + ".html"; usedResources.add(resourceName); } } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); for (Element child : design.children()) { Component childComponent = designContext.readDesign(child); if (child.hasAttr(":location")) { addComponent(childComponent, child.attr(":location")); } else { addComponent(childComponent); } } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); for (Entry slot : slots.entrySet()) { Element child = designContext.createElement(slot.getValue()); if (slots.size() > 1 || !"".equals(slot.getKey())) { child.attr(":location", slot.getKey()); } design.appendChild(child); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 6044 Content-Disposition: inline; filename="DateField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "f2fa6b526a2d23b9e06c6f28dbc7de72936a099f" /* * Copyright 2000-2022 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.time.LocalDate; import com.vaadin.shared.ui.datefield.LocalDateFieldState; /** * A date entry component, which displays the actual date selector as a popup. * * @see AbstractLocalDateField * @see InlineDateField * @author Vaadin Ltd. * @since 8.0 */ public class DateField extends AbstractLocalDateField { /** * Constructs an empty DateField with no caption. */ public DateField() { super(); } /** * Constructs a new DateField with the given caption and * initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDate value. */ public DateField(String caption, LocalDate value) { super(caption, value); } /** * Constructs a new DateField with initial date value. * * @param value * the LocalDate value. */ public DateField(LocalDate value) { super(); setValue(value); } /** * Constructs an empty DateField with caption. * * @param caption * the caption of the datefield. */ public DateField(String caption) { super(caption); } /** * Constructs a new {@code DateField} with a value change listener. *

* The listener is called when the value of this {@code DateField} is * changed either by the user or programmatically. * * @param valueChangeListener * the value change listener, not {@code null} */ public DateField(ValueChangeListener valueChangeListener) { super(); addValueChangeListener(valueChangeListener); } /** * Constructs a new {@code DateField} with the given caption and a value * change listener. *

* The listener is called when the value of this {@code DateField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param valueChangeListener * the value change listener, not {@code null} */ public DateField(String caption, ValueChangeListener valueChangeListener) { this(valueChangeListener); setCaption(caption); } /** * Constructs a new {@code DateField} with the given caption, initial text * contents and a value change listener. *

* The listener is called when the value of this {@code DateField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param value * the value for the field, not {@code null} * @param valueChangeListener * the value change listener, not {@code null} */ public DateField(String caption, LocalDate value, ValueChangeListener valueChangeListener) { this(caption, value); addValueChangeListener(valueChangeListener); } /** * Returns the current placeholder text. * * @see #setPlaceholder(String) * @return the placeholder text */ public String getPlaceholder() { return getState(false).placeholder; } /** * Sets the placeholder text. The placeholder is text that is displayed when * the field would otherwise be empty, to prompt the user for input. * * @param placeholder * the placeholder text to set */ public void setPlaceholder(String placeholder) { getState().placeholder = placeholder; } @Override protected LocalDateFieldState getState() { return (LocalDateFieldState) super.getState(); } @Override protected LocalDateFieldState getState(boolean markAsDirty) { return (LocalDateFieldState) super.getState(markAsDirty); } /** * Checks whether the text field is enabled (default) or not. * * @see #setTextFieldEnabled(boolean) * * @return true if the text field is enabled, false otherwise. */ public boolean isTextFieldEnabled() { return getState(false).textFieldEnabled; } /** * Enables or disables the text field. By default the text field is enabled. * Disabling it causes only the button for date selection to be active, thus * preventing the user from entering invalid dates. * * See issue 6790. * * @param state * true to enable text field, false to disable it. */ public void setTextFieldEnabled(boolean state) { getState().textFieldEnabled = state; } /** * Set a description that explains the usage of the Widget for users of * assistive devices. * * @param description * String with the description */ public void setAssistiveText(String description) { getState().descriptionForAssistiveDevices = description; } /** * Get the description that explains the usage of the Widget for users of * assistive devices. * * @return String with the description */ public String getAssistiveText() { return getState(false).descriptionForAssistiveDevices; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5946 Content-Disposition: inline; filename="DateTimeField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "c0717a8a01def8500c0ea0dce50b0f3f4f644636" /* * Copyright 2000-2022 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.time.LocalDateTime; import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; /** * A date time entry component, which displays the actual date selector as a * popup. * * @see AbstractLocalDateTimeField * @see InlineDateTimeField * @author Vaadin Ltd. * @since 8.0 */ public class DateTimeField extends AbstractLocalDateTimeField { /** * Constructs an empty DateTimeField with no caption. */ public DateTimeField() { super(); } /** * Constructs a new DateTimeField with the given caption and * initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDateTime value. */ public DateTimeField(String caption, LocalDateTime value) { super(caption, value); } /** * Constructs an empty DateTimeField with caption. * * @param caption * the caption of the datefield. */ public DateTimeField(String caption) { super(caption); } /** * Constructs a new {@code DateTimeField} with a value change listener. *

* The listener is called when the value of this {@code DateTimeField} is * changed either by the user or programmatically. * * @param valueChangeListener * the value change listener, not {@code null} */ public DateTimeField( ValueChangeListener valueChangeListener) { super(); addValueChangeListener(valueChangeListener); } /** * Constructs a new {@code DateTimeField} with the given caption and a value * change listener. *

* The listener is called when the value of this {@code DateTimeField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param valueChangeListener * the value change listener, not {@code null} */ public DateTimeField(String caption, ValueChangeListener valueChangeListener) { this(valueChangeListener); setCaption(caption); } /** * Constructs a new {@code DateTimeField} with the given caption, initial * text contents and a value change listener. *

* The listener is called when the value of this {@code DateTimeField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param value * the value for the field, not {@code null} * @param valueChangeListener * the value change listener, not {@code null} */ public DateTimeField(String caption, LocalDateTime value, ValueChangeListener valueChangeListener) { this(caption, value); addValueChangeListener(valueChangeListener); } /** * Returns the current placeholder text. * * @see #setPlaceholder(String) * @return the placeholder text */ public String getPlaceholder() { return getState(false).placeholder; } /** * Sets the placeholder text. The placeholder is text that is displayed when * the field would otherwise be empty, to prompt the user for input. * * @param placeholder * the placeholder text to set */ public void setPlaceholder(String placeholder) { getState().placeholder = placeholder; } @Override protected LocalDateTimeFieldState getState() { return (LocalDateTimeFieldState) super.getState(); } @Override protected LocalDateTimeFieldState getState(boolean markAsDirty) { return (LocalDateTimeFieldState) super.getState(markAsDirty); } /** * Checks whether the text field is enabled (default) or not. * * @see #setTextFieldEnabled(boolean) * * @return true if the text field is enabled, false otherwise. */ public boolean isTextFieldEnabled() { return getState(false).textFieldEnabled; } /** * Enables or disables the text field. By default the text field is enabled. * Disabling it causes only the button for date selection to be active, thus * preventing the user from entering invalid dates. * * See issue 6790. * * @param state * true to enable text field, false to disable it. */ public void setTextFieldEnabled(boolean state) { getState().textFieldEnabled = state; } /** * Set a description that explains the usage of the Widget for users of * assistive devices. * * @param description * String with the description */ public void setAssistiveText(String description) { getState().descriptionForAssistiveDevices = description; } /** * Get the description that explains the usage of the Widget for users of * assistive devices. * * @return String with the description */ public String getAssistiveText() { return getState(false).descriptionForAssistiveDevices; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1601 Content-Disposition: inline; filename="DeclarativeCaptionGenerator.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "f6321748e47babcf10b69ae246d156cf386afccf" /* * Copyright 2000-2022 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.util.HashMap; import java.util.Map; /** * Item caption generator class for declarative support. *

* Provides a straightforward mapping between an item and its caption. * * @param * item type */ class DeclarativeCaptionGenerator implements ItemCaptionGenerator { private ItemCaptionGenerator fallback; private Map captions = new HashMap<>(); public DeclarativeCaptionGenerator(ItemCaptionGenerator fallback) { this.fallback = fallback; } @Override public String apply(T item) { return captions.containsKey(item) ? captions.get(item) : fallback.apply(item); } /** * Sets a {@code caption} for the {@code item}. * * @param item * a data item * @param caption * a caption for the {@code item} */ protected void setCaption(T item, String caption) { captions.put(item, caption); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1589 Content-Disposition: inline; filename="DeclarativeIconGenerator.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "2860c7698ad9f62bf23653954faa9368c3ec4d9b" /* * Copyright 2000-2022 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.util.HashMap; import java.util.Map; import com.vaadin.server.Resource; /** * Icon generator class for declarative support. *

* Provides a straightforward mapping between an item and its icon. * * @param * item type */ class DeclarativeIconGenerator implements IconGenerator { private IconGenerator fallback; private Map captions = new HashMap<>(); public DeclarativeIconGenerator(IconGenerator fallback) { this.fallback = fallback; } @Override public Resource apply(T item) { return captions.containsKey(item) ? captions.get(item) : fallback.apply(item); } /** * Sets an {@code icon} for the {@code item}. * * @param item * a data item * @param icon * an icon for the {@code item} */ protected void setIcon(T item, Resource icon) { captions.put(item, icon); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1332 Content-Disposition: inline; filename="DeclarativeItemEnabledProvider.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "8f487e9b304523e48f2293a6d3f9c1918d7e687b" /* * Copyright 2000-2022 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.util.HashSet; import java.util.Set; import com.vaadin.server.SerializablePredicate; /** * Item enabled provider class for declarative support. *

* Provides a straightforward mapping between an item and its enable state. * * @param * item type */ class DeclarativeItemEnabledProvider implements SerializablePredicate { private Set disabled = new HashSet<>(); @Override public boolean test(T item) { return !disabled.contains(item); } /** * Adds the {@code item} to disabled items list. * * @param item * a data item */ protected void addDisabled(T item) { disabled.add(item); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1381 Content-Disposition: inline; filename="DeclarativeValueProvider.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "994c0e42e69224814ff12bbcdb496de3a9b8bffc" /* * Copyright 2000-2022 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.util.IdentityHashMap; import java.util.Map; import com.vaadin.data.ValueProvider; /** * Value provider class for declarative support. *

* Provides a straightforward mapping between an item and its value. * * @param * item type */ class DeclarativeValueProvider implements ValueProvider { private final Map values = new IdentityHashMap<>(); @Override public String apply(T t) { return values.get(t); } /** * Sets a {@code value} for the item {@code t}. * * @param t * a data item * @param value * a value for the item {@code t} */ void addValue(T t, String value) { values.put(t, value); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 7397 Content-Disposition: inline; filename="Dependency.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "d8643fb751fb5f06f8a00e87da31ad7d9f498184" /* * Copyright 2000-2022 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.annotation.Annotation; import java.util.ArrayList; import java.util.List; import com.vaadin.annotations.HtmlImport; import com.vaadin.annotations.JavaScript; import com.vaadin.annotations.StyleSheet; import com.vaadin.server.ClientConnector; import com.vaadin.server.DependencyFilter; import com.vaadin.server.DependencyFilter.FilterContext; import com.vaadin.server.LegacyCommunicationManager; import com.vaadin.server.VaadinService; /** * Represents a stylesheet or JavaScript to include on the page. * * @author Vaadin Ltd * @since 8.0 */ public class Dependency implements Serializable { /** * The type of dependency. */ public enum Type { STYLESHEET(StyleSheet.class), // JAVASCRIPT(JavaScript.class), // HTMLIMPORT(HtmlImport.class); private Class annotationType; private Type(Class annotationType) { this.annotationType = annotationType; } } private final Type type; private final String url; /** * Creates a new dependency of the given type, to be loaded from the given * URL. *

* The URL is passed through the translation mechanism before loading, so * custom protocols such as "vaadin://" can be used. * * @param type * the type of dependency, not null * @param url * the URL to load the dependency from, not null */ public Dependency(Type type, String url) { if (url == null) { throw new IllegalArgumentException("url cannot be null"); } assert type != null; this.type = type; this.url = url; } /** * Gets the untranslated URL for the dependency. * * @return the URL for the dependency */ public String getUrl() { return url; } /** * Gets the type of the dependency. * * @return the type of the dependency */ public Type getType() { return type; } /** * Finds all the URLs defined for the given class using annotations for the * given type, registers the URLs to the communication manager and adds the * registered dependencies to the given list. * * @param type * the type of dependencies to look for * @param cls * the class to scan * @param manager * a reference to the communication manager which tracks * dependencies * @param dependencies * the list to add registered dependencies to * * @return a stream of resource URLs in the order defined by the annotations */ @SuppressWarnings("deprecation") private static void findAndRegisterResources(Type type, Class cls, LegacyCommunicationManager manager, List dependencies) { Annotation[] annotations = cls .getAnnotationsByType(type.annotationType); if (annotations != null) { for (Annotation annotation : annotations) { String[] resources; if (annotation instanceof StyleSheet) { resources = ((StyleSheet) annotation).value(); } else if (annotation instanceof JavaScript) { resources = ((JavaScript) annotation).value(); } else if (annotation instanceof HtmlImport) { resources = ((HtmlImport) annotation).value(); } else { throw new IllegalArgumentException( "Unknown annotation type: " + annotation.annotationType().getName()); } for (String resource : resources) { String url = manager.registerDependency(resource, cls); dependencies.add(new Dependency(type, url)); } } } } /** * Finds all the URLs defined for the given classes, registers the URLs to * the communication manager and returns the registered dependencies. *

* The returned collection contains all types of dependencies for each class * in the given list in the order the classes are in the list, i.e. all * dependencies for the first class before all dependencies for the next * class. *

* JavaScript dependencies are returned before HTML imports. * * @param connectorTypes * the collection of connector classes to scan * @param manager * a reference to the communication manager which tracks * dependencies * @return the list of found dependencies */ @SuppressWarnings("deprecation") private static List findDependencies( List> connectorTypes, LegacyCommunicationManager manager) { List dependencies = new ArrayList<>(); for (Class connectorType : connectorTypes) { findAndRegisterResources(Type.JAVASCRIPT, connectorType, manager, dependencies); findAndRegisterResources(Type.HTMLIMPORT, connectorType, manager, dependencies); findAndRegisterResources(Type.STYLESHEET, connectorType, manager, dependencies); } return dependencies; } /** * Finds all the URLs defined for the given classes, registers the URLs to * the communication manager, passes the registered dependencies through any * defined filters and returns the filtered collection of dependencies to * load. * * @since 8.1 * @param connectorTypes * the collection of connector classes to scan * @param manager * a reference to the communication manager which tracks * dependencies * @param context * the context information for the filtering operation * @return the list of found and filtered dependencies */ public static List findDependencies( List> connectorTypes, LegacyCommunicationManager manager, FilterContext context) { List dependencies = findDependencies(connectorTypes, manager); VaadinService service = context.getService(); for (DependencyFilter filter : service.getDependencyFilters()) { dependencies = filter.filter(dependencies, context); } return dependencies; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 944 Content-Disposition: inline; filename="DescriptionGenerator.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3fa3a159f1ddc1521156f0423feee200adf71592" /* * Copyright 2000-2022 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 com.vaadin.server.SerializableFunction; /** * A callback interface for generating description texts for an item. * * @author Vaadin Ltd * @since 8.2 * * @param * the item type */ @FunctionalInterface public interface DescriptionGenerator extends SerializableFunction { } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17658 Content-Disposition: inline; filename="DragAndDropWrapper.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "9726fabdf0fa63e9efa923c4d6ec6b3c061c8ae9" /* * Copyright 2000-2022 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.OutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jsoup.nodes.Element; import com.vaadin.event.Transferable; import com.vaadin.event.TransferableImpl; import com.vaadin.event.dd.DragSource; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.DropTarget; import com.vaadin.event.dd.TargetDetails; import com.vaadin.event.dd.TargetDetailsImpl; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.StreamVariable; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.dd.HorizontalDropLocation; import com.vaadin.shared.ui.dd.VerticalDropLocation; import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperConstants; import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperServerRpc; import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.dnd.DragSourceExtension; import com.vaadin.ui.dnd.DropTargetExtension; /** * @author Vaadin Ltd * @deprecated Replaced in 8.1 with {@link DragSourceExtension} and * {@link DropTargetExtension}. */ @SuppressWarnings("serial") @Deprecated public class DragAndDropWrapper extends CustomComponent implements DropTarget, DragSource, LegacyComponent { /** * @deprecated Since 8.1, will be replaced by FileDropTargetExtension and * FileDropEvent, * https://github.com/vaadin/framework/issues/8891 */ @Deprecated public class WrapperTransferable extends TransferableImpl { private Html5File[] files; public WrapperTransferable(Component sourceComponent, Map rawVariables) { super(sourceComponent, rawVariables); Integer fc = (Integer) rawVariables.get("filecount"); if (fc != null) { files = new Html5File[fc]; for (int i = 0; i < fc; i++) { Html5File file = new Html5File( (String) rawVariables.get("fn" + i), // name ((Double) rawVariables.get("fs" + i)).longValue(), // size (String) rawVariables.get("ft" + i)); // mime String id = (String) rawVariables.get("fi" + i); files[i] = file; receivers.put(id, new ProxyReceiver(id, file)); markAsDirty(); // paint Receivers } } } /** * The component in wrapper that is being dragged or null if the * transferable is not a component (most likely an html5 drag). * * @return */ public Component getDraggedComponent() { Component object = (Component) getData("component"); return object; } /** * @return the mouse down event that started the drag and drop operation */ public MouseEventDetails getMouseDownEvent() { return MouseEventDetails.deSerialize((String) getData("mouseDown")); } public Html5File[] getFiles() { return files; } public String getText() { String data = (String) getData("Text"); // IE, html5 if (data == null) { // check for "text/plain" (webkit) data = (String) getData("text/plain"); } return data; } public String getHtml() { String data = (String) getData("Html"); // IE, html5 if (data == null) { // check for "text/plain" (webkit) data = (String) getData("text/html"); } return data; } } private final DragAndDropWrapperServerRpc rpc = () -> { // #19616 RPC to poll the server for changes }; private Map receivers = new HashMap<>(); public class WrapperTargetDetails extends TargetDetailsImpl { public WrapperTargetDetails(Map rawDropData) { super(rawDropData, DragAndDropWrapper.this); } /** * @return the absolute position of wrapper on the page */ public Integer getAbsoluteLeft() { return (Integer) getData("absoluteLeft"); } /** * * @return the absolute position of wrapper on the page */ public Integer getAbsoluteTop() { return (Integer) getData("absoluteTop"); } /** * @return a detail about the drags vertical position over the wrapper. */ public VerticalDropLocation getVerticalDropLocation() { return VerticalDropLocation .valueOf((String) getData("verticalLocation")); } /** * @return a detail about the drags horizontal position over the * wrapper. */ public HorizontalDropLocation getHorizontalDropLocation() { return HorizontalDropLocation .valueOf((String) getData("horizontalLocation")); } } public enum DragStartMode { /** * {@link DragAndDropWrapper} does not start drag events at all. */ NONE, /** * The component on which the drag started will be shown as drag image. */ COMPONENT, /** * The whole wrapper is used as a drag image when dragging. */ WRAPPER, /** * The whole wrapper is used to start an HTML5 drag. * * NOTE: In Internet Explorer 6 to 8, this prevents user interactions * with the wrapper's contents. For example, clicking a button inside * the wrapper will no longer work. */ HTML5, /** * Uses the component defined in * {@link #setDragImageComponent(Component)} as the drag image. */ COMPONENT_OTHER, } private final Map html5DataFlavors = new LinkedHashMap<>(); private DragStartMode dragStartMode = DragStartMode.NONE; private Component dragImageComponent = null; private Set sentIds = new HashSet<>(); /** * This is an internal constructor. Use * {@link DragAndDropWrapper#DragAndDropWrapper(Component)} instead. * * @since 7.5.0 */ @Deprecated public DragAndDropWrapper() { super(); registerRpc(rpc); } /** * Wraps given component in a {@link DragAndDropWrapper}. * * @param root * the component to be wrapped */ public DragAndDropWrapper(Component root) { this(); setCompositionRoot(root); } /** * Sets data flavors available in the DragAndDropWrapper is used to start an * HTML5 style drags. Most commonly the "Text" flavor should be set. * Multiple data types can be set. * * @param type * the string identifier of the drag "payload". E.g. "Text" or * "text/html" * @param value * the value */ public void setHTML5DataFlavor(String type, Object value) { html5DataFlavors.put(type, value); markAsDirty(); } @Override public void changeVariables(Object source, Map variables) { // TODO Remove once LegacyComponent is no longer implemented } @Override public void paintContent(PaintTarget target) throws PaintException { target.addAttribute(DragAndDropWrapperConstants.DRAG_START_MODE, dragStartMode.ordinal()); if (dragStartMode.equals(DragStartMode.COMPONENT_OTHER)) { if (dragImageComponent != null) { target.addAttribute( DragAndDropWrapperConstants.DRAG_START_COMPONENT_ATTRIBUTE, dragImageComponent.getConnectorId()); } else { throw new IllegalArgumentException( "DragStartMode.COMPONENT_OTHER set but no component " + "was defined. Please set a component using DragAnd" + "DropWrapper.setDragStartComponent(Component)."); } } if (getDropHandler() != null) { getDropHandler().getAcceptCriterion().paint(target); } if (receivers != null && !receivers.isEmpty()) { for (Iterator> it = receivers .entrySet().iterator(); it.hasNext();) { Entry entry = it.next(); String id = entry.getKey(); ProxyReceiver proxyReceiver = entry.getValue(); Html5File html5File = proxyReceiver.file; if (html5File.getStreamVariable() != null) { if (!sentIds.contains(id)) { target.addVariable(this, "rec-" + id, new ProxyReceiver(id, html5File)); /* * if a new batch is requested to be uploaded before the * last one is done, any remaining ids will be replayed. * We want to avoid a new ProxyReceiver to be made since * it'll get a new URL, so we need to keep extra track * on what has been sent. * * See #12330. */ sentIds.add(id); // these are cleaned from receivers once the upload has // started } } else { // instructs the client side not to send the file target.addVariable(this, "rec-" + id, (String) null); // forget the file from subsequent paints it.remove(); } } } target.addAttribute(DragAndDropWrapperConstants.HTML5_DATA_FLAVORS, html5DataFlavors); } private DropHandler dropHandler; @Override public DropHandler getDropHandler() { return dropHandler; } public void setDropHandler(DropHandler dropHandler) { this.dropHandler = dropHandler; markAsDirty(); } @Override public TargetDetails translateDropTargetDetails( Map clientVariables) { return new WrapperTargetDetails(clientVariables); } @Override public Transferable getTransferable( final Map rawVariables) { return new WrapperTransferable(this, rawVariables); } public void setDragStartMode(DragStartMode dragStartMode) { this.dragStartMode = dragStartMode; markAsDirty(); } public DragStartMode getDragStartMode() { return dragStartMode; } /** * Sets the component that will be used as the drag image. Only used when * wrapper is set to {@link DragStartMode#COMPONENT_OTHER} * * @param dragImageComponent */ public void setDragImageComponent(Component dragImageComponent) { this.dragImageComponent = dragImageComponent; markAsDirty(); } /** * Gets the component that will be used as the drag image. Only used when * wrapper is set to {@link DragStartMode#COMPONENT_OTHER} * * @return null if no component is set. */ public Component getDragImageComponent() { return dragImageComponent; } final class ProxyReceiver implements StreamVariable { private final String id; private Html5File file; public ProxyReceiver(String id, Html5File file) { this.id = id; this.file = file; } private boolean listenProgressOfUploadedFile; @Override public OutputStream getOutputStream() { if (file.getStreamVariable() == null) { return null; } return file.getStreamVariable().getOutputStream(); } @Override public boolean listenProgress() { return file.getStreamVariable().listenProgress(); } @Override public void onProgress(StreamingProgressEvent event) { file.getStreamVariable() .onProgress(new ReceivingEventWrapper(event)); } @Override public void streamingStarted(StreamingStartEvent event) { listenProgressOfUploadedFile = file.getStreamVariable() != null; if (listenProgressOfUploadedFile) { file.getStreamVariable() .streamingStarted(new ReceivingEventWrapper(event)); } // no need tell to the client about this receiver on next paint receivers.remove(id); sentIds.remove(id); // let the terminal GC the streamvariable and not to accept other // file uploads to this variable event.disposeStreamVariable(); } @Override public void streamingFinished(StreamingEndEvent event) { if (listenProgressOfUploadedFile) { file.getStreamVariable() .streamingFinished(new ReceivingEventWrapper(event)); } } @Override public void streamingFailed(final StreamingErrorEvent event) { if (listenProgressOfUploadedFile) { file.getStreamVariable() .streamingFailed(new ReceivingEventWrapper(event)); } } @Override public boolean isInterrupted() { return file.getStreamVariable().isInterrupted(); } /* * With XHR2 file posts we can't provide as much information from the * terminal as with multipart request. This helper class wraps the * terminal event and provides the lacking information from the * Html5File. */ class ReceivingEventWrapper implements StreamingErrorEvent, StreamingEndEvent, StreamingStartEvent, StreamingProgressEvent { private final StreamingEvent wrappedEvent; ReceivingEventWrapper(StreamingEvent e) { wrappedEvent = e; } @Override public String getMimeType() { return file.getType(); } @Override public String getFileName() { return file.getFileName(); } @Override public long getContentLength() { return file.getFileSize(); } @Override public Exception getException() { if (wrappedEvent instanceof StreamingErrorEvent) { return ((StreamingErrorEvent) wrappedEvent).getException(); } return null; } @Override public long getBytesReceived() { return wrappedEvent.getBytesReceived(); } /** * Calling this method has no effect. DD files are receive only once * anyway. */ @Override public void disposeStreamVariable() { } } } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); for (Element child : design.children()) { Component component = designContext.readDesign(child); if (getDragStartMode() == DragStartMode.COMPONENT_OTHER && child.hasAttr(":drag-image")) { setDragImageComponent(component); } else if (getCompositionRoot() == null) { setCompositionRoot(component); } } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); design.appendChild(designContext.createElement(getCompositionRoot())); if (getDragStartMode() == DragStartMode.COMPONENT_OTHER) { Element child = designContext .createElement(getDragImageComponent()); child.attr(":drag-image", true); design.appendChild(child); } } @Override protected DragAndDropWrapperState getState() { return (DragAndDropWrapperState) super.getState(); } @Override protected DragAndDropWrapperState getState(boolean markAsDirty) { return (DragAndDropWrapperState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 14616 Content-Disposition: inline; filename="Embedded.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "ece8586f95aa977708cb6eead353dc23d79bc892" /* * Copyright 2000-2022 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.util.Iterator; import com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.event.MouseEvents.ClickListener; import com.vaadin.server.Resource; import com.vaadin.shared.EventId; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.embedded.EmbeddedServerRpc; import com.vaadin.shared.ui.embedded.EmbeddedState; /** * A component for embedding external objects. *

* The {@code Embedded} component is used to display various types of multimedia * content using the HTML {@code } element. This includes PDF documents, * Java applets, and QuickTime videos. Installing a browser plug-in is usually * required to actually view the embedded content. *

* Note that before Vaadin 7, {@code Embedded} was also used to display images, * Adobe Flash objects, and embedded web pages. This use of the component is * deprecated in Vaadin 7; the {@link Image}, {@link Flash}, and * {@link BrowserFrame} components should be used instead, respectively. * * @see Video * @see Audio * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class Embedded extends AbstractComponent { /** * General object type. */ public static final int TYPE_OBJECT = 0; /** * Image types. * * @deprecated As of 7.0, use the {@link Image} component instead. */ @Deprecated public static final int TYPE_IMAGE = 1; /** * Browser ("iframe") type. * * @deprecated As of 7.0, use the {@link BrowserFrame} component instead. */ @Deprecated public static final int TYPE_BROWSER = 2; private EmbeddedServerRpc rpc = mouseDetails -> fireEvent( new ClickEvent(Embedded.this, mouseDetails)); /** * Creates a new empty Embedded object. */ public Embedded() { registerRpc(rpc); } /** * Creates a new empty Embedded object with caption. * * @param caption */ public Embedded(String caption) { this(); setCaption(caption); } /** * Creates a new Embedded object whose contents is loaded from given * resource. The dimensions are assumed if possible. The type is guessed * from resource. * * @param caption * @param source * the Source of the embedded object. */ public Embedded(String caption, Resource source) { this(caption); setSource(source); } /** * Sets this component's "alt-text", that is, an alternate text that can be * presented instead of this component's normal content, for accessibility * purposes. Does not work when {@link #setType(int)} has been called with * {@link #TYPE_BROWSER}. * * @param altText * A short, human-readable description of this component's * content. * @since 6.8 */ public void setAlternateText(String altText) { String oldAltText = getAlternateText(); if (altText != oldAltText || (altText != null && !altText.equals(oldAltText))) { getState().altText = altText; } } /** * Gets this component's "alt-text". * * @see #setAlternateText(String) */ public String getAlternateText() { return getState(false).altText; } /** * Sets an object parameter. Parameters are optional information, and they * are passed to the instantiated object. Parameters are are stored as name * value pairs. This overrides the previous value assigned to this * parameter. * * @param name * the name of the parameter. * @param value * the value of the parameter. */ public void setParameter(String name, String value) { getState().parameters.put(name, value); } /** * Gets the value of an object parameter. Parameters are optional * information, and they are passed to the instantiated object. Parameters * are are stored as name value pairs. * * @return the Value of parameter or null if not found. */ public String getParameter(String name) { return getState(false).parameters.get(name); } /** * Removes an object parameter from the list. * * @param name * the name of the parameter to remove. */ public void removeParameter(String name) { getState().parameters.remove(name); } /** * Gets the embedded object parameter names. * * @return the Iterator of parameters names. */ public Iterator getParameterNames() { return getState(false).parameters.keySet().iterator(); } /** * This attribute specifies the base path used to resolve relative URIs * specified by the classid, data, and archive attributes. When absent, its * default value is the base URI of the current document. * * @return the code base. */ public String getCodebase() { return getState(false).codebase; } /** * Gets the MIME-Type of the code. * * @return the MIME-Type of the code. */ public String getCodetype() { return getState(false).codetype; } /** * Gets the MIME-Type of the object. * * @return the MIME-Type of the object. */ public String getMimeType() { return getState(false).mimeType; } /** * This attribute specifies a message that a user agent may render while * loading the object's implementation and data. * * @return The text displayed when loading */ public String getStandby() { return getState(false).standby; } /** * This attribute specifies the base path used to resolve relative URIs * specified by the classid, data, and archive attributes. When absent, its * default value is the base URI of the current document. * * @param codebase * The base path */ public void setCodebase(String codebase) { String oldCodebase = getCodebase(); if (codebase != oldCodebase || (codebase != null && !codebase.equals(oldCodebase))) { getState().codebase = codebase; } } /** * This attribute specifies the content type of data expected when * downloading the object specified by classid. This attribute is optional * but recommended when classid is specified since it allows the user agent * to avoid loading information for unsupported content types. When absent, * it defaults to the value of the type attribute. * * @param codetype * the codetype to set. */ public void setCodetype(String codetype) { String oldCodetype = getCodetype(); if (codetype != oldCodetype || (codetype != null && !codetype.equals(oldCodetype))) { getState().codetype = codetype; } } /** * Sets the mimeType, the MIME-Type of the object. * * @param mimeType * the mimeType to set. */ public void setMimeType(String mimeType) { String oldMimeType = getMimeType(); if (mimeType != oldMimeType || (mimeType != null && !mimeType.equals(oldMimeType))) { getState().mimeType = mimeType; if ("application/x-shockwave-flash".equals(mimeType)) { /* * Automatically add wmode transparent as we use lots of * floating layers in Vaadin. If developers need better flash * performance, they can override this value programmatically * back to "window" (the default). */ if (getParameter("wmode") == null) { setParameter("wmode", "transparent"); } } } } /** * This attribute specifies a message that a user agent may render while * loading the object's implementation and data. * * @param standby * The text to display while loading */ public void setStandby(String standby) { String oldStandby = getStandby(); if (standby != oldStandby || (standby != null && !standby.equals(oldStandby))) { getState().standby = standby; } } /** * This attribute may be used to specify the location of an object's * implementation via a URI. * * @return the classid. */ public String getClassId() { return getState(false).classId; } /** * This attribute may be used to specify the location of an object's * implementation via a URI. * * @param classId * the classId to set. */ public void setClassId(String classId) { String oldClassId = getClassId(); if (classId != oldClassId || (classId != null && !classId.equals(oldClassId))) { getState().classId = classId; } } /** * Gets the resource contained in the embedded object. * * @return the Resource */ public Resource getSource() { return getResource("src"); } /** * Gets the type of the embedded object. *

* This can be one of the following: *

    *
  • TYPE_OBJECT (This is the default) *
  • TYPE_IMAGE *
*

* * @return the type. */ public int getType() { return getState(false).type; } /** * Sets the object source resource. The dimensions are assumed if possible. * The type is guessed from resource. * * @param source * the source to set. */ public void setSource(Resource source) { if (source != null && !source.equals(getSource())) { setResource("src", source); final String mt = source.getMIMEType(); if (getMimeType() == null) { getState().mimeType = mt; } if (mt.equals("image/svg+xml")) { getState().type = TYPE_OBJECT; } else if ((mt.substring(0, mt.indexOf('/')) .equalsIgnoreCase("image"))) { getState().type = TYPE_IMAGE; } else { // Keep previous type } } } /** * Sets the object type. *

* This can be one of the following: *

    *
  • {@link #TYPE_OBJECT} (This is the default) *
  • {@link #TYPE_IMAGE} (Deprecated) *
  • {@link #TYPE_BROWSER} (Deprecated) *
*

* * @param type * the type to set. */ public void setType(int type) { if (type != TYPE_OBJECT && type != TYPE_IMAGE && type != TYPE_BROWSER) { throw new IllegalArgumentException("Unsupported type"); } if (type != getType()) { getState().type = type; } } /** * This attribute may be used to specify a space-separated list of URIs for * archives containing resources relevant to the object, which may include * the resources specified by the classid and data attributes. Preloading * archives will generally result in reduced load times for objects. * Archives specified as relative URIs should be interpreted relative to the * codebase attribute. * * @return Space-separated list of URIs with resources relevant to the * object */ public String getArchive() { return getState(false).archive; } /** * This attribute may be used to specify a space-separated list of URIs for * archives containing resources relevant to the object, which may include * the resources specified by the classid and data attributes. Preloading * archives will generally result in reduced load times for objects. * Archives specified as relative URIs should be interpreted relative to the * codebase attribute. * * @param archive * Space-separated list of URIs with resources relevant to the * object */ public void setArchive(String archive) { String oldArchive = getArchive(); if (archive != oldArchive || (archive != null && !archive.equals(oldArchive))) { getState().archive = archive; } } /** * Add a click listener to the component. The listener is called whenever * the user clicks inside the component. Depending on the content the event * may be blocked and in that case no event is fired. * * @see Registration * * @param listener * The listener to add * @return a registration object for removing the listener * @since 8.0 */ public Registration addClickListener(ClickListener listener) { return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener, ClickListener.clickMethod); } /** * Remove a click listener from the component. The listener should earlier * have been added using {@link #addClickListener(ClickListener)}. * * @param listener * The listener to remove * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #addClickListener(ClickListener)}. */ @Deprecated public void removeClickListener(ClickListener listener) { removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener); } @Override protected EmbeddedState getState() { return (EmbeddedState) super.getState(); } @Override protected EmbeddedState getState(boolean markAsDirty) { return (EmbeddedState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 8290 Content-Disposition: inline; filename="Flash.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "086814fc67a759a4b6010b2fb685920be857dfa7" /* * Copyright 2000-2022 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.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jsoup.nodes.Element; import com.vaadin.server.Resource; import com.vaadin.shared.ui.flash.FlashState; import com.vaadin.ui.declarative.DesignContext; /** * A component for displaying Adobe® Flash® content. * * @author Vaadin Ltd. * @since 7.0 * @deprecated No modern browsers support Flash content anymore. */ @Deprecated public class Flash extends AbstractEmbedded { /** * Creates a new empty Flash component. */ public Flash() { } /** * Creates a new empty Flash component with the given caption. * * @param caption * The caption for the component */ public Flash(String caption) { setCaption(caption); } /** * Creates a new Flash component with the given caption and content. * * @param caption * The caption for the component * @param source * A Resource representing the Flash content that should be * displayed */ public Flash(String caption, Resource source) { this(caption); setSource(source); } @Override protected FlashState getState() { return (FlashState) super.getState(); } @Override protected FlashState getState(boolean markAsDirty) { return (FlashState) super.getState(markAsDirty); } /** * This attribute specifies the base path used to resolve relative URIs * specified by the classid, data, and archive attributes. When absent, its * default value is the base URI of the current document. * * @param codebase * The base path */ public void setCodebase(String codebase) { if (codebase != getState().codebase || (codebase != null && !codebase.equals(getState().codebase))) { getState().codebase = codebase; requestRepaint(); } } /** * Returns the codebase. * * @see #setCodebase(String) * @since 7.4.1 * @return Current codebase. */ public String getCodebase() { return getState(false).codebase; } /** * This attribute specifies the content type of data expected when * downloading the object specified by classid. This attribute is optional * but recommended when classid is specified since it allows the user agent * to avoid loading information for unsupported content types. When absent, * it defaults to the value of the type attribute. * * @param codetype * the codetype to set. */ public void setCodetype(String codetype) { if (codetype != getState().codetype || (codetype != null && !codetype.equals(getState().codetype))) { getState().codetype = codetype; requestRepaint(); } } /** * Returns the current codetype. * * @see #setCodetype(String) * @since 7.4.1 * @return Current codetype. */ public String getCodetype() { return getState(false).codetype; } /** * This attribute may be used to specify a space-separated list of URIs for * archives containing resources relevant to the object, which may include * the resources specified by the classid and data attributes. Preloading * archives will generally result in reduced load times for objects. * Archives specified as relative URIs should be interpreted relative to the * codebase attribute. * * @param archive * Space-separated list of URIs with resources relevant to the * object */ public void setArchive(String archive) { if (archive != getState().archive || (archive != null && !archive.equals(getState().archive))) { getState().archive = archive; requestRepaint(); } } /** * Returns current archive. * * @see #setArchive(String) * @since 7.4.1 * @return Current archive. */ public String getArchive() { return getState(false).archive; } /** * Sets standby. * * @param standby * Standby string. */ public void setStandby(String standby) { if (standby != getState().standby || (standby != null && !standby.equals(getState().standby))) { getState().standby = standby; requestRepaint(); } } /** * Returns standby. * * @since 7.4.1 * @return Standby string. */ public String getStandby() { return getState(false).standby; } /** * Sets an object parameter. Parameters are optional information, and they * are passed to the instantiated object. Parameters are are stored as name * value pairs. This overrides the previous value assigned to this * parameter. * * @param name * the name of the parameter. * @param value * the value of the parameter. */ public void setParameter(String name, String value) { if (getState().embedParams == null) { getState().embedParams = new HashMap<>(); } getState().embedParams.put(name, value); requestRepaint(); } /** * Gets the value of an object parameter. Parameters are optional * information, and they are passed to the instantiated object. Parameters * are are stored as name value pairs. * * @param name * name of the parameter * @return the Value of parameter or null if not found. */ public String getParameter(String name) { return getState(false).embedParams != null ? getState(false).embedParams.get(name) : null; } /** * Removes an object parameter from the list. * * @param name * the name of the parameter to remove. */ public void removeParameter(String name) { if (getState().embedParams == null) { return; } getState().embedParams.remove(name); requestRepaint(); } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); // Parameters, in alphabetic order List paramNames = new ArrayList<>(); for (String param : getParameterNames()) { paramNames.add(param); } Collections.sort(paramNames); for (String param : paramNames) { design.appendElement("parameter").attr("name", param).attr("value", getParameter(param)); } } /** * Returns an iterable with declared parameter names. * * @see #setParameter(String, String) * @see #getParameter(String) * @since 7.4.1 * @return An iterable with declared parameter names. */ public Iterable getParameterNames() { Map map = getState(false).embedParams; if (map == null) { return Collections.emptySet(); } else { return Collections.unmodifiableSet(map.keySet()); } } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); for (Element paramElement : design.getElementsByTag("parameter")) { setParameter(paramElement.attr("name"), paramElement.attr("value")); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2515 Content-Disposition: inline; filename="FormLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "c89ed8d6a87cdcb7851cb1534f06e73b02ef8d29" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.orderedlayout.FormLayoutState; /** * FormLayout is used to layout fields. * * FormLayout is a close relative of {@link VerticalLayout}, but in FormLayout * captions are rendered to the left of their respective components. Required * and validation indicators are shown between the captions and the fields. * * FormLayout by default has component spacing on. Also margin top and margin * bottom are by default on. * */ public class FormLayout extends AbstractOrderedLayout { public FormLayout() { super(); setSpacing(true); setMargin(new MarginInfo(true, false)); setWidth(100, UNITS_PERCENTAGE); } /** * Constructs a FormLayout and adds the given components to it. * * @see AbstractOrderedLayout#addComponents(Component...) * * @param children * Components to add to the FormLayout */ public FormLayout(Component... children) { this(); addComponents(children); } /** * @deprecated This method currently has no effect as expand ratios are not * implemented in FormLayout */ @Override @Deprecated public void setExpandRatio(Component component, float ratio) { super.setExpandRatio(component, ratio); } /** * @deprecated This method currently has no effect as expand ratios are not * implemented in FormLayout */ @Override @Deprecated public float getExpandRatio(Component component) { return super.getExpandRatio(component); } @Override protected FormLayoutState getState() { return (FormLayoutState) super.getState(); } @Override protected FormLayoutState getState(boolean markAsDirty) { return (FormLayoutState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 178637 Content-Disposition: inline; filename="Grid.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "268fdfa47ed573c6c4f6dc3a02f47e67c1e0655e" /* * Copyright 2000-2022 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.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import org.jsoup.Jsoup; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.vaadin.data.BeanPropertySet; import com.vaadin.data.Binder; import com.vaadin.data.Binder.Binding; import com.vaadin.data.HasDataProvider; import com.vaadin.data.HasValue; import com.vaadin.data.PropertyDefinition; import com.vaadin.data.PropertySet; import com.vaadin.data.ValueProvider; import com.vaadin.data.provider.CallbackDataProvider; import com.vaadin.data.provider.DataCommunicator; import com.vaadin.data.provider.DataGenerator; import com.vaadin.data.provider.DataProvider; import com.vaadin.data.provider.GridSortOrder; import com.vaadin.data.provider.GridSortOrderBuilder; import com.vaadin.data.provider.InMemoryDataProvider; import com.vaadin.data.provider.Query; import com.vaadin.data.provider.QuerySortOrder; import com.vaadin.event.ConnectorEvent; import com.vaadin.event.ContextClickEvent; import com.vaadin.event.HasUserOriginated; import com.vaadin.event.SortEvent; import com.vaadin.event.SortEvent.SortListener; import com.vaadin.event.SortEvent.SortNotifier; import com.vaadin.event.selection.MultiSelectionListener; import com.vaadin.event.selection.SelectionListener; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.server.AbstractExtension; import com.vaadin.server.EncodeResult; import com.vaadin.server.Extension; import com.vaadin.server.JsonCodec; import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializableSupplier; import com.vaadin.server.Setter; import com.vaadin.server.VaadinServiceClassLoaderUtil; import com.vaadin.shared.Connector; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.grid.AbstractGridExtensionState; import com.vaadin.shared.ui.grid.ColumnResizeMode; import com.vaadin.shared.ui.grid.ColumnState; import com.vaadin.shared.ui.grid.DetailsManagerState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.GridConstants.Section; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SectionState; import com.vaadin.ui.components.grid.ColumnReorderListener; import com.vaadin.ui.components.grid.ColumnResizeListener; import com.vaadin.ui.components.grid.ColumnVisibilityChangeListener; import com.vaadin.ui.components.grid.DetailsGenerator; import com.vaadin.ui.components.grid.Editor; import com.vaadin.ui.components.grid.EditorImpl; import com.vaadin.ui.components.grid.Footer; import com.vaadin.ui.components.grid.FooterRow; import com.vaadin.ui.components.grid.GridMultiSelect; import com.vaadin.ui.components.grid.GridSelectionModel; import com.vaadin.ui.components.grid.GridSingleSelect; import com.vaadin.ui.components.grid.Header; import com.vaadin.ui.components.grid.Header.Row; import com.vaadin.ui.components.grid.HeaderCell; import com.vaadin.ui.components.grid.HeaderRow; import com.vaadin.ui.components.grid.ItemClickListener; import com.vaadin.ui.components.grid.MultiSelectionModel; import com.vaadin.ui.components.grid.MultiSelectionModelImpl; import com.vaadin.ui.components.grid.NoSelectionModel; import com.vaadin.ui.components.grid.SingleSelectionModel; import com.vaadin.ui.components.grid.SingleSelectionModelImpl; import com.vaadin.ui.components.grid.SortOrderProvider; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; import com.vaadin.ui.declarative.DesignFormatter; import com.vaadin.ui.renderers.AbstractRenderer; import com.vaadin.ui.renderers.ComponentRenderer; import com.vaadin.ui.renderers.HtmlRenderer; import com.vaadin.ui.renderers.Renderer; import com.vaadin.ui.renderers.TextRenderer; import com.vaadin.util.ReflectTools; import elemental.json.Json; import elemental.json.JsonObject; import elemental.json.JsonValue; /** * A grid component for displaying tabular data. * * @author Vaadin Ltd * @since 8.0 * * @param * the grid bean type */ public class Grid extends AbstractListing implements HasComponents, HasDataProvider, SortNotifier> { private static final String DECLARATIVE_DATA_ITEM_TYPE = "data-item-type"; /** * A callback method for fetching items. The callback is provided with a * list of sort orders, offset index and limit. * * @param * the grid bean type */ @FunctionalInterface public interface FetchItemsCallback extends Serializable { /** * Returns a stream of items ordered by given sort orders, limiting the * results with given offset and limit. *

* This method is called after the size of the data set is asked from a * related size callback. The offset and limit are promised to be within * the size of the data set. * * @param sortOrder * a list of sort orders * @param offset * the first index to fetch * @param limit * the fetched item count * @return stream of items */ public Stream fetchItems(List sortOrder, int offset, int limit); } @Deprecated private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod( ColumnReorderListener.class, "columnReorder", ColumnReorderEvent.class); private static final Method SORT_ORDER_CHANGE_METHOD = ReflectTools .findMethod(SortListener.class, "sort", SortEvent.class); @Deprecated private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod( ColumnResizeListener.class, "columnResize", ColumnResizeEvent.class); @Deprecated private static final Method ITEM_CLICK_METHOD = ReflectTools .findMethod(ItemClickListener.class, "itemClick", ItemClick.class); @Deprecated private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools .findMethod(ColumnVisibilityChangeListener.class, "columnVisibilityChanged", ColumnVisibilityChangeEvent.class); /** * Selection mode representing the built-in selection models in grid. *

* These enums can be used in {@link Grid#setSelectionMode(SelectionMode)} * to easily switch between the build-in selection models. * * @see Grid#setSelectionMode(SelectionMode) * @see Grid#setSelectionModel(GridSelectionModel) */ public enum SelectionMode { /** * Single selection mode that maps to build-in * {@link SingleSelectionModel}. * * @see SingleSelectionModelImpl */ SINGLE { @Override protected GridSelectionModel createModel() { return new SingleSelectionModelImpl<>(); } }, /** * Multiselection mode that maps to build-in {@link MultiSelectionModel} * . * * @see MultiSelectionModelImpl */ MULTI { @Override protected GridSelectionModel createModel() { return new MultiSelectionModelImpl<>(); } }, /** * Selection model that doesn't allow selection. * * @see NoSelectionModel */ NONE { @Override protected GridSelectionModel createModel() { return new NoSelectionModel<>(); } }; /** * Creates the selection model to use with this enum. * * @param * the type of items in the grid * @return the selection model */ protected abstract GridSelectionModel createModel(); } /** * An event that is fired when the columns are reordered. */ public static class ColumnReorderEvent extends Component.Event implements HasUserOriginated { private final boolean userOriginated; /** * * @param source * the grid where the event originated from * @param userOriginated * true if event is a result of user * interaction, false if from API call */ public ColumnReorderEvent(Grid source, boolean userOriginated) { super(source); this.userOriginated = userOriginated; } /** * Returns true if the column reorder was done by the user, * false if not and it was triggered by server side code. * * @return true if event is a result of user interaction */ @Override public boolean isUserOriginated() { return userOriginated; } } /** * An event that is fired when a column is resized, either programmatically * or by the user. */ public static class ColumnResizeEvent extends Component.Event implements HasUserOriginated { private final Column column; private final boolean userOriginated; /** * * @param source * the grid where the event originated from * @param column * the column that was resized * @param userOriginated * true if event is a result of user * interaction, false if from API call */ public ColumnResizeEvent(Grid source, Column column, boolean userOriginated) { super(source); this.column = column; this.userOriginated = userOriginated; } /** * Returns the column that was resized. * * @return the resized column. */ public Column getColumn() { return column; } /** * Returns true if the column resize was done by the user, * false if not and it was triggered by server side code. * * @return true if event is a result of user interaction */ @Override public boolean isUserOriginated() { return userOriginated; } } /** * An event fired when an item in the Grid has been clicked. * * @param * the grid bean type */ public static class ItemClick extends ConnectorEvent { private final T item; private final Column column; private final MouseEventDetails mouseEventDetails; private final int rowIndex; /** * Creates a new {@code ItemClick} event containing the given item and * Column originating from the given Grid. * * @param source * the grid where the event originated from * @param column * the column that contains the clicked cell * @param item * the item that was clicked * @param mouseEventDetails * mouse event details about the click * @param rowIndex * the index of the row that contains the clicked cell */ public ItemClick(Grid source, Column column, T item, MouseEventDetails mouseEventDetails, int rowIndex) { super(source); this.column = column; this.item = item; this.mouseEventDetails = mouseEventDetails; this.rowIndex = rowIndex; } /** * Returns the clicked item. * * @return the clicked item */ public T getItem() { return item; } /** * Returns the clicked column. * * @return the clicked column */ public Column getColumn() { return column; } /** * Returns the source Grid. * * @return the grid */ @Override @SuppressWarnings("unchecked") public Grid getSource() { return (Grid) super.getSource(); } /** * Returns the mouse event details. * * @return the mouse event details */ public MouseEventDetails getMouseEventDetails() { return mouseEventDetails; } /** * Returns the clicked rowIndex. * * @return the clicked rowIndex * @since 8.4 */ public int getRowIndex() { return rowIndex; } } /** * ContextClickEvent for the Grid Component. * *

* Usage: * *

     * grid.addContextClickListener(event -> Notification.show(
     *         ((GridContextClickEvent<Person>) event).getItem() + " Clicked"));
     * 
* * @param * the grid bean type */ public static class GridContextClickEvent extends ContextClickEvent { private final T item; private final int rowIndex; private final Column column; private final Section section; /** * Creates a new context click event. * * @param source * the grid where the context click occurred * @param mouseEventDetails * details about mouse position * @param section * the section of the grid which was clicked * @param rowIndex * the index of the row which was clicked * @param item * the item which was clicked * @param column * the column which was clicked */ public GridContextClickEvent(Grid source, MouseEventDetails mouseEventDetails, Section section, int rowIndex, T item, Column column) { super(source, mouseEventDetails); this.item = item; this.section = section; this.column = column; this.rowIndex = rowIndex; } /** * Returns the item of context clicked row. * * @return item of clicked row; null if header or footer */ public T getItem() { return item; } /** * Returns the clicked column. * * @return the clicked column */ public Column getColumn() { return column; } /** * Return the clicked section of Grid. * * @return section of grid */ public Section getSection() { return section; } /** * Returns the clicked row index. *

* Header and Footer rows for index can be fetched with * {@link Grid#getHeaderRow(int)} and {@link Grid#getFooterRow(int)}. * * @return row index in section */ public int getRowIndex() { return rowIndex; } @Override @SuppressWarnings("unchecked") public Grid getComponent() { return (Grid) super.getComponent(); } } /** * An event that is fired when a column's visibility changes. * * @since 7.5.0 */ public static class ColumnVisibilityChangeEvent extends Component.Event implements HasUserOriginated { private final Column column; private final boolean userOriginated; private final boolean hidden; /** * Constructor for a column visibility change event. * * @param source * the grid from which this event originates * @param column * the column that changed its visibility * @param hidden * true if the column was hidden, * false if it became visible * @param isUserOriginated * true if the event was triggered by an UI * interaction */ public ColumnVisibilityChangeEvent(Grid source, Column column, boolean hidden, boolean isUserOriginated) { super(source); this.column = column; this.hidden = hidden; userOriginated = isUserOriginated; } /** * Gets the column that became hidden or visible. * * @return the column that became hidden or visible. * @see Column#isHidden() */ public Column getColumn() { return column; } /** * Was the column set hidden or visible. * * @return true if the column was hidden false * if it was set visible */ public boolean isHidden() { return hidden; } @Override public boolean isUserOriginated() { return userOriginated; } } /** * A helper base class for creating extensions for the Grid component. * * @param */ public abstract static class AbstractGridExtension extends AbstractListingExtension { @Override public void extend(AbstractListing grid) { if (!(grid instanceof Grid)) { throw new IllegalArgumentException( getClass().getSimpleName() + " can only extend Grid"); } super.extend(grid); } /** * Adds given component to the connector hierarchy of Grid. * * @param c * the component to add */ protected void addComponentToGrid(Component c) { getParent().addExtensionComponent(c); } /** * Removes given component from the connector hierarchy of Grid. * * @param c * the component to remove */ protected void removeComponentFromGrid(Component c) { getParent().removeExtensionComponent(c); } @Override public Grid getParent() { return (Grid) super.getParent(); } @Override protected AbstractGridExtensionState getState() { return (AbstractGridExtensionState) super.getState(); } @Override protected AbstractGridExtensionState getState(boolean markAsDirty) { return (AbstractGridExtensionState) super.getState(markAsDirty); } /** * Returns the internal id for given column. This id should not be * confused with the user-defined identifier. * * @param column * the column * @return internal id of given column */ protected String getInternalIdForColumn(Column column) { return getParent().getInternalIdForColumn(column); } } private final class GridServerRpcImpl implements GridServerRpc { @Override public void sort(String[] columnInternalIds, SortDirection[] directions, boolean isUserOriginated) { assert columnInternalIds.length == directions.length : "Column and sort direction counts don't match."; List> list = new ArrayList<>(directions.length); for (int i = 0; i < columnInternalIds.length; ++i) { Column column = columnKeys.get(columnInternalIds[i]); list.add(new GridSortOrder<>(column, directions[i])); } setSortOrder(list, isUserOriginated); } @Override public void itemClick(String rowKey, String columnInternalId, MouseEventDetails details, int rowIndex) { Column column = getColumnByInternalId(columnInternalId); T item = getDataCommunicator().getKeyMapper().get(rowKey); fireEvent(new ItemClick<>(Grid.this, column, item, details, rowIndex)); } @Override public void contextClick(int rowIndex, String rowKey, String columnInternalId, Section section, MouseEventDetails details) { T item = null; if (rowKey != null) { item = getDataCommunicator().getKeyMapper().get(rowKey); } fireEvent(new GridContextClickEvent<>(Grid.this, details, section, rowIndex, item, getColumnByInternalId(columnInternalId))); } @Override public void columnsReordered(List newColumnOrder, List oldColumnOrder) { final String diffStateKey = "columnOrder"; ConnectorTracker connectorTracker = getUI().getConnectorTracker(); JsonObject diffState = connectorTracker.getDiffState(Grid.this); // discard the change if the columns have been reordered from // the server side, as the server side is always right if (getState(false).columnOrder.equals(oldColumnOrder)) { // Don't mark as dirty since client has the state already getState(false).columnOrder = newColumnOrder; // write changes to diffState so that possible reverting the // column order is sent to client assert diffState .hasKey(diffStateKey) : "Field name has changed"; Type type = null; try { type = getState(false).getClass().getField(diffStateKey) .getGenericType(); } catch (NoSuchFieldException | SecurityException e) { e.printStackTrace(); } EncodeResult encodeResult = JsonCodec.encode( getState(false).columnOrder, diffState, type, connectorTracker); diffState.put(diffStateKey, encodeResult.getEncodedValue()); fireColumnReorderEvent(true); } else { // make sure the client is reverted to the order that the // server thinks it is diffState.remove(diffStateKey); markAsDirty(); } } @Override public void columnVisibilityChanged(String internalId, boolean hidden) { Column column = getColumnByInternalId(internalId); column.checkColumnIsAttached(); if (column.isHidden() != hidden) { column.getState().hidden = hidden; fireColumnVisibilityChangeEvent(column, hidden, true); } } @Override public void columnResized(String internalId, double pixels) { final Column column = getColumnByInternalId(internalId); if (column != null && column.isResizable()) { column.getState().width = pixels; fireColumnResizeEvent(column, true); } } } /** * Class for managing visible details rows. * * @param * the grid bean type */ public static class DetailsManager extends AbstractGridExtension { private final Set visibleDetails = new HashSet<>(); private final Map components = new HashMap<>(); private DetailsGenerator generator; /** * Sets the details component generator. * * @param generator * the generator for details components */ public void setDetailsGenerator(DetailsGenerator generator) { if (this.generator != generator) { removeAllComponents(); } getState().hasDetailsGenerator = generator != null; this.generator = generator; visibleDetails.forEach(this::refresh); } @Override public void remove() { removeAllComponents(); super.remove(); } private void removeAllComponents() { // Clean up old components components.values().forEach(this::removeComponentFromGrid); components.clear(); } @Override public void generateData(T item, JsonObject jsonObject) { if (generator == null || !visibleDetails.contains(item)) { return; } if (!components.containsKey(item)) { Component detailsComponent = generator.apply(item); Objects.requireNonNull(detailsComponent, "Details generator can't create null components"); if (detailsComponent.getParent() != null) { throw new IllegalStateException( "Details component was already attached"); } addComponentToGrid(detailsComponent); components.put(item, detailsComponent); } jsonObject.put(GridState.JSONKEY_DETAILS_VISIBLE, components.get(item).getConnectorId()); } @Override public void destroyData(T item) { // No clean up needed. Components are removed when hiding details // and/or changing details generator } /** * Sets the visibility of details component for given item. * * @param item * the item to show details for * @param visible * {@code true} if details component should be visible; * {@code false} if it should be hidden */ public void setDetailsVisible(T item, boolean visible) { boolean refresh = false; if (!visible) { refresh = visibleDetails.remove(item); if (components.containsKey(item)) { removeComponentFromGrid(components.remove(item)); } } else { refresh = visibleDetails.add(item); } if (refresh) { refresh(item); } } /** * Returns the visibility of details component for given item. * * @param item * the item to show details for * * @return {@code true} if details component should be visible; * {@code false} if it should be hidden */ public boolean isDetailsVisible(T item) { return visibleDetails.contains(item); } @Override public Grid getParent() { return super.getParent(); } @Override protected DetailsManagerState getState() { return (DetailsManagerState) super.getState(); } @Override protected DetailsManagerState getState(boolean markAsDirty) { return (DetailsManagerState) super.getState(markAsDirty); } } /** * This extension manages the configuration and data communication for a * Column inside of a Grid component. * * @param * the grid bean type * @param * the column value type */ public static class Column extends AbstractExtension { /** * Behavior when parsing nested properties which may contain * null values in the property chain. */ public enum NestedNullBehavior { /** * Throw a NullPointerException if there is a nested * null value. */ THROW, /** * Silently ignore any exceptions caused by nested null * values. */ ALLOW_NULLS } private final ValueProvider valueProvider; private ValueProvider presentationProvider; private SortOrderProvider sortOrderProvider = direction -> { String id = getId(); if (id == null) { return Stream.empty(); } return Stream.of(new QuerySortOrder(id, direction)); }; private NestedNullBehavior nestedNullBehavior = NestedNullBehavior.THROW; private boolean sortable = true; private SerializableComparator comparator; private StyleGenerator styleGenerator = item -> null; private DescriptionGenerator descriptionGenerator; private DataGenerator dataGenerator = new DataGenerator() { @Override public void generateData(T item, JsonObject jsonObject) { ColumnState state = getState(false); String communicationId = getConnectorId(); assert communicationId != null : "No communication ID set for column " + state.caption; JsonObject obj = getDataObject(jsonObject, DataCommunicatorConstants.DATA); obj.put(communicationId, generateRendererValue(item, presentationProvider, state.renderer)); String style = styleGenerator.apply(item); if (style != null && !style.isEmpty()) { JsonObject styleObj = getDataObject(jsonObject, GridState.JSONKEY_CELLSTYLES); styleObj.put(communicationId, style); } if (descriptionGenerator != null) { String description = descriptionGenerator.apply(item); if (description != null && !description.isEmpty()) { JsonObject descriptionObj = getDataObject(jsonObject, GridState.JSONKEY_CELLDESCRIPTION); descriptionObj.put(communicationId, description); } } } @Override public void destroyData(T item) { removeComponent(getGrid().getDataProvider().getId(item)); } @Override public void destroyAllData() { // Make a defensive copy of keys, as the map gets cleared when // removing components. new HashSet<>(activeComponents.keySet()) .forEach(component -> removeComponent(component)); } }; private Binding editorBinding; private Map activeComponents = new HashMap<>(); private String userId; /** * Constructs a new Column configuration with given renderer and value * provider. * * @param valueProvider * the function to get values from items, not * null * @param renderer * the value renderer, not null */ protected Column(ValueProvider valueProvider, Renderer renderer) { this(valueProvider, ValueProvider.identity(), renderer); } /** * Constructs a new Column configuration with given renderer and value * provider. *

* For a more complete explanation on presentation provider, see * {@link #setRenderer(ValueProvider, Renderer)}. * * @param valueProvider * the function to get values from items, not * null * @param presentationProvider * the function to get presentations from the value of this * column, not null. For more details, see * {@link #setRenderer(ValueProvider, Renderer)} * @param renderer * the presentation renderer, not null * @param

* the presentation type * * @since 8.1 */ protected

Column(ValueProvider valueProvider, ValueProvider presentationProvider, Renderer renderer) { Objects.requireNonNull(valueProvider, "Value provider can't be null"); Objects.requireNonNull(presentationProvider, "Presentation provider can't be null"); Objects.requireNonNull(renderer, "Renderer can't be null"); ColumnState state = getState(); this.valueProvider = valueProvider; this.presentationProvider = presentationProvider; state.renderer = renderer; state.caption = ""; // Add the renderer as a child extension of this extension, thus // ensuring the renderer will be unregistered when this column is // removed addExtension(renderer); Class valueType = renderer.getPresentationType(); if (Comparable.class.isAssignableFrom(valueType)) { comparator = (a, b) -> compareComparables( valueProvider.apply(a), valueProvider.apply(b)); } else if (Number.class.isAssignableFrom(valueType)) { /* * Value type will be Number whenever using NumberRenderer. * Provide explicit comparison support in this case even though * Number itself isn't Comparable. */ comparator = (a, b) -> compareNumbers( (Number) valueProvider.apply(a), (Number) valueProvider.apply(b)); } else { comparator = (a, b) -> compareMaybeComparables( valueProvider.apply(a), valueProvider.apply(b)); } } /** * Constructs a new Column configuration with given renderer and value * provider. *

* For a more complete explanation on presentation provider, see * {@link #setRenderer(ValueProvider, Renderer)}. * * @param valueProvider * the function to get values from items, not * null * @param presentationProvider * the function to get presentations from the value of this * column, not null. For more details, see * {@link #setRenderer(ValueProvider, Renderer)} * @param nestedNullBehavior * behavior on encountering nested null values * when reading the value from the bean * @param renderer * the presentation renderer, not null * @param

* the presentation type * * @since 8.8 */ protected

Column(ValueProvider valueProvider, ValueProvider presentationProvider, Renderer renderer, NestedNullBehavior nestedNullBehavior) { this(valueProvider, presentationProvider, renderer); this.nestedNullBehavior = nestedNullBehavior; } private static int compareMaybeComparables(Object a, Object b) { if (hasCommonComparableBaseType(a, b)) { return compareComparables(a, b); } return compareComparables(Objects.toString(a, ""), Objects.toString(b, "")); } private static boolean hasCommonComparableBaseType(Object a, Object b) { if (a instanceof Comparable && b instanceof Comparable) { Class aClass = a.getClass(); Class bClass = b.getClass(); if (aClass == bClass) { return true; } Class baseType = ReflectTools.findCommonBaseType(aClass, bClass); if (Comparable.class.isAssignableFrom(baseType)) { return true; } } if ((a == null && b instanceof Comparable) || (b == null && a instanceof Comparable)) { return true; } return false; } @SuppressWarnings({ "unchecked", "rawtypes" }) private static int compareComparables(Object a, Object b) { return ((Comparator) Comparator .nullsLast(Comparator.naturalOrder())).compare(a, b); } @SuppressWarnings("unchecked") private static int compareNumbers(Number a, Number b) { Number valueA = a != null ? a : Double.POSITIVE_INFINITY; Number valueB = b != null ? b : Double.POSITIVE_INFINITY; // Most Number implementations are Comparable if (valueA instanceof Comparable && valueA.getClass().isInstance(valueB)) { return ((Comparable) valueA).compareTo(valueB); } if (valueA.equals(valueB)) { return 0; } // Fall back to comparing based on potentially truncated values int compare = Long.compare(valueA.longValue(), valueB.longValue()); if (compare == 0) { // This might still produce 0 even though the values are not // equals, but there's nothing more we can do about that compare = Double.compare(valueA.doubleValue(), valueB.doubleValue()); } return compare; } @SuppressWarnings("unchecked") private

JsonValue generateRendererValue(T item, ValueProvider presentationProvider, Connector renderer) { V value; try { value = valueProvider.apply(item); } catch (NullPointerException npe) { value = null; if (NestedNullBehavior.THROW == nestedNullBehavior) { throw npe; } } P presentationValue = presentationProvider.apply(value); // Make Grid track components. if (renderer instanceof ComponentRenderer && presentationValue instanceof Component) { addComponent(getGrid().getDataProvider().getId(item), (Component) presentationValue); } return ((Renderer

) renderer).encode(presentationValue); } private void addComponent(Object item, Component component) { if (activeComponents.containsKey(item)) { if (activeComponents.get(item).equals(component)) { // Reusing old component return; } removeComponent(item); } activeComponents.put(item, component); getGrid().addExtensionComponent(component); } private void removeComponent(Object item) { Component component = activeComponents.remove(item); if (component != null) { getGrid().removeExtensionComponent(component); } } /** * Gets a data object with the given key from the given JsonObject. If * there is no object with the key, this method creates a new * JsonObject. * * @param jsonObject * the json object * @param key * the key where the desired data object is stored * @return data object for the given key */ private JsonObject getDataObject(JsonObject jsonObject, String key) { if (!jsonObject.hasKey(key)) { jsonObject.put(key, Json.createObject()); } return jsonObject.getObject(key); } @Override protected ColumnState getState() { return getState(true); } @Override protected ColumnState getState(boolean markAsDirty) { return (ColumnState) super.getState(markAsDirty); } /** * This method extends the given Grid with this Column. * * @param grid * the grid to extend */ private void extend(Grid grid) { super.extend(grid); } /** * Returns the identifier used with this Column in communication. * * @return the identifier string */ private String getInternalId() { return getState(false).internalId; } /** * Sets the identifier to use with this Column in communication. * * @param id * the identifier string */ private void setInternalId(String id) { Objects.requireNonNull(id, "Communication ID can't be null"); getState().internalId = id; } /** * Returns the user-defined identifier for this column. * * @return the identifier string */ public String getId() { return userId; } /** * Sets the user-defined identifier to map this column. The identifier * can be used for example in {@link Grid#getColumn(String)}. *

* The id is also used as the {@link #setSortProperty(String...) backend * sort property} for this column if no sort property or sort order * provider has been set for this column. * * @see #setSortProperty(String...) * @see #setSortOrderProvider(SortOrderProvider) * * @param id * the identifier string * @return this column */ public Column setId(String id) { Objects.requireNonNull(id, "Column identifier cannot be null"); if (userId != null) { throw new IllegalStateException( "Column identifier cannot be changed"); } userId = id; getGrid().setColumnId(id, this); updateSortable(); return this; } private void updateSortable() { boolean inMemory = getGrid().getDataProvider().isInMemory(); boolean hasSortOrder = getSortOrder(SortDirection.ASCENDING) .count() != 0; getState().sortable = this.sortable && (inMemory || hasSortOrder); } /** * Gets the function used to produce the value for data in this column * based on the row item. * * @return the value provider function * * @since 8.0.3 */ public ValueProvider getValueProvider() { return valueProvider; } /** * Gets the function to get presentations from the value of data in this * column, based on the row item. * * @return the presentation provider function * * @since 8.13 */ public ValueProvider getPresentationProvider() { return presentationProvider; } /** * Sets whether the user can sort this column or not. Whether the column * is actually sortable after {@code setSortable(true)} depends on the * {@link DataProvider} and the defined sort order for this column. When * using an {@link InMemoryDataProvider} sorting can be automatic. * * @param sortable * {@code true} to enable sorting for this column; * {@code false} to disable it * @return this column */ public Column setSortable(boolean sortable) { if (this.sortable != sortable) { this.sortable = sortable; updateSortable(); } return this; } /** * Gets whether sorting is enabled for this column. * * @return {@code true} if the sorting is enabled for this column; * {@code false} if not */ public boolean isSortable() { return sortable; } /** * Gets whether the user can actually sort this column. * * @return {@code true} if the column can be sorted by the user; * {@code false} if not * * @since 8.3.2 */ public boolean isSortableByUser() { return getState(false).sortable; } /** * Sets the header aria-label for this column. * * @param caption * the header aria-label, null removes the aria-label from * this column * * @return this column * * @since 8.2 */ public Column setAssistiveCaption(String caption) { if (Objects.equals(caption, getAssistiveCaption())) { return this; } getState().assistiveCaption = caption; return this; } /** * Gets the header caption for this column. * * @return header caption * * @since 8.2 */ public String getAssistiveCaption() { return getState(false).assistiveCaption; } /** * Sets the header caption for this column. * * @param caption * the header caption, not null * * @return this column */ public Column setCaption(String caption) { Objects.requireNonNull(caption, "Header caption can't be null"); caption = Jsoup.parse(caption).text(); if (caption.equals(getState(false).caption)) { return this; } getState().caption = caption; HeaderRow row = getGrid().getDefaultHeaderRow(); if (row != null) { row.getCell(this).setText(caption); } return this; } /** * Gets the header caption for this column. * * @return header caption */ public String getCaption() { return getState(false).caption; } /** * Sets a comparator to use with in-memory sorting with this column. * Sorting with a back-end is done using * {@link Column#setSortProperty(String...)}. * * @param comparator * the comparator to use when sorting data in this column * @return this column */ public Column setComparator( SerializableComparator comparator) { Objects.requireNonNull(comparator, "Comparator can't be null"); this.comparator = comparator; return this; } /** * Gets the comparator to use with in-memory sorting for this column * when sorting in the given direction. * * @param sortDirection * the direction this column is sorted by * @return comparator for this column */ public SerializableComparator getComparator( SortDirection sortDirection) { Objects.requireNonNull(comparator, "No comparator defined for sorted column."); boolean reverse = sortDirection != SortDirection.ASCENDING; return reverse ? (t1, t2) -> comparator.reversed().compare(t1, t2) : comparator; } /** * Sets strings describing back end properties to be used when sorting * this column. *

* By default, the {@link #setId(String) column id} will be used as the * sort property. * * @param properties * the array of strings describing backend properties * @return this column */ public Column setSortProperty(String... properties) { Objects.requireNonNull(properties, "Sort properties can't be null"); return setSortOrderProvider(dir -> Arrays.stream(properties) .map(s -> new QuerySortOrder(s, dir))); } /** * Sets the sort orders when sorting this column. The sort order * provider is a function which provides {@link QuerySortOrder} objects * to describe how to sort by this column. *

* By default, the {@link #setId(String) column id} will be used as the * sort property. * * @param provider * the function to use when generating sort orders with the * given direction * @return this column */ public Column setSortOrderProvider(SortOrderProvider provider) { Objects.requireNonNull(provider, "Sort order provider can't be null"); sortOrderProvider = provider; // Update state updateSortable(); return this; } /** * Gets the sort orders to use with back-end sorting for this column * when sorting in the given direction. * * @see #setSortProperty(String...) * @see #setId(String) * @see #setSortOrderProvider(SortOrderProvider) * * @param direction * the sorting direction * @return stream of sort orders */ public Stream getSortOrder(SortDirection direction) { return sortOrderProvider.apply(direction); } /** * Sets the style generator that is used for generating class names for * cells in this column. Returning null from the generator results in no * custom style name being set. * * Note: The style generator is applied only to the body cells, not to * the Editor. * * @param cellStyleGenerator * the cell style generator to set, not null * @return this column * @throws NullPointerException * if {@code cellStyleGenerator} is {@code null} */ public Column setStyleGenerator( StyleGenerator cellStyleGenerator) { Objects.requireNonNull(cellStyleGenerator, "Cell style generator must not be null"); this.styleGenerator = cellStyleGenerator; getGrid().getDataCommunicator().reset(); return this; } /** * Gets the style generator that is used for generating styles for * cells. * * @return the cell style generator */ public StyleGenerator getStyleGenerator() { return styleGenerator; } /** * Sets the description generator that is used for generating * descriptions for cells in this column. This method uses the * {@link ContentMode#PREFORMATTED} content mode. * * @see #setDescriptionGenerator(DescriptionGenerator, ContentMode) * * @param cellDescriptionGenerator * the cell description generator to set, or {@code null} to * remove a previously set generator * @return this column */ public Column setDescriptionGenerator( DescriptionGenerator cellDescriptionGenerator) { return setDescriptionGenerator(cellDescriptionGenerator, ContentMode.PREFORMATTED); } /** * Sets the description generator that is used for generating * descriptions for cells in this column. This method uses the given * content mode. * * @see #setDescriptionGenerator(DescriptionGenerator) * * @param cellDescriptionGenerator * the cell description generator to set, or {@code null} to * remove a previously set generator * @param tooltipContentMode * the content mode for tooltips * @return this column * * @since 8.2 */ public Column setDescriptionGenerator( DescriptionGenerator cellDescriptionGenerator, ContentMode tooltipContentMode) { this.descriptionGenerator = cellDescriptionGenerator; getState().tooltipContentMode = tooltipContentMode; getGrid().getDataCommunicator().reset(); return this; } /** * Gets the description generator that is used for generating * descriptions for cells. * * @return the cell description generator, or null if no * generator is set */ public DescriptionGenerator getDescriptionGenerator() { return descriptionGenerator; } /** * Sets the ratio with which the column expands. *

* By default, all columns expand equally (treated as if all of them had * an expand ratio of 1). Once at least one column gets a defined expand * ratio, the implicit expand ratio is removed, and only the defined * expand ratios are taken into account. *

* If a column has a defined width ({@link #setWidth(double)}), it * overrides this method's effects. *

* Example: A grid with three columns, with expand ratios 0, 1 * and 2, respectively. The column with a ratio of 0 is exactly * as wide as its contents requires. The column with a ratio of * 1 is as wide as it needs, plus a third of any excess * space, because we have 3 parts total, and this column * reserves only one of those. The column with a ratio of 2, is as wide * as it needs to be, plus two thirds of the excess * width. * * @param expandRatio * the expand ratio of this column. {@code 0} to not have it * expand at all. A negative number to clear the expand * value. * @return this column * @throws IllegalStateException * if the column is no longer attached to any grid * @see #setWidth(double) */ public Column setExpandRatio(int expandRatio) throws IllegalStateException { checkColumnIsAttached(); if (expandRatio != getExpandRatio()) { getState().expandRatio = expandRatio; getGrid().markAsDirty(); } return this; } /** * Returns the column's expand ratio. * * @return the column's expand ratio * @see #setExpandRatio(int) */ public int getExpandRatio() { return getState(false).expandRatio; } /** * Clears the expand ratio for this column. *

* Equal to calling {@link #setExpandRatio(int) setExpandRatio(-1)} * * @return this column * @throws IllegalStateException * if the column is no longer attached to any grid */ public Column clearExpandRatio() throws IllegalStateException { return setExpandRatio(-1); } /** * Returns the width (in pixels). By default a column width is * {@value com.vaadin.shared.ui.grid.GridConstants#DEFAULT_COLUMN_WIDTH_PX} * (undefined). * * @return the width in pixels of the column * @throws IllegalStateException * if the column is no longer attached to any grid */ public double getWidth() throws IllegalStateException { checkColumnIsAttached(); return getState(false).width; } /** * Sets the width (in pixels). *

* This overrides any configuration set by any of * {@link #setExpandRatio(int)}, {@link #setMinimumWidth(double)} or * {@link #setMaximumWidth(double)}. * * @param pixelWidth * the new pixel width of the column * @return the column itself * * @throws IllegalStateException * if the column is no longer attached to any grid * @throws IllegalArgumentException * thrown if pixel width is less than zero */ public Column setWidth(double pixelWidth) throws IllegalStateException, IllegalArgumentException { checkColumnIsAttached(); if (pixelWidth < 0) { throw new IllegalArgumentException( "Pixel width should be greated than 0 (in " + toString() + ")"); } if (pixelWidth != getWidth()) { getState().width = pixelWidth; getGrid().markAsDirty(); getGrid().fireColumnResizeEvent(this, false); } return this; } /** * Returns whether this column has an undefined width. * * @since 7.6 * @return whether the width is undefined * @throws IllegalStateException * if the column is no longer attached to any grid */ public boolean isWidthUndefined() { checkColumnIsAttached(); return getState(false).width < 0; } /** * Marks the column width as undefined. An undefined width means the * grid is free to resize the column based on the cell contents and * available space in the grid. * * @return the column itself */ public Column setWidthUndefined() { checkColumnIsAttached(); if (!isWidthUndefined()) { getState().width = -1; getGrid().markAsDirty(); getGrid().fireColumnResizeEvent(this, false); } return this; } /** * Sets the minimum width for this column. *

* This defines the minimum guaranteed pixel width of the column * when it is set to expand. * * Note: Value -1 is not accepted, use {@link #setWidthUndefined()} * instead. * * @param pixels * the minimum width for the column * @throws IllegalStateException * if the column is no longer attached to any grid * @see #setExpandRatio(int) * @return the column itself */ public Column setMinimumWidth(double pixels) throws IllegalStateException { checkColumnIsAttached(); final double maxwidth = getMaximumWidth(); if (pixels >= 0 && pixels > maxwidth && maxwidth >= 0) { throw new IllegalArgumentException("New minimum width (" + pixels + ") was greater than maximum width (" + maxwidth + ")"); } getState().minWidth = pixels; getGrid().markAsDirty(); return this; } /** * Return the minimum width for this column. * * @return the minimum width for this column * @see #setMinimumWidth(double) */ public double getMinimumWidth() { return getState(false).minWidth; } /** * Sets whether the width of the contents in the column should be * considered minimum width for this column. *

* If this is set to true (default for backwards * compatibility), then a column will not shrink to smaller than the * width required to show the contents available when calculating the * widths (only the widths of the initially rendered rows are * considered). *

* If this is set to false and the column has been set to * expand using #setExpandRatio(int), then the contents of the column * will be ignored when calculating the width, and the column will thus * shrink down to the minimum width defined by #setMinimumWidth(double) * if necessary. * * @param minimumWidthFromContent * true to reserve space for all contents, * false to allow the column to shrink smaller * than the contents * @return the column itself * @throws IllegalStateException * if the column is no longer attached to any grid * @see #setMinimumWidth(double) * @since 8.1 */ public Column setMinimumWidthFromContent( boolean minimumWidthFromContent) throws IllegalStateException { checkColumnIsAttached(); if (isMinimumWidthFromContent() != minimumWidthFromContent) { getState().minimumWidthFromContent = minimumWidthFromContent; getGrid().markAsDirty(); } return this; } /** * Gets whether the width of the contents in the column should be * considered minimum width for this column. * * @return true to reserve space for all contents, * false to allow the column to shrink smaller than * the contents * @see #setMinimumWidthFromContent(boolean) * @since 8.1 */ public boolean isMinimumWidthFromContent() { return getState(false).minimumWidthFromContent; } /** * Sets the maximum width for this column. *

* This defines the maximum allowed pixel width of the column when * it is set to expand. * * @param pixels * the maximum width * @return this column * @throws IllegalStateException * if the column is no longer attached to any grid * @see #setExpandRatio(int) */ public Column setMaximumWidth(double pixels) { checkColumnIsAttached(); final double minwidth = getMinimumWidth(); if (pixels >= 0 && pixels < minwidth && minwidth >= 0) { throw new IllegalArgumentException("New maximum width (" + pixels + ") was less than minimum width (" + minwidth + ")"); } getState().maxWidth = pixels; getGrid().markAsDirty(); return this; } /** * Returns the maximum width for this column. * * @return the maximum width for this column * @see #setMaximumWidth(double) */ public double getMaximumWidth() { return getState(false).maxWidth; } /** * Sets whether this column can be resized by the user. * * @since 7.6 * @param resizable * {@code true} if this column should be resizable, * {@code false} otherwise * @return this column * @throws IllegalStateException * if the column is no longer attached to any grid */ public Column setResizable(boolean resizable) { checkColumnIsAttached(); if (resizable != isResizable()) { getState().resizable = resizable; getGrid().markAsDirty(); } return this; } /** * Gets the caption of the hiding toggle for this column. * * @since 7.5.0 * @see #setHidingToggleCaption(String) * @return the caption for the hiding toggle for this column */ public String getHidingToggleCaption() { return getState(false).hidingToggleCaption; } /** * Sets the caption of the hiding toggle for this column. Shown in the * toggle for this column in the grid's sidebar when the column is * {@link #isHidable() hidable}. *

* The default value is null, and in that case the column's * {@link #getCaption() header caption} is used. *

* NOTE: setting this to empty string might cause the hiding * toggle to not render correctly. * * @since 7.5.0 * @param hidingToggleCaption * the text to show in the column hiding toggle * @return the column itself */ public Column setHidingToggleCaption(String hidingToggleCaption) { if (hidingToggleCaption != getHidingToggleCaption()) { getState().hidingToggleCaption = hidingToggleCaption; } return this; } /** * Hides or shows the column. By default columns are visible before * explicitly hiding them. * * @since 7.5.0 * @param hidden * true to hide the column, false * to show * @return this column * @throws IllegalStateException * if the column is no longer attached to any grid */ public Column setHidden(boolean hidden) { checkColumnIsAttached(); if (hidden != isHidden()) { getState().hidden = hidden; getGrid().fireColumnVisibilityChangeEvent(this, hidden, false); } return this; } /** * Returns whether this column is hidden. Default is {@code false}. * * @since 7.5.0 * @return true if the column is currently hidden, * false otherwise */ public boolean isHidden() { return getState(false).hidden; } /** * Sets whether this column can be hidden by the user. Hidable columns * can be hidden and shown via the sidebar menu. * * @since 7.5.0 * @param hidable * true if the column may be hidable by the user * via UI interaction * @return this column */ public Column setHidable(boolean hidable) { if (hidable != isHidable()) { getState().hidable = hidable; } return this; } /** * Returns whether this column can be hidden by the user. Default is * {@code false}. *

* Note: the column can be programmatically hidden using * {@link #setHidden(boolean)} regardless of the returned value. * * @since 7.5.0 * @return true if the user can hide the column, * false if not */ public boolean isHidable() { return getState(false).hidable; } /** * Returns whether this column can be resized by the user. Default is * {@code true}. *

* Note: the column can be programmatically resized using * {@link #setWidth(double)} and {@link #setWidthUndefined()} regardless * of the returned value. * * @since 7.6 * @return {@code true} if this column is resizable, {@code false} * otherwise */ public boolean isResizable() { return getState(false).resizable; } /** * Sets whether this Column has a component displayed in Editor or not. * A column can only be editable if an editor component or binding has * been set. * * @param editable * {@code true} if column is editable; {@code false} if not * @return this column * @throws IllegalStateException * if editable is true and column has no editor binding or * component defined * * @see #setEditorComponent(HasValue, Setter) * @see #setEditorBinding(Binding) */ public Column setEditable(boolean editable) throws IllegalStateException { if (editable && editorBinding == null) { throw new IllegalStateException( "Column has no editor binding or component defined"); } getState().editable = editable; return this; } /** * Gets whether this Column has a component displayed in Editor or not. * * @return {@code true} if the column displays an editor component; * {@code false} if not */ public boolean isEditable() { return getState(false).editable; } /** * Sets an editor binding for this column. The {@link Binding} is used * when a row is in editor mode to define how to populate an editor * component based on the edited row and how to update an item based on * the value in the editor component. *

* To create a binding to use with a column, define a binding for the * editor binder (grid.getEditor().getBinder()) using e.g. * {@link Binder#forField(HasValue)}. You can also use * {@link #setEditorComponent(HasValue, Setter)} if no validator or * converter is needed for the binding. *

* The {@link HasValue} that the binding is defined to use must be a * {@link Component}. * * @param binding * the binding to use for this column * @return this column * * @see #setEditorComponent(HasValue, Setter) * @see Binding * @see Grid#getEditor() * @see Editor#getBinder() */ public Column setEditorBinding(Binding binding) { Objects.requireNonNull(binding, "null is not a valid editor field"); if (!(binding.getField() instanceof Component)) { throw new IllegalArgumentException( "Binding target must be a component."); } this.editorBinding = binding; return setEditable(true); } /** * Gets the binder binding that is currently used for this column. * * @return the used binder binding, or null if no binding * is configured * * @see #setEditorBinding(Binding) */ public Binding getEditorBinding() { return editorBinding; } /** * Sets a component and setter to use for editing values of this column * in the editor row. This is a shorthand for use in simple cases where * no validator or converter is needed. Use * {@link #setEditorBinding(Binding)} to support more complex cases. *

* Note: The same component cannot be used for multiple * columns. * * @param * a class that extends both {@link HasValue} and * {@link Component} * @param editorComponent * the editor component * @param setter * a setter that stores the component value in the row item * @return this column * * @see #setEditorBinding(Binding) * @see Grid#getEditor() * @see Binder#bind(HasValue, ValueProvider, Setter) */ public & Component> Column setEditorComponent( C editorComponent, Setter setter) { Objects.requireNonNull(editorComponent, "Editor component cannot be null"); Objects.requireNonNull(setter, "Setter cannot be null"); Binding binding = getGrid().getEditor().getBinder() .bind(editorComponent, valueProvider::apply, setter); return setEditorBinding(binding); } /** * Sets a component to use for editing values of this columns in the * editor row. This method can only be used if the column has an * {@link #setId(String) id} and the {@link Grid} has been created using * {@link Grid#Grid(Class)} or some other way that allows finding * properties based on property names. *

* This is a shorthand for use in simple cases where no validator or * converter is needed. Use {@link #setEditorBinding(Binding)} to * support more complex cases. *

* Note: The same component cannot be used for multiple * columns. * * @param * a value type * @param * a class that extends both {@link HasValue} (for type * {@code }) and {@link Component} * @param editorComponent * the editor component * @return this column * * @see #setEditorBinding(Binding) * @see Grid#getEditor() * @see Binder#bind(HasValue, String) * @see Grid#Grid(Class) */ public & Component> Column setEditorComponent( C editorComponent) { Objects.requireNonNull(editorComponent, "Editor component cannot be null"); String propertyName = getId(); if (propertyName == null) { throw new IllegalStateException( "setEditorComponent without a setter can only be used if the column has an id. " + "Use another setEditorComponent(Component, Setter) or setEditorBinding(Binding) instead."); } Binding binding = getGrid().getEditor().getBinder() .bind(editorComponent, propertyName); return setEditorBinding(binding); } /** * Sets the Renderer for this Column. Setting the renderer will cause * all currently available row data to be recreated and sent to the * client. * * Note: Setting a new renderer will reset presentation provider if it * exists. * * @param renderer * the new renderer * @return this column * * @since 8.0.3 */ public Column setRenderer(Renderer renderer) { return setRenderer(ValueProvider.identity(), renderer); } /** * Sets the Renderer for this Column. Setting the renderer will cause * all currently available row data to be recreated and sent to the * client. *

* The presentation provider is a method that takes the value of this * column on a single row, and maps that to a value that the renderer * accepts. This feature can be used for storing a complex value in a * column for editing, but providing a simplified presentation for the * user when not editing. * * @param presentationProvider * the function to get presentations from the value of this * column, not {@code null} * @param renderer * the new renderer, not {@code null} * * @param

* the presentation type * * @return this column * * @since 8.1 */ public

Column setRenderer( ValueProvider presentationProvider, Renderer renderer) { Objects.requireNonNull(renderer, "Renderer can not be null"); Objects.requireNonNull(presentationProvider, "Presentation provider can not be null"); // Remove old renderer Connector oldRenderer = getState().renderer; if (oldRenderer instanceof Extension) { removeExtension((Extension) oldRenderer); } // Set new renderer getState().renderer = renderer; addExtension(renderer); this.presentationProvider = presentationProvider; // Trigger redraw getGrid().getDataCommunicator().reset(); return this; } /** * Gets the Renderer for this Column. * * @return the renderer * @since 8.1 */ public Renderer getRenderer() { return (Renderer) getState().renderer; } /** * Sets whether Grid should handle events in this Column from Components * and Widgets rendered by certain Renderers. By default the events are * not handled. *

* Note: Enabling this feature will for example select * a row when a component is clicked. For example in the case of a * {@link ComboBox} or {@link TextField} it might be problematic as the * component gets re-rendered and might lose focus. * * @param handleWidgetEvents * {@code true} to handle events; {@code false} to not * @return this column * @since 8.3 */ public Column setHandleWidgetEvents(boolean handleWidgetEvents) { getState().handleWidgetEvents = handleWidgetEvents; return this; } /** * Gets whether Grid is handling the events in this Column from * Component and Widgets. * * @see #setHandleWidgetEvents(boolean) * * @return {@code true} if handling events; {@code false} if not * @since 8.3 */ public boolean isHandleWidgetEvents() { return getState(false).handleWidgetEvents; } /** * Gets the grid that this column belongs to. * * @return the grid that this column belongs to, or null if * this column has not yet been associated with any grid */ @SuppressWarnings("unchecked") protected Grid getGrid() { return (Grid) getParent(); } /** * Checks if column is attached and throws an * {@link IllegalStateException} if it is not. * * @throws IllegalStateException * if the column is no longer attached to any grid */ protected void checkColumnIsAttached() throws IllegalStateException { if (getGrid() == null) { throw new IllegalStateException( "Column is no longer attached to a grid."); } } /** * Writes the design attributes for this column into given element. * * @since 7.5.0 * * @param element * Element to write attributes into * * @param designContext * the design context */ protected void writeDesign(Element element, DesignContext designContext) { Attributes attributes = element.attributes(); ColumnState defaultState = new ColumnState(); if (getId() == null) { setId("column" + getGrid().getColumns().indexOf(this)); } DesignAttributeHandler.writeAttribute("column-id", attributes, getId(), null, String.class, designContext); // Sortable is a special attribute that depends on the data // provider. DesignAttributeHandler.writeAttribute("sortable", attributes, isSortable(), null, boolean.class, designContext); DesignAttributeHandler.writeAttribute("editable", attributes, isEditable(), defaultState.editable, boolean.class, designContext); DesignAttributeHandler.writeAttribute("resizable", attributes, isResizable(), defaultState.resizable, boolean.class, designContext); DesignAttributeHandler.writeAttribute("hidable", attributes, isHidable(), defaultState.hidable, boolean.class, designContext); DesignAttributeHandler.writeAttribute("hidden", attributes, isHidden(), defaultState.hidden, boolean.class, designContext); DesignAttributeHandler.writeAttribute("hiding-toggle-caption", attributes, getHidingToggleCaption(), defaultState.hidingToggleCaption, String.class, designContext); DesignAttributeHandler.writeAttribute("width", attributes, getWidth(), defaultState.width, Double.class, designContext); DesignAttributeHandler.writeAttribute("min-width", attributes, getMinimumWidth(), defaultState.minWidth, Double.class, designContext); DesignAttributeHandler.writeAttribute("max-width", attributes, getMaximumWidth(), defaultState.maxWidth, Double.class, designContext); DesignAttributeHandler.writeAttribute("expand", attributes, getExpandRatio(), defaultState.expandRatio, Integer.class, designContext); } /** * Reads the design attributes for this column from given element. * * @since 7.5.0 * @param design * Element to read attributes from * @param designContext * the design context */ @SuppressWarnings("unchecked") protected void readDesign(Element design, DesignContext designContext) { Attributes attributes = design.attributes(); if (design.hasAttr("sortable")) { setSortable(DesignAttributeHandler.readAttribute("sortable", attributes, boolean.class)); } else { setSortable(false); } if (design.hasAttr("editable")) { /** * This is a fake editor just to have something (otherwise * "setEditable" throws an exception. * * Let's use TextField here because we support only Strings as * inline data type. It will work incorrectly for other types * but we don't support them anyway. */ setEditorComponent((HasValue & Component) new TextField(), (item, value) -> { // Ignore user value since we don't know the setter }); setEditable(DesignAttributeHandler.readAttribute("editable", attributes, boolean.class)); } if (design.hasAttr("resizable")) { setResizable(DesignAttributeHandler.readAttribute("resizable", attributes, boolean.class)); } if (design.hasAttr("hidable")) { setHidable(DesignAttributeHandler.readAttribute("hidable", attributes, boolean.class)); } if (design.hasAttr("hidden")) { setHidden(DesignAttributeHandler.readAttribute("hidden", attributes, boolean.class)); } if (design.hasAttr("hiding-toggle-caption")) { setHidingToggleCaption(DesignAttributeHandler.readAttribute( "hiding-toggle-caption", attributes, String.class)); } if (design.hasAttr("assistive-caption")) { setAssistiveCaption(DesignAttributeHandler.readAttribute( "assistive-caption", attributes, String.class)); } // Read size info where necessary. if (design.hasAttr("width")) { setWidth(DesignAttributeHandler.readAttribute("width", attributes, Double.class)); } if (design.hasAttr("min-width")) { setMinimumWidth(DesignAttributeHandler .readAttribute("min-width", attributes, Double.class)); } if (design.hasAttr("max-width")) { setMaximumWidth(DesignAttributeHandler .readAttribute("max-width", attributes, Double.class)); } if (design.hasAttr("expand")) { if (design.attr("expand").isEmpty()) { setExpandRatio(1); } else { setExpandRatio(DesignAttributeHandler.readAttribute( "expand", attributes, Integer.class)); } } } /** * Gets the DataGenerator for this Column. * * @return data generator */ private DataGenerator getDataGenerator() { return dataGenerator; } } private class HeaderImpl extends Header { @Override protected Grid getGrid() { return Grid.this; } @Override protected SectionState getState(boolean markAsDirty) { return Grid.this.getState(markAsDirty).header; } @Override protected Column getColumnByInternalId(String internalId) { return getGrid().getColumnByInternalId(internalId); } @Override @SuppressWarnings("unchecked") protected String getInternalIdForColumn(Column column) { return getGrid().getInternalIdForColumn((Column) column); } }; private class FooterImpl extends Footer { @Override protected Grid getGrid() { return Grid.this; } @Override protected SectionState getState(boolean markAsDirty) { return Grid.this.getState(markAsDirty).footer; } @Override protected Column getColumnByInternalId(String internalId) { return getGrid().getColumnByInternalId(internalId); } @Override @SuppressWarnings("unchecked") protected String getInternalIdForColumn(Column column) { return getGrid().getInternalIdForColumn((Column) column); } }; private final Set> columnSet = new LinkedHashSet<>(); private final Map> columnKeys = new HashMap<>(); private final Map> columnIds = new HashMap<>(); private final List> sortOrder = new ArrayList<>(); private final DetailsManager detailsManager; private final Set extensionComponents = new HashSet<>(); private StyleGenerator styleGenerator = item -> null; private DescriptionGenerator descriptionGenerator; private final Header header = new HeaderImpl(); private final Footer footer = new FooterImpl(); private int counter = 0; private GridSelectionModel selectionModel; private Editor editor; private PropertySet propertySet; private Class beanType = null; /** * Creates a new grid without support for creating columns based on property * names. Use an alternative constructor, such as {@link Grid#Grid(Class)}, * to create a grid that automatically sets up columns based on the type of * presented data. * * @see #Grid(Class) * @see #withPropertySet(PropertySet) */ public Grid() { this(new DataCommunicator<>()); } /** * Creates a new grid that uses reflection based on the provided bean type * to automatically set up an initial set of columns. All columns will be * configured using the same {@link Object#toString()} renderer that is used * by {@link #addColumn(ValueProvider)}. * * @param beanType * the bean type to use, not null * @see #Grid() * @see #withPropertySet(PropertySet) */ public Grid(Class beanType) { this(beanType, new DataCommunicator<>()); } /** * Creates a new grid that uses custom data communicator and provided bean * type * * It uses reflection of the provided bean type to automatically set up an * initial set of columns. All columns will be configured using the same * {@link Object#toString()} renderer that is used by * {@link #addColumn(ValueProvider)}. * * @param beanType * the bean type to use, not null * @param dataCommunicator * the data communicator to use, notnull * @since 8.0.7 */ protected Grid(Class beanType, DataCommunicator dataCommunicator) { this(BeanPropertySet.get(beanType), dataCommunicator); this.beanType = beanType; } /** * Creates a new grid with the given data communicator and without support * for creating columns based on property names. * * @param dataCommunicator * the custom data communicator to set * @see #Grid() * @see #Grid(PropertySet, DataCommunicator) * @since 8.0.7 */ protected Grid(DataCommunicator dataCommunicator) { this(new PropertySet() { @Override public Stream> getProperties() { // No columns configured by default return Stream.empty(); } @Override public Optional> getProperty(String name) { throw new IllegalStateException( "A Grid created without a bean type class literal or a custom property set" + " doesn't support finding properties by name."); } }, dataCommunicator); } /** * Creates a grid using a custom {@link PropertySet} implementation for * configuring the initial columns and resolving property names for * {@link #addColumn(String)} and * {@link Column#setEditorComponent(HasValue)}. * * @see #withPropertySet(PropertySet) * * @param propertySet * the property set implementation to use, not null. */ protected Grid(PropertySet propertySet) { this(propertySet, new DataCommunicator<>()); } /** * Creates a grid using a custom {@link PropertySet} implementation and * custom data communicator. *

* Property set is used for configuring the initial columns and resolving * property names for {@link #addColumn(String)} and * {@link Column#setEditorComponent(HasValue)}. * * @see #withPropertySet(PropertySet) * * @param propertySet * the property set implementation to use, not null. * @param dataCommunicator * the data communicator to use, notnull * @since 8.0.7 */ protected Grid(PropertySet propertySet, DataCommunicator dataCommunicator) { super(dataCommunicator); registerRpc(new GridServerRpcImpl()); setDefaultHeaderRow(appendHeaderRow()); setSelectionModel(new SingleSelectionModelImpl<>()); detailsManager = new DetailsManager<>(); addExtension(detailsManager); addDataGenerator(detailsManager); addDataGenerator((item, json) -> { String styleName = styleGenerator.apply(item); if (styleName != null && !styleName.isEmpty()) { json.put(GridState.JSONKEY_ROWSTYLE, styleName); } if (descriptionGenerator != null) { String description = descriptionGenerator.apply(item); if (description != null && !description.isEmpty()) { json.put(GridState.JSONKEY_ROWDESCRIPTION, description); } } }); setPropertySet(propertySet); // Automatically add columns for all available properties propertySet.getProperties().map(PropertyDefinition::getName) .forEach(this::addColumn); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); if (initial && editor.isOpen()) { // Re-attaching grid. Any old editor should be closed. editor.cancel(); } } /** * Sets the property set to use for this grid. Does not create or update * columns in any way but will delete and re-create the editor. *

* This is only meant to be called from constructors and readDesign, at a * stage where it does not matter if you throw away the editor. * * @param propertySet * the property set to use * * @since 8.0.3 */ protected void setPropertySet(PropertySet propertySet) { Objects.requireNonNull(propertySet, "propertySet cannot be null"); this.propertySet = propertySet; if (editor instanceof Extension) { removeExtension((Extension) editor); } editor = createEditor(); if (editor instanceof Extension) { addExtension((Extension) editor); } } /** * Returns the property set used by this grid. * * @return propertySet the property set to return * @since 8.4 */ protected PropertySet getPropertySet() { return propertySet; } /** * Creates a grid using a custom {@link PropertySet} implementation for * creating a default set of columns and for resolving property names with * {@link #addColumn(String)} and * {@link Column#setEditorComponent(HasValue)}. *

* This functionality is provided as static method instead of as a public * constructor in order to make it possible to use a custom property set * without creating a subclass while still leaving the public constructors * focused on the common use cases. * * @see Grid#Grid() * @see Grid#Grid(Class) * * @param * the grid bean type * @param propertySet * the property set implementation to use, not null. * @return a new grid using the provided property set, not null */ public static Grid withPropertySet( PropertySet propertySet) { return new Grid<>(propertySet); } /** * Creates a new {@code Grid} using the given caption. * * @param caption * the caption of the grid */ public Grid(String caption) { this(); setCaption(caption); } /** * Creates a new {@code Grid} using the given caption and * {@code DataProvider}. * * @param caption * the caption of the grid * @param dataProvider * the data provider, not {@code null} */ public Grid(String caption, DataProvider dataProvider) { this(caption); setDataProvider(dataProvider); } /** * Creates a new {@code Grid} using the given {@code DataProvider}. * * @param dataProvider * the data provider, not {@code null} */ public Grid(DataProvider dataProvider) { this(); setDataProvider(dataProvider); } /** * Creates a new {@code Grid} using the given caption and collection of * items. * * @param caption * the caption of the grid * @param items * the data items to use, not {@çode null} */ public Grid(String caption, Collection items) { this(caption, DataProvider.ofCollection(items)); } /** * Gets the bean type used by this grid. *

* The bean type is used to automatically set up a column added using a * property name. * * @return the used bean type or null if no bean type has been * defined * * @since 8.0.3 */ public Class getBeanType() { return beanType; } /** * Sends a {@link ColumnVisibilityChangeEvent} to all listeners. * * @param * the column value type * @param column * the column that changed its visibility * @param hidden * {@code true} if the column was hidden, {@code false} if it * became visible * @param userOriginated * {@code true} if the event was triggered by an UI interaction, * {@code false} otherwise */ public void fireColumnVisibilityChangeEvent(Column column, boolean hidden, boolean userOriginated) { fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden, userOriginated)); } /** * Adds a new column with the given property name. The column will use a * {@link TextRenderer}. The value is converted to a String using * {@link Object#toString()}. The property name will be used as the * {@link Column#getId() column id} and the {@link Column#getCaption() * column caption} will be set based on the property definition. *

* This method can only be used for a Grid created using * {@link Grid#Grid(Class)} or {@link #withPropertySet(PropertySet)}. *

* You can add columns for nested properties with dot notation, eg. * "property.nestedProperty" * * @param propertyName * the property name of the new column, not null * @return the newly added column, not null */ public Column addColumn(String propertyName) { return addColumn(propertyName, new TextRenderer()); } /** * Adds a new column with the given property name and renderer. The property * name will be used as the {@link Column#getId() column id} and the * {@link Column#getCaption() column caption} will be set based on the * property definition. *

* This method can only be used for a Grid created using * {@link Grid#Grid(Class)} or {@link #withPropertySet(PropertySet)}. *

* You can add columns for nested properties with dot notation, eg. * "property.nestedProperty" * * * @param propertyName * the property name of the new column, not null * @param renderer * the renderer to use, not null * @return the newly added column, not null */ public Column addColumn(String propertyName, AbstractRenderer renderer) { Objects.requireNonNull(propertyName, "Property name cannot be null"); Objects.requireNonNull(renderer, "Renderer cannot be null"); if (getColumn(propertyName) != null) { throw new IllegalStateException( "There is already a column for " + propertyName); } PropertyDefinition definition = propertySet .getProperty(propertyName) .orElseThrow(() -> new IllegalArgumentException( "Could not resolve property name " + propertyName + " from " + propertySet)); if (!renderer.getPresentationType() .isAssignableFrom(definition.getType())) { throw new IllegalArgumentException( renderer + " cannot be used with a property of type " + definition.getType().getName()); } @SuppressWarnings({ "unchecked", "rawtypes" }) Column column = addColumn(definition.getGetter(), (AbstractRenderer) renderer).setId(definition.getName()) .setCaption(definition.getCaption()); return column; } /** * Adds a new column with the given property name and renderer. The property * name will be used as the {@link Column#getId() column id} and the * {@link Column#getCaption() column caption} will be set based on the * property definition. *

* This method can only be used for a Grid created using * {@link Grid#Grid(Class)} or {@link #withPropertySet(PropertySet)}. *

* You can add columns for nested properties with dot notation, eg. * "property.nestedProperty" * * @param propertyName * the property name of the new column, not null * @param renderer * the renderer to use, not null * @param nestedNullBehavior * the behavior when * @return the newly added column, not null * * @since 8.8 */ public Column addColumn(String propertyName, AbstractRenderer renderer, Column.NestedNullBehavior nestedNullBehavior) { Objects.requireNonNull(propertyName, "Property name cannot be null"); Objects.requireNonNull(renderer, "Renderer cannot be null"); if (getColumn(propertyName) != null) { throw new IllegalStateException( "There is already a column for " + propertyName); } PropertyDefinition definition = propertySet .getProperty(propertyName) .orElseThrow(() -> new IllegalArgumentException( "Could not resolve property name " + propertyName + " from " + propertySet)); if (!renderer.getPresentationType() .isAssignableFrom(definition.getType())) { throw new IllegalArgumentException( renderer + " cannot be used with a property of type " + definition.getType().getName()); } @SuppressWarnings({ "unchecked", "rawtypes" }) Column column = createColumn(definition.getGetter(), ValueProvider.identity(), (AbstractRenderer) renderer, nestedNullBehavior); String generatedIdentifier = getGeneratedIdentifier(); addColumn(generatedIdentifier, column); column.setId(definition.getName()).setCaption(definition.getCaption()); return column; } /** * Adds a new text column to this {@link Grid} with a value provider. The * column will use a {@link TextRenderer}. The value is converted to a * String using {@link Object#toString()}. In-memory sorting will use the * natural ordering of elements if they are mutually comparable and * otherwise fall back to comparing the string representations of the * values. * * @param valueProvider * the value provider * @param * the column value type * * @return the new column */ public Column addColumn(ValueProvider valueProvider) { return addColumn(valueProvider, new TextRenderer()); } /** * Adds a new column to this {@link Grid} with typed renderer and value * provider. * * @param valueProvider * the value provider * @param renderer * the column value renderer * @param * the column value type * * @return the new column * * @see AbstractRenderer */ public Column addColumn(ValueProvider valueProvider, AbstractRenderer renderer) { return addColumn(valueProvider, ValueProvider.identity(), renderer); } /** * Adds a new column to this {@link Grid} with value provider and * presentation provider. *

* Note: The presentation type for this method is set to be * String. To use any custom renderer with the presentation provider, use * {@link #addColumn(ValueProvider, ValueProvider, AbstractRenderer)}. * * @param valueProvider * the value provider * @param presentationProvider * the value presentation provider * @param * the column value type * * @see #addColumn(ValueProvider, ValueProvider, AbstractRenderer) * * @return the new column * @since 8.1 */ public Column addColumn(ValueProvider valueProvider, ValueProvider presentationProvider) { return addColumn(valueProvider, presentationProvider, new TextRenderer()); } /** * Adds a new column to this {@link Grid} with value provider, presentation * provider and typed renderer. * *

* The presentation provider is a method that takes the value from the value * provider, and maps that to a value that the renderer accepts. This * feature can be used for storing a complex value in a column for editing, * but providing a simplified presentation for the user when not editing. * * @param valueProvider * the value provider * @param presentationProvider * the value presentation provider * @param renderer * the column value renderer * @param * the column value type * @param

* the column presentation type * * @return the new column * * @see AbstractRenderer * @since 8.1 */ public Column addColumn(ValueProvider valueProvider, ValueProvider presentationProvider, AbstractRenderer renderer) { String generatedIdentifier = getGeneratedIdentifier(); Column column = createColumn(valueProvider, presentationProvider, renderer); addColumn(generatedIdentifier, column); return column; } /** * Adds a column that shows components. *

* This is a shorthand for {@link #addColumn()} with a * {@link ComponentRenderer}. * * @param componentProvider * a value provider that will return a component for the given * item * @return the new column * @param * the column value type, extends component * @since 8.1 */ public Column addComponentColumn( ValueProvider componentProvider) { return addColumn(componentProvider, new ComponentRenderer()); } /** * Creates a column instance from a value provider, presentation provider * and a renderer. * * @param valueProvider * the value provider * @param presentationProvider * the presentation provider * @param renderer * the renderer * @return a new column instance * @param * the column value type * @param

* the column presentation type * * @since 8.1 */ protected Column createColumn( ValueProvider valueProvider, ValueProvider presentationProvider, AbstractRenderer renderer) { return new Column<>(valueProvider, presentationProvider, renderer); } /** * Creates a column instance from a value provider, presentation provider * and a renderer. * * @param valueProvider * the value provider * @param presentationProvider * the presentation provider * @param renderer * the renderer * @param nestedNullBehavior * the behavior when facing nested null values * @return a new column instance * @param * the column value type * @param

* the column presentation type * * @since 8.8 */ private Column createColumn(ValueProvider valueProvider, ValueProvider presentationProvider, AbstractRenderer renderer, Column.NestedNullBehavior nestedNullBehavior) { return new Column<>(valueProvider, presentationProvider, renderer, nestedNullBehavior); } private void addColumn(String identifier, Column column) { if (getColumns().contains(column)) { return; } column.extend(this); columnSet.add(column); columnKeys.put(identifier, column); column.setInternalId(identifier); addDataGenerator(column.getDataGenerator()); getState().columnOrder.add(identifier); getHeader().addColumn(identifier); getFooter().addColumn(identifier); if (getDefaultHeaderRow() != null) { getDefaultHeaderRow().getCell(column).setText(column.getCaption()); } column.updateSortable(); } /** * Removes the given column from this {@link Grid}. * * Note: If you have Editor with binding in this Grid to this property, you * need to remove that using removeBinding method provided by Binder. * * @param column * the column to remove * * @throws IllegalArgumentException * if the column is not a valid one */ public void removeColumn(Column column) { if (columnSet.remove(column)) { String columnId = column.getInternalId(); int displayIndex = getState(false).columnOrder.indexOf(columnId); assert displayIndex != -1 : "Tried to remove a column which is not included in columnOrder. This should not be possible as all columns should be in columnOrder."; columnKeys.remove(columnId); columnIds.remove(column.getId()); column.remove(); removeDataGenerator(column.getDataGenerator()); getHeader().removeColumn(columnId); getFooter().removeColumn(columnId); getState(true).columnOrder.remove(columnId); // Remove column from sorted columns. List> filteredSortOrder = sortOrder.stream() .filter(order -> !order.getSorted().equals(column)) .collect(Collectors.toList()); if (filteredSortOrder.size() < sortOrder.size()) { setSortOrder(filteredSortOrder); } if (displayIndex < getFrozenColumnCount()) { setFrozenColumnCount(getFrozenColumnCount() - 1); } } else { throw new IllegalArgumentException("Column with id " + column.getId() + " cannot be removed from the grid"); } } /** * Removes the column with the given column id. * * @see #removeColumn(Column) * @see Column#setId(String) * * @param columnId * the id of the column to remove, not null */ public void removeColumn(String columnId) { removeColumn(getColumnOrThrow(columnId)); } /** * Removes all columns from this Grid. * * @since 8.0.2 */ public void removeAllColumns() { for (Column column : getColumns()) { removeColumn(column); } } /** * Requests that the column widths should be recalculated. *

* In most cases Grid will know when column widths need to be recalculated * but this method can be used to force recalculation in situations when * grid does not recalculate automatically. * * @since 8.1.1 */ public void recalculateColumnWidths() { getRpcProxy(GridClientRpc.class).recalculateColumnWidths(); } /** * Sets the details component generator. * * @param generator * the generator for details components */ public void setDetailsGenerator(DetailsGenerator generator) { this.detailsManager.setDetailsGenerator(generator); } /** * Sets the visibility of details component for given item. * * @param item * the item to show details for * @param visible * {@code true} if details component should be visible; * {@code false} if it should be hidden */ public void setDetailsVisible(T item, boolean visible) { detailsManager.setDetailsVisible(item, visible); } /** * Returns the visibility of details component for given item. * * @param item * the item to show details for * * @return {@code true} if details component should be visible; * {@code false} if it should be hidden */ public boolean isDetailsVisible(T item) { return detailsManager.isDetailsVisible(item); } /** * Gets an unmodifiable collection of all columns currently in this * {@link Grid}. * * @return unmodifiable collection of columns */ public List> getColumns() { return Collections.unmodifiableList(getState(false).columnOrder.stream() .map(columnKeys::get).collect(Collectors.toList())); } /** * Gets a {@link Column} of this grid by its identifying string. * * When you use the Grid constructor with bean class, the columns are * initialised with columnId being the property name. * * @see Column#setId(String) * * @param columnId * the identifier of the column to get * @return the column corresponding to the given column identifier, or * null if there is no such column */ public Column getColumn(String columnId) { return columnIds.get(columnId); } private Column getColumnOrThrow(String columnId) { Objects.requireNonNull(columnId, "Column id cannot be null"); Column column = getColumn(columnId); if (column == null) { throw new IllegalStateException( "There is no column with the id " + columnId); } return column; } /** * {@inheritDoc} *

* Note that the order of the returned components it not specified. */ @Override public Iterator iterator() { Set componentSet = new LinkedHashSet<>(extensionComponents); Header header = getHeader(); for (int i = 0; i < header.getRowCount(); ++i) { HeaderRow row = header.getRow(i); componentSet.addAll(row.getComponents()); } Footer footer = getFooter(); for (int i = 0; i < footer.getRowCount(); ++i) { FooterRow row = footer.getRow(i); componentSet.addAll(row.getComponents()); } return Collections.unmodifiableSet(componentSet).iterator(); } /** * Sets the number of frozen columns in this grid. Setting the count to 0 * means that no data columns will be frozen, but the built-in selection * checkbox column will still be frozen if it's in use. Setting the count to * -1 will also disable the selection column. *

* NOTE: this count includes {@link Column#isHidden() hidden * columns} in the count. *

* The default value is 0. * * @param numberOfColumns * the number of columns that should be frozen * * @throws IllegalArgumentException * if the column count is less than -1 or greater than the * number of visible columns */ public void setFrozenColumnCount(int numberOfColumns) { if (numberOfColumns < -1 || numberOfColumns > columnSet.size()) { throw new IllegalArgumentException( "count must be between -1 and the current number of columns (" + columnSet.size() + "): " + numberOfColumns); } int currentFrozenColumnState = getState(false).frozenColumnCount; /* * we remove the current value from the state so that setting frozen * columns will always happen after this call. This is so that the value * will be set also in the widget even if it happens to seem to be the * same as this current value we're setting. */ if (currentFrozenColumnState != numberOfColumns) { final String diffStateKey = "frozenColumnCount"; UI ui = getUI(); if (ui != null) { JsonObject diffState = ui.getConnectorTracker() .getDiffState(Grid.this); // if diffState is not present, there's nothing for us to clean if (diffState != null) { diffState.remove(diffStateKey); } } } getState().frozenColumnCount = numberOfColumns; } /** * Gets the number of frozen columns in this grid. 0 means that no data * columns will be frozen, but the built-in selection checkbox column will * still be frozen if it's in use. -1 means that not even the selection * column is frozen. *

* NOTE: this count includes {@link Column#isHidden() hidden * columns} in the count. * * @see #setFrozenColumnCount(int) * * @return the number of frozen columns */ public int getFrozenColumnCount() { return getState(false).frozenColumnCount; } /** * Sets the number of rows that should be visible in Grid's body. This * method will set the height mode to be {@link HeightMode#ROW}. * * @param rows * The height in terms of number of rows displayed in Grid's * body. If Grid doesn't contain enough rows, white space is * displayed instead. * @throws IllegalArgumentException * if {@code rows} is zero or less * @throws IllegalArgumentException * if {@code rows} is {@link Double#isInfinite(double) infinite} * @throws IllegalArgumentException * if {@code rows} is {@link Double#isNaN(double) NaN} */ public void setHeightByRows(double rows) { if (rows <= 0.0d) { throw new IllegalArgumentException( "More than zero rows must be shown."); } if (Double.isInfinite(rows)) { throw new IllegalArgumentException( "Grid doesn't support infinite heights"); } if (Double.isNaN(rows)) { throw new IllegalArgumentException("NaN is not a valid row count"); } getState().heightMode = HeightMode.ROW; getState().heightByRows = rows; } /** * Gets the amount of rows in Grid's body that are shown, while * {@link #getHeightMode()} is {@link HeightMode#ROW}. * * @return the amount of rows that are being shown in Grid's body * @see #setHeightByRows(double) */ public double getHeightByRows() { return getState(false).heightByRows; } /** * {@inheritDoc} *

* Note: This method will set the height mode to be * {@link HeightMode#CSS}. * * @see #setHeightMode(HeightMode) */ @Override public void setHeight(float height, Unit unit) { getState().heightMode = HeightMode.CSS; super.setHeight(height, unit); } /** * Defines the mode in which the Grid widget's height is calculated. *

* If {@link HeightMode#CSS} is given, Grid will respect the values given * via a {@code setHeight}-method, and behave as a traditional Component. *

* If {@link HeightMode#ROW} is given, Grid will make sure that the body * will display as many rows as {@link #getHeightByRows()} defines. * Note: If headers/footers are inserted or removed, the widget * will resize itself to still display the required amount of rows in its * body. It also takes the horizontal scrollbar into account. * * @param heightMode * the mode in to which Grid should be set */ public void setHeightMode(HeightMode heightMode) { /** * This method is a workaround for the fact that Vaadin re-applies * widget dimensions (height/width) on each state change event. The * original design was to have setHeight and setHeightByRow be equals, * and whichever was called the latest was considered in effect. * * But, because of Vaadin always calling setHeight on the widget, this * approach doesn't work. */ getState().heightMode = heightMode; } /** * Returns the current {@link HeightMode} the Grid is in. *

* Defaults to {@link HeightMode#CSS}. * * @return the current HeightMode */ public HeightMode getHeightMode() { return getState(false).heightMode; } /** * Sets the height of body, header and footer rows. If -1 (default), the row * height is calculated based on the theme for an empty row before the Grid * is displayed. *

* Note that all header, body and footer rows get the same height if * explicitly set. In automatic mode, each section is calculated separately * based on an empty row of that type. * * @see #setBodyRowHeight(double) * @see #setHeaderRowHeight(double) * @see #setFooterRowHeight(double) * * @param rowHeight * The height of a row in pixels or -1 for automatic calculation */ public void setRowHeight(double rowHeight) { setBodyRowHeight(rowHeight); setHeaderRowHeight(rowHeight); setFooterRowHeight(rowHeight); } /** * Sets the height of a body row. If -1 (default), the row height is * calculated based on the theme for an empty row before the Grid is * displayed. * * @param rowHeight * The height of a row in pixels or -1 for automatic calculation * @since 8.2 */ public void setBodyRowHeight(double rowHeight) { getState().bodyRowHeight = rowHeight; } /** * Sets the height of a header row. If -1 (default), the row height is * calculated based on the theme for an empty row before the Grid is * displayed. * * @param rowHeight * The height of a row in pixels or -1 for automatic calculation * @since 8.2 */ public void setHeaderRowHeight(double rowHeight) { getState().headerRowHeight = rowHeight; } /** * Sets the height of a footer row. If -1 (default), the row height is * calculated based on the theme for an empty row before the Grid is * displayed. * * @param rowHeight * The height of a row in pixels or -1 for automatic calculation * @since 8.2 */ public void setFooterRowHeight(double rowHeight) { getState().footerRowHeight = rowHeight; } /** * Returns the current body row height.-1 if row height is in automatic * calculation mode. * * @see #getBodyRowHeight() * @see #getHeaderRowHeight() * @see #getFooterRowHeight() * * @return body row height * @deprecated replaced by three separate row height controls */ @Deprecated public double getRowHeight() { return getBodyRowHeight(); } /** * Returns the current body row height. -1 if row height is in automatic * calculation mode. * * @return body row height * @since 8.2 */ public double getBodyRowHeight() { return getState(false).bodyRowHeight; } /** * Returns the current header row height. -1 if row height is in automatic * calculation mode. * * @return header row height * @since 8.2 */ public double getHeaderRowHeight() { return getState(false).headerRowHeight; } /** * Returns the current footer row height. -1 if row height is in automatic * calculation mode. * * @return footer row height * @since 8.2 */ public double getFooterRowHeight() { return getState(false).footerRowHeight; } /** * Sets the style generator that is used for generating class names for rows * in this grid. Returning null from the generator results in no custom * style name being set. * * Note: The style generator is applied only to the body cells, not to the * Editor. * * @see StyleGenerator * * @param styleGenerator * the row style generator to set, not null * @throws NullPointerException * if {@code styleGenerator} is {@code null} */ public void setStyleGenerator(StyleGenerator styleGenerator) { Objects.requireNonNull(styleGenerator, "Style generator must not be null"); this.styleGenerator = styleGenerator; getDataCommunicator().reset(); } /** * Gets the style generator that is used for generating class names for * rows. * * @see StyleGenerator * * @return the row style generator */ public StyleGenerator getStyleGenerator() { return styleGenerator; } /** * Sets the description generator that is used for generating descriptions * for rows. This method uses the {@link ContentMode#PREFORMATTED} content * mode. * * @see #setDescriptionGenerator(DescriptionGenerator, ContentMode) * * @param descriptionGenerator * the row description generator to set, or null to * remove a previously set generator */ public void setDescriptionGenerator( DescriptionGenerator descriptionGenerator) { setDescriptionGenerator(descriptionGenerator, ContentMode.PREFORMATTED); } /** * Sets the description generator that is used for generating descriptions * for rows. This method uses the given content mode. * * @see #setDescriptionGenerator(DescriptionGenerator) * * @param descriptionGenerator * the row description generator to set, or {@code null} to * remove a previously set generator * @param contentMode * the content mode for row tooltips * * @since 8.2 */ public void setDescriptionGenerator( DescriptionGenerator descriptionGenerator, ContentMode contentMode) { Objects.requireNonNull(contentMode, "contentMode cannot be null"); this.descriptionGenerator = descriptionGenerator; getState().rowDescriptionContentMode = contentMode; getDataCommunicator().reset(); } /** * Gets the description generator that is used for generating descriptions * for rows. * * @return the row description generator, or null if no * generator is set */ public DescriptionGenerator getDescriptionGenerator() { return descriptionGenerator; } // // HEADER AND FOOTER // /** * Returns the header row at the given index. * * @param index * the index of the row, where the topmost row has index zero * @return the header row at the index * @throws IndexOutOfBoundsException * if {@code rowIndex < 0 || rowIndex >= getHeaderRowCount()} */ public HeaderRow getHeaderRow(int index) { return getHeader().getRow(index); } /** * Gets the number of rows in the header section. * * @return the number of header rows */ public int getHeaderRowCount() { return header.getRowCount(); } /** * Inserts a new row at the given position to the header section. Shifts the * row currently at that position and any subsequent rows down (adds one to * their indices). Inserting at {@link #getHeaderRowCount()} appends the row * at the bottom of the header. * * @param index * the index at which to insert the row, where the topmost row * has index zero * @return the inserted header row * * @throws IndexOutOfBoundsException * if {@code rowIndex < 0 || rowIndex > getHeaderRowCount()} * * @see #appendHeaderRow() * @see #prependHeaderRow() * @see #removeHeaderRow(HeaderRow) * @see #removeHeaderRow(int) */ public HeaderRow addHeaderRowAt(int index) { return getHeader().addRowAt(index); } /** * Adds a new row at the bottom of the header section. * * @return the appended header row * * @see #prependHeaderRow() * @see #addHeaderRowAt(int) * @see #removeHeaderRow(HeaderRow) * @see #removeHeaderRow(int) */ public HeaderRow appendHeaderRow() { return addHeaderRowAt(getHeaderRowCount()); } /** * Adds a new row at the top of the header section. * * @return the prepended header row * * @see #appendHeaderRow() * @see #addHeaderRowAt(int) * @see #removeHeaderRow(HeaderRow) * @see #removeHeaderRow(int) */ public HeaderRow prependHeaderRow() { return addHeaderRowAt(0); } /** * Removes the given row from the header section. Removing a default row * sets the Grid to have no default row. * * @param row * the header row to be removed, not null * * @throws IllegalArgumentException * if the header does not contain the row * * @see #removeHeaderRow(int) * @see #addHeaderRowAt(int) * @see #appendHeaderRow() * @see #prependHeaderRow() */ public void removeHeaderRow(HeaderRow row) { getHeader().removeRow(row); } /** * Removes the row at the given position from the header section. * * @param index * the index of the row to remove, where the topmost row has * index zero * * @throws IndexOutOfBoundsException * if {@code index < 0 || index >= getHeaderRowCount()} * * @see #removeHeaderRow(HeaderRow) * @see #addHeaderRowAt(int) * @see #appendHeaderRow() * @see #prependHeaderRow() */ public void removeHeaderRow(int index) { getHeader().removeRow(index); } /** * Sets the visibility of the Header in this Grid. * * @param headerVisible * {@code true} if visible; {@code false} if not * * @since 8.1.1 */ public void setHeaderVisible(boolean headerVisible) { getHeader().setVisible(headerVisible); } /** * Gets the visibility of the Header in this Grid. * * @return {@code true} if visible; {@code false} if not * * @since 8.1.1 */ public boolean isHeaderVisible() { return getHeader().isVisible(); } /** * Returns the current default row of the header. * * @return the default row or null if no default row set * * @see #setDefaultHeaderRow(HeaderRow) */ public HeaderRow getDefaultHeaderRow() { return header.getDefaultRow(); } /** * Sets the default row of the header. The default row is a special header * row that displays column captions and sort indicators. By default Grid * has a single row which is also the default row. When a header row is set * as the default row, any existing cell content is replaced by the column * captions. * * @param row * the new default row, or null for no default row * * @throws IllegalArgumentException * if the header does not contain the row */ public void setDefaultHeaderRow(HeaderRow row) { header.setDefaultRow((Row) row); } /** * Returns the header section of this grid. The default header contains a * single row, set as the {@linkplain #setDefaultHeaderRow(HeaderRow) * default row}. * * @return the header section */ protected Header getHeader() { return header; } /** * Returns the footer row at the given index. * * @param index * the index of the row, where the topmost row has index zero * @return the footer row at the index * @throws IndexOutOfBoundsException * if {@code rowIndex < 0 || rowIndex >= getFooterRowCount()} */ public FooterRow getFooterRow(int index) { return getFooter().getRow(index); } /** * Gets the number of rows in the footer section. * * @return the number of footer rows */ public int getFooterRowCount() { return getFooter().getRowCount(); } /** * Inserts a new row at the given position to the footer section. Shifts the * row currently at that position and any subsequent rows down (adds one to * their indices). Inserting at {@link #getFooterRowCount()} appends the row * at the bottom of the footer. * * @param index * the index at which to insert the row, where the topmost row * has index zero * @return the inserted footer row * * @throws IndexOutOfBoundsException * if {@code rowIndex < 0 || rowIndex > getFooterRowCount()} * * @see #appendFooterRow() * @see #prependFooterRow() * @see #removeFooterRow(FooterRow) * @see #removeFooterRow(int) */ public FooterRow addFooterRowAt(int index) { return getFooter().addRowAt(index); } /** * Adds a new row at the bottom of the footer section. * * @return the appended footer row * * @see #prependFooterRow() * @see #addFooterRowAt(int) * @see #removeFooterRow(FooterRow) * @see #removeFooterRow(int) */ public FooterRow appendFooterRow() { return addFooterRowAt(getFooterRowCount()); } /** * Adds a new row at the top of the footer section. * * @return the prepended footer row * * @see #appendFooterRow() * @see #addFooterRowAt(int) * @see #removeFooterRow(FooterRow) * @see #removeFooterRow(int) */ public FooterRow prependFooterRow() { return addFooterRowAt(0); } /** * Removes the given row from the footer section. Removing a default row * sets the Grid to have no default row. * * @param row * the footer row to be removed, not null * * @throws IllegalArgumentException * if the footer does not contain the row * * @see #removeFooterRow(int) * @see #addFooterRowAt(int) * @see #appendFooterRow() * @see #prependFooterRow() */ public void removeFooterRow(FooterRow row) { getFooter().removeRow(row); } /** * Removes the row at the given position from the footer section. * * @param index * the index of the row to remove, where the topmost row has * index zero * * @throws IndexOutOfBoundsException * if {@code index < 0 || index >= getFooterRowCount()} * * @see #removeFooterRow(FooterRow) * @see #addFooterRowAt(int) * @see #appendFooterRow() * @see #prependFooterRow() */ public void removeFooterRow(int index) { getFooter().removeRow(index); } /** * Sets the visibility of the Footer in this Grid. * * @param footerVisible * {@code true} if visible; {@code false} if not * * @since 8.1.1 */ public void setFooterVisible(boolean footerVisible) { getFooter().setVisible(footerVisible); } /** * Gets the visibility of the Footer in this Grid. * * @return {@code true} if visible; {@code false} if not * * @since 8.1.1 */ public boolean isFooterVisible() { return getFooter().isVisible(); } /** * Returns the footer section of this grid. * * @return the footer section */ protected Footer getFooter() { return footer; } /** * Registers a new column reorder listener. * * @param listener * the listener to register, not null * @return a registration for the listener */ public Registration addColumnReorderListener( ColumnReorderListener listener) { return addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD); } /** * Registers a new column resize listener. * * @param listener * the listener to register, not null * @return a registration for the listener */ public Registration addColumnResizeListener(ColumnResizeListener listener) { return addListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD); } /** * Adds an item click listener. The listener is called when an item of this * {@code Grid} is clicked. * * @param listener * the item click listener, not null * @return a registration for the listener * @see #addContextClickListener */ public Registration addItemClickListener( ItemClickListener listener) { return addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClick.class, listener, ITEM_CLICK_METHOD); } /** * Adds a context click listener that gets notified when a context click * happens. * * @param listener * the context click listener to add, not null actual event * provided to the listener is {@link GridContextClickEvent} * @return a registration object for removing the listener * * @since 8.1 * @see #addItemClickListener * @see Registration */ @Override public Registration addContextClickListener( ContextClickEvent.ContextClickListener listener) { return super.addContextClickListener(listener); } /** * Registers a new column visibility change listener. * * @param listener * the listener to register, not null * @return a registration for the listener */ public Registration addColumnVisibilityChangeListener( ColumnVisibilityChangeListener listener) { return addListener(ColumnVisibilityChangeEvent.class, listener, COLUMN_VISIBILITY_METHOD); } /** * Returns whether column reordering is allowed. Default value is * false. * * @return true if reordering is allowed */ public boolean isColumnReorderingAllowed() { return getState(false).columnReorderingAllowed; } /** * Sets whether or not column reordering is allowed. Default value is * false. * * @param columnReorderingAllowed * specifies whether column reordering is allowed */ public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { if (isColumnReorderingAllowed() != columnReorderingAllowed) { getState().columnReorderingAllowed = columnReorderingAllowed; } } /** * Sets the columns and their order based on their column ids. Columns * currently in this grid that are not present in the list of column ids are * removed. This includes any column that has no id. Similarly, any new * column in columns will be added to this grid. New columns can only be * added for a Grid created using {@link Grid#Grid(Class)} or * {@link #withPropertySet(PropertySet)}. * * * @param columnIds * the column ids to set * * @see Column#setId(String) */ @SuppressWarnings("unchecked") public void setColumns(String... columnIds) { // Must extract to an explicitly typed variable because otherwise javac // cannot determine which overload of setColumnOrder to use Column[] newColumnOrder = Stream.of(columnIds) .map((Function>) id -> { Column column = getColumn(id); if (column == null) { column = addColumn(id); } return column; }).toArray(Column[]::new); setColumnOrder(newColumnOrder); // The columns to remove are now at the end of the column list getColumns().stream().skip(columnIds.length) .forEach(this::removeColumn); } /** * Sets the columns and their order based on their column ids provided that * collection supports preserving of the order. Columns currently in this * grid that are not present in the collection of column ids are removed. * This includes any column that has no id. Similarly, any new column in * columns will be added to this grid. New columns can only be added for a * Grid created using {@link Grid#Grid(Class)} or * {@link #withPropertySet(PropertySet)}. * * * @param columnIds * the column ids to set * * @see Column#setId(String) * @see #setColumns(String...) */ public void setColumns(Collection columnIds) { Objects.requireNonNull(columnIds, "columnIds can't be null"); String[] columns = columnIds.toArray(new String[columnIds.size()]); setColumns(columns); } private String getGeneratedIdentifier() { String columnId = "" + counter; counter++; return columnId; } /** * Sets a new column order for the grid. All columns which are not ordered * here will remain in the order they were before as the last columns of * grid. * * @param columns * the columns in the order they should be */ @SuppressWarnings("unchecked") public void setColumnOrder(Column... columns) { setColumnOrder(Stream.of(columns)); } private void setColumnOrder(Stream> columns) { List columnOrder = new ArrayList<>(); columns.forEach(column -> { if (columnSet.contains(column)) { columnOrder.add(column.getInternalId()); } else { throw new IllegalStateException( "setColumnOrder should not be called " + "with columns that are not in the grid."); } }); List stateColumnOrder = getState().columnOrder; if (stateColumnOrder.size() != columnOrder.size()) { stateColumnOrder.removeAll(columnOrder); columnOrder.addAll(stateColumnOrder); } getState().columnOrder = columnOrder; fireColumnReorderEvent(false); } /** * Sets a new column order for the grid based on their column ids. All * columns which are not ordered here will remain in the order they were * before as the last columns of grid. * * @param columnIds * the column ids in the order they should be * * @see Column#setId(String) */ public void setColumnOrder(String... columnIds) { setColumnOrder(Stream.of(columnIds).map(this::getColumnOrThrow)); } /** * Returns the selection model for this grid. * * @return the selection model, not null */ public GridSelectionModel getSelectionModel() { assert selectionModel != null : "No selection model set by " + getClass().getName() + " constructor"; return selectionModel; } /** * Use this grid as a single select in {@link Binder}. *

* Throws {@link IllegalStateException} if the grid is not using a * {@link SingleSelectionModel}. * * @return the single select wrapper that can be used in binder * @throws IllegalStateException * if not using a single selection model */ public GridSingleSelect asSingleSelect() { return new GridSingleSelect<>(this); } /** * Returns the {@link Editor} for this grid. * * @return the editor, not null */ public Editor getEditor() { return editor; } /** * User this grid as a multiselect in {@link Binder}. *

* Throws {@link IllegalStateException} if the grid is not using a * {@link MultiSelectionModel}. * * @return the multiselect wrapper that can be used in binder * @throws IllegalStateException * if not using a multiselection model */ public GridMultiSelect asMultiSelect() { return new GridMultiSelect<>(this); } /** * Sets the selection model for the grid. *

* This method is for setting a custom selection model, and is * {@code protected} because {@link #setSelectionMode(SelectionMode)} should * be used for easy switching between built-in selection models. *

* The default selection model is {@link SingleSelectionModelImpl}. *

* To use a custom selection model, you can e.g. extend the grid call this * method with your custom selection model. * * @param model * the selection model to use, not {@code null} * * @see #setSelectionMode(SelectionMode) */ @SuppressWarnings("unchecked") protected void setSelectionModel(GridSelectionModel model) { Objects.requireNonNull(model, "selection model cannot be null"); if (selectionModel != null) { // null when called from constructor selectionModel.remove(); } selectionModel = model; if (selectionModel instanceof AbstractListingExtension) { ((AbstractListingExtension) selectionModel).extend(this); } else { addExtension(selectionModel); } } /** * Sets the grid's selection mode. *

* The built-in selection models are: *

    *
  • {@link SelectionMode#SINGLE} -> {@link SingleSelectionModelImpl}, * the default model
  • *
  • {@link SelectionMode#MULTI} -> {@link MultiSelectionModelImpl}, with * checkboxes in the first column for selection
  • *
  • {@link SelectionMode#NONE} -> {@link NoSelectionModel}, preventing * selection
  • *
*

* To use your custom selection model, you can use * {@link #setSelectionModel(GridSelectionModel)}, see existing selection * model implementations for example. * * @param selectionMode * the selection mode to switch to, not {@code null} * @return the used selection model * * @see SelectionMode * @see GridSelectionModel * @see #setSelectionModel(GridSelectionModel) */ public GridSelectionModel setSelectionMode(SelectionMode selectionMode) { Objects.requireNonNull(selectionMode, "Selection mode cannot be null."); GridSelectionModel model = selectionMode.createModel(); setSelectionModel(model); return model; } /** * This method is a shorthand that delegates to the currently set selection * model. * * @see #getSelectionModel() * @see GridSelectionModel * @return the items in the current selection, not null */ public Set getSelectedItems() { return getSelectionModel().getSelectedItems(); } /** * This method is a shorthand that delegates to the currently set selection * model. * * @see #getSelectionModel() * @see GridSelectionModel * @param item * the item to select, not null */ public void select(T item) { getSelectionModel().select(item); } /** * This method is a shorthand that delegates to the currently set selection * model. * * @see #getSelectionModel() * @see GridSelectionModel * @param item * the item to deselect, not null */ public void deselect(T item) { getSelectionModel().deselect(item); } /** * This method is a shorthand that delegates to the currently set selection * model. * * @see #getSelectionModel() * @see GridSelectionModel */ public void deselectAll() { getSelectionModel().deselectAll(); } /** * Adds a selection listener to the current selection model. *

* NOTE: If selection mode is switched with * {@link #setSelectionMode(SelectionMode)}, then this listener is not * triggered anymore when selection changes! *

* This is a shorthand for * {@code grid.getSelectionModel().addSelectionListener()}. To get more * detailed selection events, use {@link #getSelectionModel()} and either * {@link SingleSelectionModel#addSingleSelectionListener(SingleSelectionListener)} * or * {@link MultiSelectionModel#addMultiSelectionListener(MultiSelectionListener)} * depending on the used selection mode. * * @param listener * the listener to add * @return a registration handle to remove the listener * @throws UnsupportedOperationException * if selection has been disabled with * {@link SelectionMode#NONE} */ public Registration addSelectionListener(SelectionListener listener) throws UnsupportedOperationException { return getSelectionModel().addSelectionListener(listener); } /** * Sort this Grid in ascending order by a specified column. * * @param column * a column to sort against * */ public void sort(Column column) { sort(column, SortDirection.ASCENDING); } /** * Sort this Grid in user-specified direction by a column. * * @param column * a column to sort against * @param direction * a sort order value (ascending/descending) * */ public void sort(Column column, SortDirection direction) { setSortOrder(Collections .singletonList(new GridSortOrder<>(column, direction))); } /** * Sort this Grid in ascending order by a specified column defined by id. * * @param columnId * the id of the column to sort against * * @see Column#setId(String) */ public void sort(String columnId) { sort(columnId, SortDirection.ASCENDING); } /** * Sort this Grid in a user-specified direction by a column defined by id. * * @param columnId * the id of the column to sort against * @param direction * a sort order value (ascending/descending) * * @see Column#setId(String) */ public void sort(String columnId, SortDirection direction) { sort(getColumnOrThrow(columnId), direction); } /** * Clear the current sort order, and re-sort the grid. */ public void clearSortOrder() { setSortOrder(Collections.emptyList()); } /** * Sets the sort order to use. * * @param order * a sort order list. * * @throws IllegalArgumentException * if order is null */ public void setSortOrder(List> order) { setSortOrder(order, false); } /** * Sets the sort order to use, given a {@link GridSortOrderBuilder}. * Shorthand for {@code setSortOrder(builder.build())}. * * @see GridSortOrderBuilder * * @param builder * the sort builder to retrieve the sort order from * @throws NullPointerException * if builder is null */ public void setSortOrder(GridSortOrderBuilder builder) { Objects.requireNonNull(builder, "Sort builder cannot be null"); setSortOrder(builder.build()); } /** * Adds a sort order change listener that gets notified when the sort order * changes. * * @param listener * the sort order change listener to add */ @Override public Registration addSortListener( SortListener> listener) { return addListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD); } /** * Get the current sort order list. * * @return a sort order list */ public List> getSortOrder() { return Collections.unmodifiableList(sortOrder); } /** * Scrolls to a certain item, using {@link ScrollDestination#ANY}. *

* If the item has an open details row, its size will also be taken into * account. * * @param row * zero based index of the item to scroll to in the current view. * @throws IllegalArgumentException * if the provided row is outside the item range */ public void scrollTo(int row) throws IllegalArgumentException { scrollTo(row, ScrollDestination.ANY); } /** * Scrolls to a certain item, using user-specified scroll destination. *

* If the item has an open details row, its size will also be taken into * account. * * @param row * zero based index of the item to scroll to in the current view. * @param destination * value specifying desired position of scrolled-to row, not * {@code null} * @throws IllegalArgumentException * if the provided row is outside the item range */ public void scrollTo(int row, ScrollDestination destination) { Objects.requireNonNull(destination, "ScrollDestination can not be null"); if (row >= getDataCommunicator().getDataProviderSize()) { throw new IllegalArgumentException("Row outside dataProvider size"); } getRpcProxy(GridClientRpc.class).scrollToRow(row, destination); } /** * Scrolls to the beginning of the first data row. */ public void scrollToStart() { getRpcProxy(GridClientRpc.class).scrollToStart(); } /** * Scrolls to the end of the last data row. */ public void scrollToEnd() { getRpcProxy(GridClientRpc.class).scrollToEnd(); } @Override protected GridState getState() { return getState(true); } @Override protected GridState getState(boolean markAsDirty) { return (GridState) super.getState(markAsDirty); } /** * Sets the column resize mode to use. The default mode is * {@link ColumnResizeMode#ANIMATED}. * * @param mode * a ColumnResizeMode value * @since 7.7.5 */ public void setColumnResizeMode(ColumnResizeMode mode) { getState().columnResizeMode = mode; } /** * Returns the current column resize mode. The default mode is * {@link ColumnResizeMode#ANIMATED}. * * @return a ColumnResizeMode value * @since 7.7.5 */ public ColumnResizeMode getColumnResizeMode() { return getState(false).columnResizeMode; } /** * Creates a new Editor instance. Can be overridden to create a custom * Editor. If the Editor is a {@link AbstractGridExtension}, it will be * automatically added to {@link DataCommunicator}. * * @return editor */ protected Editor createEditor() { return new EditorImpl<>(propertySet); } private void addExtensionComponent(Component c) { if (extensionComponents.add(c)) { c.setParent(this); markAsDirty(); } } private void removeExtensionComponent(Component c) { if (extensionComponents.remove(c)) { c.setParent(null); markAsDirty(); } } private void fireColumnReorderEvent(boolean userOriginated) { fireEvent(new ColumnReorderEvent(this, userOriginated)); } private void fireColumnResizeEvent(Column column, boolean userOriginated) { fireEvent(new ColumnResizeEvent(this, column, userOriginated)); } @Override protected void readItems(Element design, DesignContext context) { // Grid handles reading of items in Grid#readData } @Override public DataProvider getDataProvider() { return internalGetDataProvider(); } @Override public void setDataProvider(DataProvider dataProvider) { internalSetDataProvider(dataProvider); } /** * Sets a CallbackDataProvider using the given fetch items callback and a * size callback. *

* This method is a shorthand for making a {@link CallbackDataProvider} that * handles a partial {@link Query} object. * * @param fetchItems * a callback for fetching items * @param sizeCallback * a callback for getting the count of items * * @see CallbackDataProvider * @see #setDataProvider(DataProvider) */ public void setDataProvider(FetchItemsCallback fetchItems, SerializableSupplier sizeCallback) { internalSetDataProvider( new CallbackDataProvider<>( q -> fetchItems.fetchItems(q.getSortOrders(), q.getOffset(), q.getLimit()), q -> sizeCallback.get())); } @Override protected void doReadDesign(Element design, DesignContext context) { Attributes attrs = design.attributes(); if (design.hasAttr(DECLARATIVE_DATA_ITEM_TYPE)) { String itemType = design.attr(DECLARATIVE_DATA_ITEM_TYPE); setBeanType(itemType); } if (attrs.hasKey("selection-mode")) { setSelectionMode(DesignAttributeHandler.readAttribute( "selection-mode", attrs, SelectionMode.class)); } Attributes attr = design.attributes(); if (attr.hasKey("selection-allowed")) { setReadOnly(DesignAttributeHandler .readAttribute("selection-allowed", attr, Boolean.class)); } if (attrs.hasKey("rows")) { setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs, double.class)); } readStructure(design, context); // Read frozen columns after columns are read. if (attrs.hasKey("frozen-columns")) { setFrozenColumnCount(DesignAttributeHandler .readAttribute("frozen-columns", attrs, int.class)); } } /** * Sets the bean type to use for property mapping. *

* This method is responsible also for setting or updating the property set * so that it matches the given bean type. *

* Protected mostly for Designer needs, typically should not be overridden * or even called. * * @param beanTypeClassName * the fully qualified class name of the bean type * * @since 8.0.3 */ @SuppressWarnings("unchecked") protected void setBeanType(String beanTypeClassName) { setBeanType((Class) resolveClass(beanTypeClassName)); } /** * Sets the bean type to use for property mapping. *

* This method is responsible also for setting or updating the property set * so that it matches the given bean type. *

* Protected mostly for Designer needs, typically should not be overridden * or even called. * * @param beanType * the bean type class * * @since 8.0.3 */ protected void setBeanType(Class beanType) { this.beanType = beanType; setPropertySet(BeanPropertySet.get(beanType)); } private Class resolveClass(String qualifiedClassName) { try { Class resolvedClass = Class.forName(qualifiedClassName, true, VaadinServiceClassLoaderUtil.findDefaultClassLoader()); return resolvedClass; } catch (ClassNotFoundException | SecurityException e) { throw new IllegalArgumentException( "Unable to find class " + qualifiedClassName, e); } } @Override protected void doWriteDesign(Element design, DesignContext designContext) { Attributes attr = design.attributes(); if (this.beanType != null) { design.attr(DECLARATIVE_DATA_ITEM_TYPE, this.beanType.getCanonicalName()); } DesignAttributeHandler.writeAttribute("selection-allowed", attr, isReadOnly(), false, Boolean.class, designContext); Attributes attrs = design.attributes(); Grid defaultInstance = designContext.getDefaultInstance(this); DesignAttributeHandler.writeAttribute("frozen-columns", attrs, getFrozenColumnCount(), defaultInstance.getFrozenColumnCount(), int.class, designContext); if (HeightMode.ROW.equals(getHeightMode())) { DesignAttributeHandler.writeAttribute("rows", attrs, getHeightByRows(), defaultInstance.getHeightByRows(), double.class, designContext); } SelectionMode mode = getSelectionMode(); if (mode != null) { DesignAttributeHandler.writeAttribute("selection-mode", attrs, mode, SelectionMode.SINGLE, SelectionMode.class, designContext); } writeStructure(design, designContext); } @Override protected T deserializeDeclarativeRepresentation(String item) { if (item == null) { return super.deserializeDeclarativeRepresentation( UUID.randomUUID().toString()); } return super.deserializeDeclarativeRepresentation(new String(item)); } @Override protected boolean isReadOnly() { SelectionMode selectionMode = getSelectionMode(); if (SelectionMode.SINGLE.equals(selectionMode)) { return asSingleSelect().isReadOnly(); } if (SelectionMode.MULTI.equals(selectionMode)) { return asMultiSelect().isReadOnly(); } return false; } @Override protected void setReadOnly(boolean readOnly) { SelectionMode selectionMode = getSelectionMode(); if (SelectionMode.SINGLE.equals(selectionMode)) { asSingleSelect().setReadOnly(readOnly); } else if (SelectionMode.MULTI.equals(selectionMode)) { asMultiSelect().setReadOnly(readOnly); } } private void readStructure(Element design, DesignContext context) { if (design.children().isEmpty()) { return; } if (design.children().size() > 1 || !design.child(0).tagName().equals("table")) { throw new DesignException( "Grid needs to have a table element as its only child"); } Element table = design.child(0); Elements colgroups = table.getElementsByTag("colgroup"); if (colgroups.size() != 1) { throw new DesignException( "Table element in declarative Grid needs to have a" + " colgroup defining the columns used in Grid"); } List> providers = new ArrayList<>(); for (Element col : colgroups.get(0).getElementsByTag("col")) { String id = DesignAttributeHandler.readAttribute("column-id", col.attributes(), null, String.class); // If there is a property with a matching name available, // map to that Optional> property = propertySet .getProperties().filter(p -> p.getName().equals(id)) .findFirst(); Column column; if (property.isPresent()) { column = addColumn(id); } else { DeclarativeValueProvider provider = new DeclarativeValueProvider<>(); column = createColumn(provider, ValueProvider.identity(), new HtmlRenderer()); addColumn(getGeneratedIdentifier(), column); if (id != null) { column.setId(id); } providers.add(provider); } column.readDesign(col, context); } for (Element child : table.children()) { if (child.tagName().equals("thead")) { getHeader().readDesign(child, context); } else if (child.tagName().equals("tbody")) { readData(child, providers); } else if (child.tagName().equals("tfoot")) { getFooter().readDesign(child, context); } } // Sync default header captions to column captions if (getDefaultHeaderRow() != null) { for (Column c : getColumns()) { HeaderCell headerCell = getDefaultHeaderRow().getCell(c); if (headerCell.getCellType() == GridStaticCellType.TEXT) { String text = headerCell.getText(); c.setCaption(text == null ? "" : text); } } } } /** * Reads the declarative representation of a grid's data from the given * element and stores it in the given {@link DeclarativeValueProvider}s. * Each member in the list of value providers corresponds to a column in the * grid. * * @since 8.1 * * @param body * the element to read data from * @param providers * list of {@link DeclarativeValueProvider}s to store the data of * each column to */ protected void readData(Element body, List> providers) { getSelectionModel().deselectAll(); List items = new ArrayList<>(); List selectedItems = new ArrayList<>(); for (Element row : body.children()) { T item = deserializeDeclarativeRepresentation(row.attr("item")); items.add(item); if (row.hasAttr("selected")) { selectedItems.add(item); } Elements cells = row.children(); int i = 0; for (Element cell : cells) { providers.get(i).addValue(item, cell.html()); i++; } } setItems(items); selectedItems.forEach(getSelectionModel()::select); } private void writeStructure(Element design, DesignContext designContext) { if (getColumns().isEmpty()) { return; } Element tableElement = design.appendElement("table"); Element colGroup = tableElement.appendElement("colgroup"); getColumns().forEach(column -> column .writeDesign(colGroup.appendElement("col"), designContext)); // Always write thead. Reads correctly when there no header rows getHeader().writeDesign(tableElement.appendElement("thead"), designContext); if (designContext.shouldWriteData(this)) { Element bodyElement = tableElement.appendElement("tbody"); writeData(bodyElement, designContext); } if (getFooter().getRowCount() > 0) { getFooter().writeDesign(tableElement.appendElement("tfoot"), designContext); } } /** * Writes the data contained in this grid. Used when serializing a grid to * its declarative representation, if * {@link DesignContext#shouldWriteData(Component)} returns {@code true} for * the grid that is being written. * * @since 8.1 * * @param body * the body element to write the declarative representation of * data to * @param designContext * the design context * * @since 8.1 */ protected void writeData(Element body, DesignContext designContext) { getDataProvider().fetch(new Query<>()) .forEach(item -> writeRow(body, item)); } private void writeRow(Element container, T item) { Element tableRow = container.appendElement("tr"); tableRow.attr("item", serializeDeclarativeRepresentation(item)); if (getSelectionModel().isSelected(item)) { tableRow.attr("selected", true); } for (Column column : getColumns()) { Object value = column.valueProvider.apply(item); tableRow.appendElement("td") .append(Optional.ofNullable(value).map(Object::toString) .map(DesignFormatter::encodeForTextNode) .orElse("")); } } private SelectionMode getSelectionMode() { GridSelectionModel selectionModel = getSelectionModel(); SelectionMode mode = null; if (selectionModel.getClass().equals(SingleSelectionModelImpl.class)) { mode = SelectionMode.SINGLE; } else if (selectionModel.getClass() .equals(MultiSelectionModelImpl.class)) { mode = SelectionMode.MULTI; } else if (selectionModel.getClass().equals(NoSelectionModel.class)) { mode = SelectionMode.NONE; } return mode; } /** * Sets a user-defined identifier for given column. * * @see Column#setId(String) * * @param column * the column * @param id * the user-defined identifier */ protected void setColumnId(String id, Column column) { if (columnIds.containsKey(id)) { throw new IllegalArgumentException("Duplicate ID for columns"); } columnIds.put(id, column); } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); // "rename" for frozen column count result.add("frozen-column-count"); result.add("frozen-columns"); // "rename" for height-mode result.add("height-by-rows"); result.add("rows"); // add a selection-mode attribute result.add("selection-mode"); return result; } /** * Returns a column identified by its internal id. This id should not be * confused with the user-defined identifier. * * @param columnId * the internal id of column * @return column identified by internal id */ protected Column getColumnByInternalId(String columnId) { return columnKeys.get(columnId); } /** * Returns the internal id for given column. This id should not be confused * with the user-defined identifier. * * @param column * the column * @return internal id of given column */ protected String getInternalIdForColumn(Column column) { return column.getInternalId(); } private void setSortOrder(List> order, boolean userOriginated) { Objects.requireNonNull(order, "Sort order list cannot be null"); // Update client state to display sort order. List sortColumns = new ArrayList<>(); List directions = new ArrayList<>(); order.stream().forEach(sortOrder -> { sortColumns.add(sortOrder.getSorted().getInternalId()); directions.add(sortOrder.getDirection()); }); getState().sortColumns = sortColumns.toArray(new String[0]); getState().sortDirs = directions.toArray(new SortDirection[0]); sortOrder.clear(); sortOrder.addAll(order); sort(userOriginated); } private void sort(boolean userOriginated) { // Set sort orders // In-memory comparator getDataCommunicator().setInMemorySorting(createSortingComparator(), false); // Back-end sort properties List sortProperties = new ArrayList<>(); sortOrder.stream().map( order -> order.getSorted().getSortOrder(order.getDirection())) .forEach(s -> s.forEach(sortProperties::add)); getDataCommunicator().setBackEndSorting(sortProperties, true); // Close grid editor if it's open. if (getEditor().isOpen()) { getEditor().cancel(); } fireEvent(new SortEvent<>(this, new ArrayList<>(sortOrder), userOriginated)); } /** * Creates a comparator for grid to sort rows. * * @return the comparator based on column sorting information. */ protected SerializableComparator createSortingComparator() { /* * thenComparing is defined to return a serializable comparator as long * as both original comparators are also serializable */ BinaryOperator> operator = (comparator1, comparator2) -> comparator1.thenComparing(comparator2)::compare; return sortOrder.stream().map( order -> order.getSorted().getComparator(order.getDirection())) .reduce((x, y) -> 0, operator); } @Override protected void internalSetDataProvider(DataProvider dataProvider) { boolean newProvider = getDataProvider() != dataProvider; super.internalSetDataProvider(dataProvider); if (newProvider) { Set oldVisibleDetails = new HashSet<>( detailsManager.visibleDetails); oldVisibleDetails.forEach(item -> { // close all old details even if the same item exists in the new // provider detailsManager.setDetailsVisible(item, false); }); } for (Column column : getColumns()) { column.updateSortable(); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 54718 Content-Disposition: inline; filename="GridLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "66ce5ee96756723d76fd4d47fcd562847e8c08ef" /* * Copyright 2000-2022 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.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.gridlayout.GridLayoutServerRpc; import com.vaadin.shared.ui.gridlayout.GridLayoutState; import com.vaadin.shared.ui.gridlayout.GridLayoutState.ChildComponentData; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * A layout where the components are laid out on a grid using cell coordinates. * *

* The GridLayout also maintains a cursor for adding components in * left-to-right, top-to-bottom order. *

* *

* Each component in a GridLayout uses a defined * {@link GridLayout.Area area} (column1,row1,column2,row2) from the grid. The * components may not overlap with the existing components - if you try to do so * you will get an {@link OverlapsException}. Adding a component with cursor * automatically extends the grid by increasing the grid height. *

* *

* The grid coordinates, which are specified by a row and column index, always * start from 0 for the topmost row and the leftmost column. *

* * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class GridLayout extends AbstractLayout implements Layout.AlignmentHandler, Layout.SpacingHandler, Layout.MarginHandler, LayoutClickNotifier { private GridLayoutServerRpc rpc = (MouseEventDetails mouseDetails, Connector clickedConnector) -> fireEvent( LayoutClickEvent.createEvent(GridLayout.this, mouseDetails, clickedConnector)); /** * Cursor X position: this is where the next component with unspecified x,y * is inserted */ private int cursorX = 0; /** * Cursor Y position: this is where the next component with unspecified x,y * is inserted */ private int cursorY = 0; private final LinkedList components = new LinkedList<>(); private Map columnExpandRatio = new HashMap<>(); private Map rowExpandRatio = new HashMap<>(); private Alignment defaultComponentAlignment = Alignment.TOP_LEFT; /** * Constructor for a grid of given size (number of columns and rows). * * The grid may grow or shrink later. Grid grows automatically if you add * components outside its area. * * @param columns * Number of columns in the grid. * @param rows * Number of rows in the grid. */ public GridLayout(int columns, int rows) { setColumns(columns); setRows(rows); registerRpc(rpc); } /** * Constructs an empty (1x1) grid layout that is extended as needed. */ public GridLayout() { this(1, 1); } /** * Constructs a GridLayout of given size (number of columns and rows) and * adds the given components in order to the grid. * * @see #addComponents(Component...) * * @param columns * Number of columns in the grid. * @param rows * Number of rows in the grid. * @param children * Components to add to the grid. */ public GridLayout(int columns, int rows, Component... children) { this(columns, rows); addComponents(children); } @Override protected GridLayoutState getState() { return (GridLayoutState) super.getState(); } @Override protected GridLayoutState getState(boolean markAsDirty) { return (GridLayoutState) super.getState(markAsDirty); } /** *

* Adds a component to the grid in the specified area. The area is defined * by specifying the upper left corner (column1, row1) and the lower right * corner (column2, row2) of the area. The coordinates are zero-based. *

* *

* If the area overlaps with any of the existing components already present * in the grid, the operation will fail and an {@link OverlapsException} is * thrown. *

* * @param component * the component to be added, not null. * @param column1 * the column of the upper left corner of the area c * is supposed to occupy. The leftmost column has index 0. * @param row1 * the row of the upper left corner of the area c is * supposed to occupy. The topmost row has index 0. * @param column2 * the column of the lower right corner of the area * c is supposed to occupy. * @param row2 * the row of the lower right corner of the area c * is supposed to occupy. * @throws OverlapsException * if the new component overlaps with any of the components * already in the grid. * @throws OutOfBoundsException * if the cells are outside the grid area. */ public void addComponent(Component component, int column1, int row1, int column2, int row2) throws OverlapsException, OutOfBoundsException { if (component == null) { throw new NullPointerException("Component must not be null"); } // Checks that the component does not already exist in the container if (components.contains(component)) { throw new IllegalArgumentException( "Component is already in the container"); } // Creates the area final Area area = new Area(component, column1, row1, column2, row2); // Checks the validity of the coordinates if (column2 < column1 || row2 < row1) { throw new IllegalArgumentException(String.format( "Illegal coordinates for the component: %s!<=%s, %s!<=%s", column1, column2, row1, row2)); } if (column1 < 0 || row1 < 0 || column2 >= getColumns() || row2 >= getRows()) { throw new OutOfBoundsException(area); } // Checks that newItem does not overlap with existing items checkExistingOverlaps(area); // Inserts the component to right place at the list // Respect top-down, left-right ordering // component.setParent(this); final Map childDataMap = getState().childData; int index = 0; boolean done = false; for (Component c : components) { final ChildComponentData existingArea = childDataMap.get(c); if ((existingArea.row1 >= row1 && existingArea.column1 > column1) || existingArea.row1 > row1) { components.add(index, component); done = true; break; } index++; } if (!done) { components.addLast(component); } childDataMap.put(component, area.childData); // Attempt to add to super try { super.addComponent(component); } catch (IllegalArgumentException e) { childDataMap.remove(component); components.remove(component); throw e; } // update cursor position, if it's within this area; use first position // outside this area, even if it's occupied if (cursorX >= column1 && cursorX <= column2 && cursorY >= row1 && cursorY <= row2) { // cursor within area cursorX = column2 + 1; // one right of area if (cursorX >= getColumns()) { // overflowed columns cursorX = 0; // first col // move one row down, or one row under the area cursorY = (column1 == 0 ? row2 : row1) + 1; } else { cursorY = row1; } } } /** * Tests if the given area overlaps with any of the items already on the * grid. * * @param area * the Area to be checked for overlapping. * @throws OverlapsException * if area overlaps with any existing area. */ private void checkExistingOverlaps(Area area) throws OverlapsException { for (Entry entry : getState().childData .entrySet()) { if (componentsOverlap(entry.getValue(), area.childData)) { // Component not added, overlaps with existing component throw new OverlapsException( new Area(entry.getValue(), (Component) entry.getKey())); } } } /** * Adds the component to the grid in cells column1,row1 (NortWest corner of * the area.) End coordinates (SouthEast corner of the area) are the same as * column1,row1. The coordinates are zero-based. Component width and height * is 1. * * @param component * the component to be added, not null. * @param column * the column index, starting from 0. * @param row * the row index, starting from 0. * @throws OverlapsException * if the new component overlaps with any of the components * already in the grid. * @throws OutOfBoundsException * if the cell is outside the grid area. */ public void addComponent(Component component, int column, int row) throws OverlapsException, OutOfBoundsException { this.addComponent(component, column, row, column, row); } /** * Forces the next component to be added at the beginning of the next line. * *

* Sets the cursor column to 0 and increments the cursor row by one. *

* *

* By calling this function you can ensure that no more components are added * right of the previous component. *

* * @see #space() */ public void newLine() { cursorX = 0; cursorY++; } /** * Moves the cursor forward by one. If the cursor goes out of the right grid * border, it is moved to the first column of the next row. * * @see #newLine() */ public void space() { cursorX++; if (cursorX >= getColumns()) { cursorX = 0; cursorY++; } } /** * Adds the component into this container to the cursor position. If the * cursor position is already occupied, the cursor is moved forwards to find * free position. If the cursor goes out from the bottom of the grid, the * grid is automatically extended. * * @param component * the component to be added, not null. */ @Override public void addComponent(Component component) { if (component == null) { throw new IllegalArgumentException("Component must not be null"); } // Finds first available place from the grid Area area; boolean done = false; while (!done) { try { area = new Area(component, cursorX, cursorY, cursorX, cursorY); checkExistingOverlaps(area); done = true; } catch (final OverlapsException e) { space(); } } // Extends the grid if needed if (cursorX >= getColumns()) { setColumns(cursorX + 1); } if (cursorY >= getRows()) { setRows(cursorY + 1); } addComponent(component, cursorX, cursorY); } /** * Removes the specified component from the layout. * * @param component * the component to be removed. */ @Override public void removeComponent(Component component) { // Check that the component is contained in the container if (component == null || !components.contains(component)) { return; } getState().childData.remove(component); components.remove(component); super.removeComponent(component); } /** * Removes the component specified by its cell coordinates. * * @param column * the component's column, starting from 0. * @param row * the component's row, starting from 0. */ public void removeComponent(int column, int row) { // Finds the area for (final Component component : components) { final ChildComponentData childData = getState().childData .get(component); if (childData.column1 == column && childData.row1 == row) { removeComponent(component); return; } } } /** * Gets an Iterator for the components contained in the layout. By using the * Iterator it is possible to step through the contents of the layout. * * @return the Iterator of the components inside the layout. */ @Override public Iterator iterator() { return Collections.unmodifiableCollection(components).iterator(); } /** * Gets the number of components contained in the layout. Consistent with * the iterator returned by {@link #getComponentIterator()}. * * @return the number of contained components */ @Override public int getComponentCount() { return components.size(); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); getState().colExpand = new float[getColumns()]; float colSum = getExpandRatioSum(columnExpandRatio); if (colSum == 0) { // no cols have been expanded for (int i = 0; i < getColumns(); i++) { getState().colExpand[i] = 1f; } } else { for (int i = 0; i < getColumns(); i++) { getState().colExpand[i] = getColumnExpandRatio(i); } } getState().rowExpand = new float[getRows()]; float rowSum = getExpandRatioSum(rowExpandRatio); if (rowSum == 0) { // no rows have been expanded for (int i = 0; i < getRows(); i++) { getState().rowExpand[i] = 1f; } } else { for (int i = 0; i < getRows(); i++) { getState().rowExpand[i] = getRowExpandRatio(i); } } } private float getExpandRatioSum(Map ratioMap) { float sum = 0; for (Float expandRatio : ratioMap.values()) { sum += expandRatio; } return sum; } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.AlignmentHandler#getComponentAlignment(com * .vaadin.ui.Component) */ @Override public Alignment getComponentAlignment(Component childComponent) { ChildComponentData childComponentData = getState(false).childData .get(childComponent); if (childComponentData == null) { throw new IllegalArgumentException( "The given component is not a child of this layout"); } else { return new Alignment(childComponentData.alignment); } } /** * Defines a rectangular area of cells in a GridLayout. * *

* Also maintains a reference to the component contained in the area. *

* *

* The area is specified by the cell coordinates of its upper left corner * (column1,row1) and lower right corner (column2,row2). As otherwise with * GridLayout, the column and row coordinates start from zero. *

* * @author Vaadin Ltd. * @since 3.0 */ public class Area implements Serializable { private final ChildComponentData childData; private final Component component; /** *

* Construct a new area on a grid. *

* * @param component * the component connected to the area. * @param column1 * The column of the upper left corner cell of the area. The * leftmost column has index 0. * @param row1 * The row of the upper left corner cell of the area. The * topmost row has index 0. * @param column2 * The column of the lower right corner cell of the area. The * leftmost column has index 0. * @param row2 * The row of the lower right corner cell of the area. The * topmost row has index 0. */ public Area(Component component, int column1, int row1, int column2, int row2) { this.component = component; childData = new ChildComponentData(); childData.alignment = getDefaultComponentAlignment().getBitMask(); childData.column1 = column1; childData.row1 = row1; childData.column2 = column2; childData.row2 = row2; } public Area(ChildComponentData childData, Component component) { this.childData = childData; this.component = component; } /** * Tests if this Area overlaps with another Area. * * @param other * the other Area that is to be tested for overlap with this * area * @return true if other area overlaps with * this on, false if it does not. */ public boolean overlaps(Area other) { return componentsOverlap(childData, other.childData); } /** * Gets the component connected to the area. * * @return the Component. */ public Component getComponent() { return component; } /** * Gets the column of the top-left corner cell. * * @return the column of the top-left corner cell. */ public int getColumn1() { return childData.column1; } /** * Gets the column of the bottom-right corner cell. * * @return the column of the bottom-right corner cell. */ public int getColumn2() { return childData.column2; } /** * Gets the row of the top-left corner cell. * * @return the row of the top-left corner cell. */ public int getRow1() { return childData.row1; } /** * Gets the row of the bottom-right corner cell. * * @return the row of the bottom-right corner cell. */ public int getRow2() { return childData.row2; } @Override public String toString() { return String.format("Area{%s,%s - %s,%s}", getColumn1(), getRow1(), getColumn2(), getRow2()); } } private static boolean componentsOverlap(ChildComponentData a, ChildComponentData b) { return a.column1 <= b.column2 && a.row1 <= b.row2 && a.column2 >= b.column1 && a.row2 >= b.row1; } /** * Gridlayout does not support laying components on top of each other. An * OverlapsException is thrown when a component already exists * (even partly) at the same space on a grid with the new component. * * @author Vaadin Ltd. * @since 3.0 */ public class OverlapsException extends RuntimeException { private final Area existingArea; /** * Constructs an OverlapsException. * * @param existingArea * the existing area that needs overlapping */ public OverlapsException(Area existingArea) { this.existingArea = existingArea; } @Override public String getMessage() { StringBuilder sb = new StringBuilder(); Component component = existingArea.getComponent(); sb.append(component); sb.append("( type = "); sb.append(component.getClass().getName()); if (component.getCaption() != null) { sb.append(", caption = \""); sb.append(component.getCaption()); sb.append("\""); } sb.append(')'); sb.append(" is already added to "); sb.append(existingArea.childData.column1); sb.append(','); sb.append(existingArea.childData.column1); sb.append(','); sb.append(existingArea.childData.row1); sb.append(','); sb.append(existingArea.childData.row2); sb.append("(column1, column2, row1, row2)."); return sb.toString(); } /** * Gets the area . * * @return the existing area. */ public Area getArea() { return existingArea; } } /** * An Exception object which is thrown when an area exceeds the * bounds of the grid. * * @author Vaadin Ltd. * @since 3.0 */ public class OutOfBoundsException extends RuntimeException { private final Area areaOutOfBounds; /** * Constructs an OoutOfBoundsException with the specified * detail message. * * @param areaOutOfBounds * the area that exceeds the bounds of the grid */ public OutOfBoundsException(Area areaOutOfBounds) { super(String.format("%s, layout dimension: %sx%s", areaOutOfBounds, getColumns(), getRows())); this.areaOutOfBounds = areaOutOfBounds; } /** * Gets the area that is out of bounds. * * @return the area out of Bound. */ public Area getArea() { return areaOutOfBounds; } } /** * Sets the number of columns in the grid. The column count can not be * reduced if there are any areas that would be outside of the shrunk grid. * * @param columns * the new number of columns in the grid. */ public void setColumns(int columns) { // The the param if (columns < 1) { throw new IllegalArgumentException( "The number of columns and rows in the grid must be at least 1"); } // In case of no change if (getColumns() == columns) { return; } // Checks for overlaps if (getColumns() > columns) { for (Entry entry : getState().childData .entrySet()) { if (entry.getValue().column2 >= columns) { throw new OutOfBoundsException(new Area(entry.getValue(), (Component) entry.getKey())); } } } // Forget expands for removed columns if (columns < getColumns()) { for (int i = columns; i < getColumns(); i++) { columnExpandRatio.remove(i); getState().explicitColRatios.remove(i); } } getState().columns = columns; } /** * Get the number of columns in the grid. * * @return the number of columns in the grid. */ public int getColumns() { return getState(false).columns; } /** * Sets the number of rows in the grid. The number of rows can not be * reduced if there are any areas that would be outside of the shrunk grid. * * @param rows * the new number of rows in the grid. */ public void setRows(int rows) { // The the param if (rows < 1) { throw new IllegalArgumentException( "The number of columns and rows in the grid must be at least 1"); } // In case of no change if (getRows() == rows) { return; } // Checks for overlaps if (getRows() > rows) { for (Entry entry : getState().childData .entrySet()) { if (entry.getValue().row2 >= rows) { throw new OutOfBoundsException(new Area(entry.getValue(), (Component) entry.getKey())); } } } // Forget expands for removed rows if (rows < getRows()) { for (int i = rows; i < getRows(); i++) { rowExpandRatio.remove(i); getState().explicitRowRatios.remove(i); } } getState().rows = rows; } /** * Get the number of rows in the grid. * * @return the number of rows in the grid. */ public int getRows() { return getState(false).rows; } /** * Gets the current x-position (column) of the cursor. * *

* The cursor position points the position for the next component that is * added without specifying its coordinates (grid cell). When the cursor * position is occupied, the next component will be added to first free * position after the cursor. *

* * @return the grid column the cursor is on, starting from 0. */ public int getCursorX() { return cursorX; } /** * Sets the current cursor x-position. This is usually handled automatically * by GridLayout. * * @param cursorX * current cursor x-position */ public void setCursorX(int cursorX) { this.cursorX = cursorX; } /** * Gets the current y-position (row) of the cursor. * *

* The cursor position points the position for the next component that is * added without specifying its coordinates (grid cell). When the cursor * position is occupied, the next component will be added to the first free * position after the cursor. *

* * @return the grid row the Cursor is on. */ public int getCursorY() { return cursorY; } /** * Sets the current y-coordinate (row) of the cursor. This is usually * handled automatically by GridLayout. * * @param cursorY * the row number, starting from 0 for the topmost row. */ public void setCursorY(int cursorY) { this.cursorY = cursorY; } /* Documented in superclass */ @Override public void replaceComponent(Component oldComponent, Component newComponent) { // Gets the locations ChildComponentData oldLocation = getState().childData.get(oldComponent); ChildComponentData newLocation = getState().childData.get(newComponent); if (oldLocation == null) { addComponent(newComponent); } else if (newLocation == null) { removeComponent(oldComponent); addComponent(newComponent, oldLocation.column1, oldLocation.row1, oldLocation.column2, oldLocation.row2); } else { int oldAlignment = oldLocation.alignment; oldLocation.alignment = newLocation.alignment; newLocation.alignment = oldAlignment; getState().childData.put(newComponent, oldLocation); getState().childData.put(oldComponent, newLocation); } } /* * Removes all components from this container. * * @see com.vaadin.ui.ComponentContainer#removeAllComponents() */ @Override public void removeAllComponents() { super.removeAllComponents(); cursorX = 0; cursorY = 0; } @Override public void setComponentAlignment(Component childComponent, Alignment alignment) { ChildComponentData childComponentData = getState().childData .get(childComponent); if (childComponentData == null) { throw new IllegalArgumentException( "Component must be added to layout before using setComponentAlignment()"); } else { if (alignment == null) { childComponentData.alignment = GridLayoutState.ALIGNMENT_DEFAULT .getBitMask(); } else { childComponentData.alignment = alignment.getBitMask(); } } } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.SpacingHandler#setSpacing(boolean) */ @Override public void setSpacing(boolean spacing) { getState().spacing = spacing; } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.SpacingHandler#isSpacing() */ @Override public boolean isSpacing() { return getState(false).spacing; } /** * Inserts an empty row at the specified position in the grid. * * @param row * Index of the row before which the new row will be inserted. * The leftmost row has index 0. */ public void insertRow(int row) { if (row > getRows()) { throw new IllegalArgumentException("Cannot insert row at " + row + " in a gridlayout with height " + getRows()); } for (ChildComponentData existingArea : getState().childData.values()) { // Areas ending below the row needs to be moved down or stretched if (existingArea.row2 >= row) { existingArea.row2++; // Stretch areas that span over the selected row if (existingArea.row1 >= row) { existingArea.row1++; } } } if (cursorY >= row) { cursorY++; } setRows(getRows() + 1); markAsDirty(); } /** * Removes a row and all the components in the row. * *

* Components which span over several rows are removed if the selected row * is on the first row of such a component. *

* *

* If the last row is removed then all remaining components will be removed * and the grid will be reduced to one row. The cursor will be moved to the * upper left cell of the grid. *

* * @param row * Index of the row to remove. The leftmost row has index 0. */ public void removeRow(int row) { if (row >= getRows()) { throw new IllegalArgumentException("Cannot delete row " + row + " from a gridlayout with height " + getRows()); } // Remove all components in row for (int col = 0; col < getColumns(); col++) { removeComponent(col, row); } // Shrink or remove areas in the selected row for (ChildComponentData existingArea : getState().childData.values()) { if (existingArea.row2 >= row) { existingArea.row2--; if (existingArea.row1 > row) { existingArea.row1--; } } } if (getRows() == 1) { /* * Removing the last row means that the dimensions of the Grid * layout will be truncated to 1 empty row and the cursor is moved * to the first cell */ cursorX = 0; cursorY = 0; } else { setRows(getRows() - 1); if (cursorY > row) { cursorY--; } } markAsDirty(); } /** * Sets the expand ratio of given column. * *

* The expand ratio defines how excess space is distributed among columns. * Excess space means space that is left over from components that are not * sized relatively. By default, the excess space is distributed evenly. *

* *

* Note, that width of this GridLayout needs to be defined (fixed or * relative, as opposed to undefined height) for this method to have any * effect. *

* Note that checking for relative width for the child components is done on * the server so you cannot set a child component to have undefined width on * the server and set it to 100% in CSS. You must set it to * 100% on the server. * * @see #setWidth(float, Unit) * * @param columnIndex * The column index, starting from 0 for the leftmost row. * @param ratio * the expand ratio */ public void setColumnExpandRatio(int columnIndex, float ratio) { columnExpandRatio.put(columnIndex, ratio); getState().explicitColRatios.add(columnIndex); markAsDirty(); } /** * Returns the expand ratio of given column. * * @see #setColumnExpandRatio(int, float) * * @param columnIndex * The column index, starting from 0 for the leftmost row. * @return the expand ratio, 0.0f by default */ public float getColumnExpandRatio(int columnIndex) { Float r = columnExpandRatio.get(columnIndex); return r == null ? 0 : r.floatValue(); } /** * Sets the expand ratio of given row. * *

* Expand ratio defines how excess space is distributed among rows. Excess * space means the space left over from components that are not sized * relatively. By default, the excess space is distributed evenly. *

* *

* Note, that height of this GridLayout needs to be defined (fixed or * relative, as opposed to undefined height) for this method to have any * effect. *

* Note that checking for relative height for the child components is done * on the server so you cannot set a child component to have undefined * height on the server and set it to 100% in CSS. You must set * it to 100% on the server. * * @see #setHeight(float, Unit) * * @param rowIndex * The row index, starting from 0 for the topmost row. * @param ratio * the expand ratio */ public void setRowExpandRatio(int rowIndex, float ratio) { rowExpandRatio.put(rowIndex, ratio); getState().explicitRowRatios.add(rowIndex); markAsDirty(); } /** * Returns the expand ratio of given row. * * @see #setRowExpandRatio(int, float) * * @param rowIndex * The row index, starting from 0 for the topmost row. * @return the expand ratio, 0.0f by default */ public float getRowExpandRatio(int rowIndex) { Float r = rowExpandRatio.get(rowIndex); return r == null ? 0 : r.floatValue(); } /** * Gets the Component at given index. * * @param x * The column index, starting from 0 for the leftmost column. * @param y * The row index, starting from 0 for the topmost row. * @return Component in given cell or null if empty */ public Component getComponent(int x, int y) { for (Entry entry : getState( false).childData.entrySet()) { ChildComponentData childData = entry.getValue(); if (childData.column1 <= x && x <= childData.column2 && childData.row1 <= y && y <= childData.row2) { return (Component) entry.getKey(); } } return null; } /** * Returns information about the area where given component is laid in the * GridLayout. * * @param component * the component whose area information is requested. * @return an Area object that contains information how component is laid in * the grid */ public Area getComponentArea(Component component) { ChildComponentData childComponentData = getState(false).childData .get(component); if (childComponentData == null) { return null; } else { return new Area(childComponentData, component); } } @Override public Registration addLayoutClickListener(LayoutClickListener listener) { return addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener, LayoutClickListener.clickMethod); } @Override @Deprecated public void removeLayoutClickListener(LayoutClickListener listener) { removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutClickEvent.class, listener); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.MarginHandler#setMargin(boolean) */ @Override public void setMargin(boolean enabled) { setMargin(new MarginInfo(enabled)); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.MarginHandler#setMargin(com.vaadin.shared.ui. * MarginInfo ) */ @Override @SuppressWarnings("deprecation") public void setMargin(MarginInfo marginInfo) { getState().marginsBitmask = marginInfo.getBitMask(); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.MarginHandler#getMargin() */ @Override @SuppressWarnings("deprecation") public MarginInfo getMargin() { return new MarginInfo(getState(false).marginsBitmask); } /* * (non-Javadoc) * * @see com.vaadin.ui.Layout.AlignmentHandler#getDefaultComponentAlignment() */ @Override public Alignment getDefaultComponentAlignment() { return defaultComponentAlignment; } /* * (non-Javadoc) * * @see * com.vaadin.ui.Layout.AlignmentHandler#setDefaultComponentAlignment(com * .vaadin.ui.Alignment) */ @Override public void setDefaultComponentAlignment(Alignment defaultAlignment) { defaultComponentAlignment = defaultAlignment; } /** * Sets whether empty rows and columns should be considered as non-existent * when rendering or not. If this is set to true then the spacing between * multiple empty columns (or rows) will be collapsed. * * The default behavior is to consider all rows and columns as visible * * NOTE that this must be set before the initial rendering takes place. * Updating this on the fly is not supported. * * @since 7.3 * @param hideEmptyRowsAndColumns * true to hide empty rows and columns, false to leave them as-is */ public void setHideEmptyRowsAndColumns(boolean hideEmptyRowsAndColumns) { getState().hideEmptyRowsAndColumns = hideEmptyRowsAndColumns; } /** * Checks whether whether empty rows and columns should be considered as * non-existent when rendering or not. * * @see #setHideEmptyRowsAndColumns(boolean) * @since 7.3 * @return true if empty rows and columns are hidden, false otherwise */ public boolean isHideEmptyRowsAndColumns() { return getState(false).hideEmptyRowsAndColumns; } /** * {@inheritDoc} *

* After reading the design, cursorY is set to point to a row outside of the * GridLayout area. CursorX is reset to 0. */ @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); setMargin(readMargin(design, getMargin(), designContext)); if (design.childNodeSize() > 0) { // Touch content only if there is some content specified. This is // needed to be able to use extended GridLayouts which add // components in the constructor (e.g. Designs based on GridLayout). readChildComponents(design.children(), designContext); } // Set cursor position explicitly setCursorY(getRows()); setCursorX(0); } private void readChildComponents(Elements childElements, DesignContext designContext) { List rowElements = new ArrayList<>(); List> rows = new ArrayList<>(); // Prepare a 2D map for reading column contents for (Element e : childElements) { if (e.tagName().equalsIgnoreCase("row")) { rowElements.add(e); rows.add(new HashMap<>()); } } setRows(Math.max(rows.size(), 1)); Map alignments = new HashMap<>(); List columnExpandRatios = new ArrayList<>(); for (int row = 0; row < rowElements.size(); ++row) { Element rowElement = rowElements.get(row); // Row Expand if (rowElement.hasAttr("expand")) { float expand = DesignAttributeHandler.readAttribute("expand", rowElement.attributes(), float.class); setRowExpandRatio(row, expand); } Elements cols = rowElement.children(); // Amount of skipped columns due to spanned components int skippedColumns = 0; for (int column = 0; column < cols.size(); ++column) { while (rows.get(row).containsKey(column + skippedColumns)) { // Skip any spanned components skippedColumns++; } Element col = cols.get(column); Component child = null; if (!col.children().isEmpty()) { Element childElement = col.child(0); child = designContext.readDesign(childElement); alignments.put(child, DesignAttributeHandler .readAlignment(childElement.attributes())); // TODO: Currently ignoring any extra children. // Needs Error handling? } // Else: Empty placeholder. No child component. // Handle rowspan and colspan for this child component Attributes attr = col.attributes(); int colspan = DesignAttributeHandler.readAttribute("colspan", attr, 1, int.class); int rowspan = DesignAttributeHandler.readAttribute("rowspan", attr, 1, int.class); for (int rowIndex = row; rowIndex < row + rowspan; ++rowIndex) { for (int colIndex = column; colIndex < column + colspan; ++colIndex) { if (rowIndex == rows.size()) { // Rowspan with not enough rows. Fix by adding rows. rows.add(new HashMap<>()); } rows.get(rowIndex).put(colIndex + skippedColumns, child); } } // Read column expand ratios if handling the first row. if (row == 0) { if (col.hasAttr("expand")) { for (String expand : col.attr("expand").split(",")) { columnExpandRatios.add(Float.parseFloat(expand)); } } else { for (int c = 0; c < colspan; ++c) { columnExpandRatios.add(0f); } } } skippedColumns += (colspan - 1); } } // Calculate highest column count and set columns int colMax = 0; for (Map cols : rows) { if (colMax < cols.size()) { colMax = cols.size(); } } setColumns(Math.max(colMax, 1)); for (int i = 0; i < columnExpandRatios.size(); ++i) { setColumnExpandRatio(i, columnExpandRatios.get(i)); } // Reiterate through the 2D map and add components to GridLayout Set visited = new HashSet<>(); // Ignore any missing components visited.add(null); for (int i = 0; i < rows.size(); ++i) { Map row = rows.get(i); for (int j = 0; j < colMax; ++j) { Component child = row.get(j); if (visited.contains(child)) { // Empty location or already handled child continue; } visited.add(child); // Figure out col and rowspan from 2D map int colspan = 0; while (j + colspan + 1 < row.size() && row.get(j + colspan + 1) == child) { ++colspan; } int rowspan = 0; while (i + rowspan + 1 < rows.size() && rows.get(i + rowspan + 1).get(j) == child) { ++rowspan; } // Add component with area addComponent(child, j, i, j + colspan, i + rowspan); setComponentAlignment(child, alignments.get(child)); } } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); GridLayout def = designContext.getDefaultInstance(this); writeMargin(design, getMargin(), def.getMargin(), designContext); if (!designContext.shouldWriteChildren(this, def)) { return; } if (components.isEmpty()) { writeEmptyColsAndRows(design); return; } final Map childData = getState().childData; // Make a 2D map of component areas. Component[][] componentMap = new Component[getState().rows][getState().columns]; final Component dummyComponent = new Label(""); for (Component component : components) { ChildComponentData coords = childData.get(component); for (int row = coords.row1; row <= coords.row2; ++row) { for (int col = coords.column1; col <= coords.column2; ++col) { componentMap[row][col] = component; } } } // Go through the map and write only needed column tags Set visited = new HashSet<>(); // Skip the dummy placeholder visited.add(dummyComponent); for (int i = 0; i < componentMap.length; ++i) { Element row = design.appendElement("row"); // Row Expand DesignAttributeHandler.writeAttribute("expand", row.attributes(), getRowExpandRatio(i), 0.0f, float.class, designContext); int colspan = 1; Element col; for (int j = 0; j < componentMap[i].length; ++j) { Component child = componentMap[i][j]; if (child != null) { if (visited.contains(child)) { // Child has already been written in the design continue; } visited.add(child); Element childElement = designContext.createElement(child); col = row.appendElement("column"); // Write child data into design ChildComponentData coords = childData.get(child); Alignment alignment = getComponentAlignment(child); DesignAttributeHandler.writeAlignment(childElement, alignment); col.appendChild(childElement); if (coords.row1 != coords.row2) { col.attr("rowspan", "" + (1 + coords.row2 - coords.row1)); } colspan = 1 + coords.column2 - coords.column1; if (colspan > 1) { col.attr("colspan", "" + colspan); } } else { boolean hasExpands = false; if (i == 0 && lastComponentOnRow(componentMap[i], j, visited)) { // A column with expand and no content in the end of // first row needs to be present. for (int c = j; c < componentMap[i].length; ++c) { if (getColumnExpandRatio(c) > 0) { hasExpands = true; } } } if (lastComponentOnRow(componentMap[i], j, visited) && !hasExpands) { continue; } // Empty placeholder tag. col = row.appendElement("column"); // Use colspan to make placeholders more pleasant while (j + colspan < componentMap[i].length && componentMap[i][j + colspan] == child) { ++colspan; } int rowspan = getRowSpan(componentMap, i, j, colspan, child); if (colspan > 1) { col.attr("colspan", "" + colspan); } if (rowspan > 1) { col.attr("rowspan", "" + rowspan); } for (int x = 0; x < rowspan; ++x) { for (int y = 0; y < colspan; ++y) { // Mark handled columns componentMap[i + x][j + y] = dummyComponent; } } } // Column expands if (i == 0) { // Only do expands on first row String expands = ""; boolean expandRatios = false; for (int c = 0; c < colspan; ++c) { float colExpand = getColumnExpandRatio(j + c); if (colExpand > 0) { expandRatios = true; } expands += (c > 0 ? "," : "") + colExpand; } if (expandRatios) { col.attr("expand", expands); } } j += colspan - 1; } } } /** * Fills in the design with rows and empty columns. This needs to be done * for empty {@link GridLayout}, because there's no other way to serialize * info about number of columns and rows if there are absolutely no * components in the {@link GridLayout} * * @param design */ private void writeEmptyColsAndRows(Element design) { int rowCount = getState(false).rows; int colCount = getState(false).columns; // only write cols and rows tags if size is not 1x1 if (rowCount == 1 && colCount == 1) { return; } for (int i = 0; i < rowCount; i++) { Element row = design.appendElement("row"); for (int j = 0; j < colCount; j++) { row.appendElement("column"); } } } private int getRowSpan(Component[][] compMap, int i, int j, int colspan, Component child) { int rowspan = 1; while (i + rowspan < compMap.length && compMap[i + rowspan][j] == child) { for (int k = 0; k < colspan; ++k) { if (compMap[i + rowspan][j + k] != child) { return rowspan; } } rowspan++; } return rowspan; } private boolean lastComponentOnRow(Component[] componentArray, int j, Set visited) { while ((++j) < componentArray.length) { Component child = componentArray[j]; if (child != null && !visited.contains(child)) { return false; } } return true; } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("cursor-x"); result.add("cursor-y"); result.add("rows"); result.add("columns"); result.add("margin"); result.add("margin-left"); result.add("margin-right"); result.add("margin-top"); result.add("margin-bottom"); return result; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2095 Content-Disposition: inline; filename="HasChildMeasurementHint.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "7c224bfe9aa175603344adef0848d5d6b5c1cc1b" /* * Copyright 2000-2022 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; /** * Component with layout measuring hint. Used to improve granularity of control * over child component measurements. * * @since 7.6 * @author Vaadin Ltd */ public interface HasChildMeasurementHint extends HasComponents { /** * Specifies how you would like child components measurements to be handled. * Since this is a hint, it can be ignored when deemed necessary. */ public enum ChildMeasurementHint { /** * Always measure all child components (default). */ MEASURE_ALWAYS, /** * Measure child component only if child component is a Layout or * implements either ManagedLayout or ElementResizeListener. */ MEASURE_IF_NEEDED, /** * Never measure child components. This can improve rendering speed of * components with lots of children (e.g. Table), but can cause some * child components to be rendered incorrectly (e.g. ComboBox). */ MEASURE_NEVER } /** * Sets desired child size measurement hint. * * @param hint * desired hint. A value of null will reset value back to the * default (MEASURE_ALWAYS) */ void setChildMeasurementHint(ChildMeasurementHint hint); /** * Returns the current child size measurement hint. * * @return a child measurement hint value */ ChildMeasurementHint getChildMeasurementHint(); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 6488 Content-Disposition: inline; filename="HasComponents.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "91ca5a6020f13f0ab1152040d6deed0b425354a5" /* * Copyright 2000-2022 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.Iterator; import com.vaadin.event.SerializableEventListener; import com.vaadin.shared.Registration; import com.vaadin.util.ReflectTools; /** * Interface that must be implemented by all {@link Component}s that contain * other {@link Component}s. * * @author Vaadin Ltd * @since 7.0.0 * */ public interface HasComponents extends Component, Iterable { /** * Gets an iterator to the collection of contained components. Using this * iterator it is possible to step through all components contained in this * container. *

* The iterator is typically unmodifiable, and calls to * {@link Iterator#remove()} throw an exception. * * @return the component iterator. */ @Override public Iterator iterator(); /** * Interface for {@link HasComponents} implementations that support sending * attach and detach events for components. * * @since 7.0 */ public interface ComponentAttachDetachNotifier extends Serializable { /** * Listens the component attach events. * * @see Registration * * @param listener * the listener to add, not null * @return a registration object for removing the listener * @since 8.0 */ public Registration addComponentAttachListener( ComponentAttachListener listener); /** * Stops the listening component attach events. * * @param listener * the listener to removed. * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in * the registration object returned from * {@link #addComponentAttachListener(ComponentAttachListener)} * . * @since 8.0 */ @Deprecated public void removeComponentAttachListener( ComponentAttachListener listener); /** * Listens the component detach events. */ public Registration addComponentDetachListener( ComponentDetachListener listener); /** * Stops the listening component detach events. */ @Deprecated public void removeComponentDetachListener( ComponentDetachListener listener); } /** * Component attach listener interface. */ @FunctionalInterface public interface ComponentAttachListener extends SerializableEventListener { public static final Method attachMethod = ReflectTools.findMethod( ComponentAttachListener.class, "componentAttachedToContainer", ComponentAttachEvent.class); /** * A new component is attached to container. * * @param event * the component attach event. */ public void componentAttachedToContainer(ComponentAttachEvent event); } /** * Component detach listener interface. */ @FunctionalInterface public interface ComponentDetachListener extends SerializableEventListener { public static final Method detachMethod = ReflectTools.findMethod( ComponentDetachListener.class, "componentDetachedFromContainer", ComponentDetachEvent.class); /** * A component has been detached from container. * * @param event * the component detach event. */ public void componentDetachedFromContainer(ComponentDetachEvent event); } /** * Component attach event sent when a component is attached to container. */ @SuppressWarnings("serial") public static class ComponentAttachEvent extends Component.Event { private final Component component; /** * Creates a new attach event. * * @param container * the container the component has been detached to. * @param attachedComponent * the component that has been attached. */ public ComponentAttachEvent(HasComponents container, Component attachedComponent) { super(container); component = attachedComponent; } /** * Gets the component container. * */ public HasComponents getContainer() { return (HasComponents) getSource(); } /** * Gets the attached component. * */ public Component getAttachedComponent() { return component; } } /** * Component detach event sent when a component is detached from container. */ @SuppressWarnings("serial") public static class ComponentDetachEvent extends Component.Event { private final Component component; /** * Creates a new detach event. * * @param container * the container the component has been detached from. * @param detachedComponent * the component that has been detached. */ public ComponentDetachEvent(HasComponents container, Component detachedComponent) { super(container); component = detachedComponent; } /** * Gets the component container. * */ public HasComponents getContainer() { return (HasComponents) getSource(); } /** * Gets the detached component. * * @return the detached component. */ public Component getDetachedComponent() { return component; } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5906 Content-Disposition: inline; filename="HasStyleNames.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "6caacfc154aae0a326d20fd958fb23c581265ad0" /* * Copyright 2000-2022 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; /** * Implemented by components which support style names. * *

* Each style name will occur only once as specified and it is not prefixed with * the style name of the component. *

* * @since 8.7 */ public interface HasStyleNames extends Serializable { /** * Gets all user-defined CSS style names of a component. If the component * has multiple style names defined, the return string is a space-separated * list of style names. Built-in style names defined in Vaadin or GWT are * not returned. * *

* The style names are returned only in the basic form in which they were * added. *

* * @since 8.7 * @return the style name or a space-separated list of user-defined style * names of the component * @see #setStyleName(String) * @see #addStyleName(String) * @see #removeStyleName(String) */ String getStyleName(); /** * Sets one or more user-defined style names of the component, replacing any * previous user-defined styles. Multiple styles can be specified as a * space-separated list of style names. The style names must be valid CSS * class names. * *

* It is normally a good practice to use {@link #addStyleName(String) * addStyleName()} rather than this setter, as different software * abstraction layers can then add their own styles without accidentally * removing those defined in other layers. *

* * @since 8.7 * @param style * the new style or styles of the component as a space-separated * list * @see #getStyleName() * @see #addStyleName(String) * @see #removeStyleName(String) */ void setStyleName(String style); /** * Adds or removes a style name. Multiple styles can be specified as a * space-separated list of style names. * * If the {@code add} parameter is true, the style name is added to the * component. If the {@code add} parameter is false, the style name is * removed from the component. *

* Functionally this is equivalent to using {@link #addStyleName(String)} or * {@link #removeStyleName(String)} * * @since 8.7 * @param style * the style name to be added or removed * @param add * true to add the given style, false * to remove it * @see #addStyleName(String) * @see #removeStyleName(String) */ default void setStyleName(String style, boolean add) { if (add) { addStyleName(style); } else { removeStyleName(style); } } /** * Adds one or more style names to this component. Multiple styles can be * specified as a space-separated list of style names. The style name will * be rendered as a HTML class name, which can be used in a CSS definition. * * * @since 8.7 * @param style * the new style to be added to the component * @see #getStyleName() * @see #setStyleName(String) * @see #removeStyleName(String) */ void addStyleName(String style); /** * Adds one or more style names to this component by using one or multiple * parameters. * * @since 8.7 * @param styles * the style name or style names to be added to the component * @see #addStyleName(String) * @see #setStyleName(String) * @see #removeStyleName(String) */ default void addStyleNames(String... styles) { for (String style : styles) { addStyleName(style); } } /** * Removes one or more style names from component. Multiple styles can be * specified as a space-separated list of style names. * *

* The parameter must be a valid CSS style name. Only user-defined style * names added with {@link #addStyleName(String) addStyleName()} or * {@link #setStyleName(String) setStyleName()} can be removed; built-in * style names defined in Vaadin or GWT can not be removed. *

* * @since 8.7 * @param style * the style name or style names to be removed * @see #getStyleName() * @see #setStyleName(String) * @see #addStyleName(String) */ void removeStyleName(String style); /** * Removes one or more style names from component. Multiple styles can be * specified by using multiple parameters. * *

* The parameter must be a valid CSS style name. Only user-defined style * names added with {@link #addStyleName(String) addStyleName()} or * {@link #setStyleName(String) setStyleName()} can be removed; built-in * style names defined in Vaadin or GWT can not be removed. *

* * @since 8.7 * @param styles * the style name or style names to be removed * @see #removeStyleName(String) * @see #setStyleName(String) * @see #addStyleName(String) */ default void removeStyleNames(String... styles) { for (String style : styles) { removeStyleName(style); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2303 Content-Disposition: inline; filename="HasValueChangeMode.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "d6ee6c74c7cca6cc028502caff37823812d6c5d5" /* * Copyright 2000-2022 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 com.vaadin.data.HasValue.ValueChangeEvent; import com.vaadin.shared.ui.ValueChangeMode; /** * Implemented by components which support value change modes. * * @since 8.0 */ public interface HasValueChangeMode extends Component { /** * Sets the mode how the TextField triggers {@link ValueChangeEvent}s. * * @param valueChangeMode * the new mode * * @see ValueChangeMode */ public void setValueChangeMode(ValueChangeMode valueChangeMode); /** * Returns the currently set {@link ValueChangeMode}. * * @return the mode used to trigger {@link ValueChangeEvent}s. * * @see ValueChangeMode */ public ValueChangeMode getValueChangeMode(); /** * Sets how often {@link ValueChangeEvent}s are triggered when the * {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or * {@link ValueChangeMode#TIMEOUT}. * * @param valueChangeTimeout * timeout in milliseconds, must be greater or equal to 0 * @throws IllegalArgumentException * if given timeout is smaller than 0 * * @see ValueChangeMode */ public void setValueChangeTimeout(int valueChangeTimeout); /** * Returns the currently set timeout, in milliseconds, for how often * {@link ValueChangeEvent}s are triggered if the current * {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or * {@link ValueChangeMode#TIMEOUT}. * * @return the timeout in milliseconds of how often * {@link ValueChangeEvent}s are triggered. */ public int getValueChangeTimeout(); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2709 Content-Disposition: inline; filename="HorizontalLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "70ca5b949adc959ba4f6d360389db15a576a5798" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.orderedlayout.HorizontalLayoutState; /** * Horizontal layout * * HorizontalLayout is a component container, which shows the * subcomponents in the order of their addition (horizontally). * * @author Vaadin Ltd. * @since 5.3 */ @SuppressWarnings("serial") public class HorizontalLayout extends AbstractOrderedLayout { /** * Constructs an empty HorizontalLayout. */ public HorizontalLayout() { setSpacing(true); } /** * Constructs a HorizontalLayout with the given components. The components * are added in the given order. * * @see AbstractOrderedLayout#addComponents(Component...) * * @param children * The components to add. */ public HorizontalLayout(Component... children) { this(); addComponents(children); } @Override protected HorizontalLayoutState getState() { return (HorizontalLayoutState) super.getState(); } @Override protected HorizontalLayoutState getState(boolean markAsDirty) { return (HorizontalLayoutState) super.getState(markAsDirty); } /** * Adds the given components to this layout and sets them as expanded. The * width of all added child components are set to 100% so that the expansion * will be effective. The width of this layout is also set to 100% if it is * currently undefined. *

* The components are added in the provided order to the end of this layout. * Any components that are already children of this layout will be moved to * new positions. * * @param components * the components to set, not null * @since 8.0 */ public void addComponentsAndExpand(Component... components) { addComponents(components); if (getWidth() < 0) { setWidth(100, Unit.PERCENTAGE); } for (Component child : components) { child.setWidth(100, Unit.PERCENTAGE); setExpandRatio(child, 1); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2294 Content-Disposition: inline; filename="HorizontalSplitPanel.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "f80d4486584db4fa8122e3360a3a606f9f720c5a" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.splitpanel.HorizontalSplitPanelState; /** * A horizontal split panel contains two components and lays them horizontally. * The first component is on the left side. * *

 *
 *      +---------------------++----------------------+
 *      |                     ||                      |
 *      | The first component || The second component |
 *      |                     ||                      |
 *      +---------------------++----------------------+
 *
 *                            ^
 *                            |
 *                      the splitter
 *
 * 
* * @author Vaadin Ltd. * @since 6.5 */ public class HorizontalSplitPanel extends AbstractSplitPanel { /** * Creates an empty horizontal split panel. */ public HorizontalSplitPanel() { super(); setSizeFull(); } /** * Creates a horizontal split panel containing the given components. * * @param firstComponent * The component to be placed to the left of the splitter * @param secondComponent * The component to be placed to the right of the splitter */ public HorizontalSplitPanel(Component firstComponent, Component secondComponent) { this(); setFirstComponent(firstComponent); setSecondComponent(secondComponent); } @Override protected HorizontalSplitPanelState getState() { return (HorizontalSplitPanelState) super.getState(); } @Override protected HorizontalSplitPanelState getState(boolean markAsDirty) { return (HorizontalSplitPanelState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2658 Content-Disposition: inline; filename="Html5File.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "fc2690f35529863406af28dc4971b36b1ac7293c" /* * Copyright 2000-2022 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 com.vaadin.event.dd.DropHandler; import com.vaadin.server.StreamVariable; /** * {@link DragAndDropWrapper} can receive also files from client computer if * appropriate HTML 5 features are supported on client side. This class wraps * information about dragged file on server side. */ public class Html5File implements Serializable { private final String name; private final long size; private StreamVariable streamVariable; private final String type; /** * Constructs a new Html5 file wrapper. * * @param name * the file name * @param size * the size of the file * @param mimeType * the type of the file */ public Html5File(String name, long size, String mimeType) { this.name = name; this.size = size; type = mimeType; } public String getFileName() { return name; } public long getFileSize() { return size; } public String getType() { return type; } /** * Sets the {@link StreamVariable} that into which the file contents will be * written. Usage of StreamVariable is similar to {@link Upload} component. *

* If the {@link StreamVariable} is not set in the {@link DropHandler} the * file contents will not be sent to server. *

* Note! receiving file contents is experimental feature depending * on HTML 5 API's. It is supported only by modern web browsers like Firefox * 3.6 and above and recent webkit based browsers (Safari 5, Chrome 6) at * this time. * * @param streamVariable * the callback that returns stream where the implementation * writes the file contents as it arrives. */ public void setStreamVariable(StreamVariable streamVariable) { this.streamVariable = streamVariable; } public StreamVariable getStreamVariable() { return streamVariable; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1204 Content-Disposition: inline; filename="IconGenerator.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "5da430bb8d54c4ddba005de626975795f35c1a13" /* * Copyright 2000-2022 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 com.vaadin.server.Resource; import com.vaadin.server.SerializableFunction; /** * A callback interface for generating icons for an item. * * @param * item type for which the icon is generated * @since 8.0 */ @FunctionalInterface public interface IconGenerator extends SerializableFunction { /** * Gets an icon resource for the {@code item}. * * @param item * the item for which to generate an icon for * @return the generated icon resource */ @Override public Resource apply(T item); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 3442 Content-Disposition: inline; filename="Image.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "9de05e690fe12ebba560e7aa173e28f4ca9dd1cb" /* * Copyright 2000-2022 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 com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.event.MouseEvents.ClickListener; import com.vaadin.server.Resource; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.image.ImageServerRpc; import com.vaadin.shared.ui.image.ImageState; /** * Component for embedding images. * * @author Vaadin Ltd. * @since 7.0 */ @SuppressWarnings("serial") public class Image extends AbstractEmbedded { protected ImageServerRpc rpc = ( MouseEventDetails mouseDetails) -> fireEvent( new ClickEvent(Image.this, mouseDetails)); /** * Creates a new empty Image. */ public Image() { registerRpc(rpc); } /** * Creates a new empty Image with caption. * * @param caption */ public Image(String caption) { this(); setCaption(caption); } /** * Creates a new Image whose contents is loaded from given resource. The * dimensions are assumed if possible. The type is guessed from resource. * * @param caption * @param source * the Source of the embedded object. */ public Image(String caption, Resource source) { this(caption); setSource(source); } @Override protected ImageState getState() { return (ImageState) super.getState(); } @Override protected ImageState getState(boolean markAsDirty) { return (ImageState) super.getState(markAsDirty); } /** * Add a click listener to the component. The listener is called whenever * the user clicks inside the component. Depending on the content the event * may be blocked and in that case no event is fired. * * @see Registration * * @param listener * The listener to add, not null * @return a registration object for removing the listener * @since 8.0 */ public Registration addClickListener(ClickListener listener) { return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener, ClickListener.clickMethod); } /** * Remove a click listener from the component. The listener should earlier * have been added using {@link #addClickListener(ClickListener)}. * * @param listener * The listener to remove * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #addClickListener(ClickListener)}. */ @Deprecated public void removeClickListener(ClickListener listener) { removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 3866 Content-Disposition: inline; filename="InlineDateField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "768e3c4280511dddf46c6c10a888c4d5b4ecb402" /* * Copyright 2000-2022 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.time.LocalDate; import com.vaadin.shared.ui.datefield.InlineDateFieldState; /** * A date entry component, which displays the actual date selector inline. * * @see AbstractLocalDateField * @see DateField * @author Vaadin Ltd. * @since 8.0 */ public class InlineDateField extends AbstractLocalDateField { /** * Constructs an empty InlineDateField with no caption. */ public InlineDateField() { super(); } /** * Constructs a new InlineDateField with the given caption and * initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDate value. */ public InlineDateField(String caption, LocalDate value) { super(caption, value); } /** * Constructs an empty InlineDateField with caption. * * @param caption * the caption of the datefield. */ public InlineDateField(String caption) { super(caption); } /** * Constructs a new {@code InlineDateField} with a value change listener. *

* The listener is called when the value of this {@code InlineDateField} is * changed either by the user or programmatically. * * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateField(ValueChangeListener valueChangeListener) { super(); addValueChangeListener(valueChangeListener); } /** * Constructs a new {@code InlineDateField} with the given caption and a * value change listener. *

* The listener is called when the value of this {@code InlineDateField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateField(String caption, ValueChangeListener valueChangeListener) { this(valueChangeListener); setCaption(caption); } /** * Constructs a new {@code InlineDateField} with the given caption, initial * text contents and a value change listener. *

* The listener is called when the value of this {@code InlineDateField} is * changed either by the user or programmatically. * * @param caption * the caption for the field * @param value * the value for the field, not {@code null} * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateField(String caption, LocalDate value, ValueChangeListener valueChangeListener) { this(caption, value); addValueChangeListener(valueChangeListener); } @Override protected InlineDateFieldState getState() { return (InlineDateFieldState) super.getState(); } @Override protected InlineDateFieldState getState(boolean markAsDirty) { return (InlineDateFieldState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 4010 Content-Disposition: inline; filename="InlineDateTimeField.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3a0e1b0cc932ae7baf588a589e46e54f5bab6259" /* * Copyright 2000-2022 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.time.LocalDateTime; import com.vaadin.shared.ui.datefield.InlineDateTimeFieldState; /** * A date time entry component, which displays the actual date selector inline. * * @see AbstractLocalDateTimeField * @see DateTimeField * @author Vaadin Ltd. * @since 8.0 */ public class InlineDateTimeField extends AbstractLocalDateTimeField { /** * Constructs an empty InlineDateTimeField with no caption. */ public InlineDateTimeField() { super(); } /** * Constructs a new InlineDateTimeField with the given caption * and initial text contents. * * @param caption * the caption String for the editor. * @param value * the LocalDate value. */ public InlineDateTimeField(String caption, LocalDateTime value) { super(caption, value); } /** * Constructs an empty InlineDateTimeField with caption. * * @param caption * the caption of the datefield. */ public InlineDateTimeField(String caption) { super(caption); } /** * Constructs a new {@code InlineDateTimeField} with a value change * listener. *

* The listener is called when the value of this {@code InlineDateTimeField} * is changed either by the user or programmatically. * * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateTimeField( ValueChangeListener valueChangeListener) { super(); addValueChangeListener(valueChangeListener); } /** * Constructs a new {@code InlineDateTimeField} with the given caption and a * value change listener. *

* The listener is called when the value of this {@code InlineDateTimeField} * is changed either by the user or programmatically. * * @param caption * the caption for the field * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateTimeField(String caption, ValueChangeListener valueChangeListener) { this(valueChangeListener); setCaption(caption); } /** * Constructs a new {@code InlineDateTimeField} with the given caption, * initial text contents and a value change listener. *

* The listener is called when the value of this {@code InlineDateTimeField} * is changed either by the user or programmatically. * * @param caption * the caption for the field * @param value * the value for the field, not {@code null} * @param valueChangeListener * the value change listener, not {@code null} */ public InlineDateTimeField(String caption, LocalDateTime value, ValueChangeListener valueChangeListener) { this(caption, value); addValueChangeListener(valueChangeListener); } @Override protected InlineDateTimeFieldState getState() { return (InlineDateTimeFieldState) super.getState(); } @Override protected InlineDateTimeFieldState getState(boolean markAsDirty) { return (InlineDateTimeFieldState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1306 Content-Disposition: inline; filename="ItemCaptionGenerator.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "6d8ed7674bb5959d43dce498397e5f573a2df5ab" /* * Copyright 2000-2022 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 com.vaadin.server.SerializableFunction; /** * {@link ItemCaptionGenerator} can be used to customize the string shown to the * user for an item. * * @see ComboBox#setItemCaptionGenerator(ItemCaptionGenerator) * @param * item type * @since 8.0 * @author Vaadin Ltd */ @FunctionalInterface public interface ItemCaptionGenerator extends SerializableFunction { /** * Gets a caption for the {@code item}. * * @param item * the item to get caption for * @return the caption of the item; {@code null} will be shown as an empty * string */ @Override String apply(T item); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1436 Content-Disposition: inline; filename="ItemCollapseAllowedProvider.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "fe92171a5255be0afa7c22488b93d14b944c7425" /* * Copyright 2000-2022 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 com.vaadin.server.SerializablePredicate; /** * A callback interface for resolving whether client-side collapsing should be * allowed for an item in a listing component that displays hierarchical data, * such as {@link TreeGrid}. * * @author Vaadin Ltd * @since 8.1 * * @see TreeGrid#setItemCollapseAllowedProvider(ItemCollapseAllowedProvider) * * @param * item data type */ @FunctionalInterface public interface ItemCollapseAllowedProvider extends SerializablePredicate { /** * Returns whether collapsing is allowed for the given item. * * @param item * the item to test * @return {@code true} if collapse is allowed for the given item, * {@code false} otherwise */ @Override boolean test(T item); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5581 Content-Disposition: inline; filename="JavaScript.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "904803123c9318c3841d55470c8fe2159dcfad62" /* * Copyright 2000-2022 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.util.HashMap; import java.util.Map; import com.vaadin.server.AbstractExtension; import com.vaadin.server.Page; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.extension.javascriptmanager.ExecuteJavaScriptRpc; import com.vaadin.shared.extension.javascriptmanager.JavaScriptManagerState; import elemental.json.JsonArray; import elemental.json.JsonException; /** * Provides access to JavaScript functionality in the web browser. To get an * instance of JavaScript, either use Page.getJavaScript() or * JavaScript.getCurrent() as a shorthand for getting the JavaScript object * corresponding to the current Page. * * @author Vaadin Ltd * @since 7.0.0 */ public class JavaScript extends AbstractExtension { private Map functions = new HashMap<>(); // Can not be defined in client package as this JSONArray is not available // in GWT public interface JavaScriptCallbackRpc extends ServerRpc { public void call(String name, JsonArray arguments); } /** * Creates a new JavaScript object. You should typically not this, but * instead use the JavaScript object already associated with your Page * object. */ public JavaScript() { registerRpc( (JavaScriptCallbackRpc) (String name, JsonArray arguments) -> { JavaScriptFunction function = functions.get(name); // TODO handle situation if name is not registered try { function.call(arguments); } catch (JsonException e) { throw new IllegalArgumentException(e); } }); } @Override protected JavaScriptManagerState getState() { return (JavaScriptManagerState) super.getState(); } @Override protected JavaScriptManagerState getState(boolean markAsDirty) { return (JavaScriptManagerState) super.getState(markAsDirty); } /** * Add a new function to the global JavaScript namespace (i.e. the window * object). The call method in the passed * {@link JavaScriptFunction} object will be invoked with the same * parameters whenever the JavaScript function is called in the browser. * * A function added with the name "myFunction" can thus be * invoked with the following JavaScript code: * window.myFunction(argument1, argument2). * * If the name parameter contains dots, simple objects are created on demand * to allow calling the function using the same name (e.g. * window.myObject.myFunction). * * @param name * the name that the function should get in the global JavaScript * namespace. * @param function * the JavaScriptFunction that will be invoked if the JavaScript * function is called. */ public void addFunction(String name, JavaScriptFunction function) { functions.put(name, function); getState().names.add(name); } /** * Removes a JavaScriptFunction from the browser's global JavaScript * namespace. * * If the name contains dots and intermediate objects were created by * {@link #addFunction(String, JavaScriptFunction)}, these objects will not * be removed by this method. * * @param name * the name of the callback to remove */ public void removeFunction(String name) { functions.remove(name); getState().names.remove(name); } /** * Executes the given JavaScript code in the browser. * * @param script * The JavaScript code to run. */ public void execute(String script) { getRpcProxy(ExecuteJavaScriptRpc.class).executeJavaScript(script); } /** * Executes the given JavaScript code in the browser. * * @param script * The JavaScript code to run. */ public static void eval(String script) { getCurrent().execute(script); } /** * Get the JavaScript object for the current Page, or null if there is no * current page. * * @see Page#getCurrent() * * @return the JavaScript object corresponding to the current Page, or * null if there is no current page. */ public static JavaScript getCurrent() { Page page = Page.getCurrent(); if (page == null) { return null; } return page.getJavaScript(); } /** * JavaScript is not designed to be removed. * * @throws UnsupportedOperationException * when invoked */ @Override public void remove() { throw new UnsupportedOperationException( "JavaScript is not designed to be removed."); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1743 Content-Disposition: inline; filename="JavaScriptFunction.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "af7fe6b406107b3d71d6337cf5b41240c7c29a34" /* * Copyright 2000-2022 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 com.vaadin.server.AbstractJavaScriptExtension; import elemental.json.JsonArray; /** * Defines a method that is called by a client-side JavaScript function. When * the corresponding JavaScript function is called, the {@link #call(JsonArray)} * method is invoked. * * @see JavaScript#addFunction(String, JavaScriptFunction) * @see AbstractJavaScriptComponent#addFunction(String, JavaScriptFunction) * @see AbstractJavaScriptExtension#addFunction(String, JavaScriptFunction) * * @author Vaadin Ltd * @since 7.0.0 */ @FunctionalInterface public interface JavaScriptFunction extends Serializable { /** * Invoked whenever the corresponding JavaScript function is called in the * browser. *

* Because of the asynchronous nature of the communication between client * and server, no return value can be sent back to the browser. * * @param arguments * an array with JSON representations of the arguments with which * the JavaScript function was called. */ public void call(JsonArray arguments); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5257 Content-Disposition: inline; filename="Label.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "8cf0865d3541672fc06e465237041127a5e458e7" /* * Copyright 2000-2022 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.util.Collection; import org.jsoup.nodes.Element; import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.label.LabelState; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignFormatter; /** * Label component for showing non-editable short texts. *

* The label content can be set to the modes specified by {@link ContentMode}. * If content mode is set to HTML, any HTML content is allowed. * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class Label extends AbstractComponent { /** * Creates an empty Label. */ public Label() { this(""); } /** * Creates a new instance with text content mode and the given text. * * @param text * the text to set */ public Label(String text) { this(text, ContentMode.TEXT); } /** * Creates a new instance with the given text and content mode. * * @param text * the text to set * @param contentMode * the content mode to use * @since 8.0 */ public Label(String text, ContentMode contentMode) { setValue(text); setContentMode(contentMode); } @Override protected LabelState getState() { return (LabelState) super.getState(); } @Override protected LabelState getState(boolean markAsDirty) { return (LabelState) super.getState(markAsDirty); } /** * Gets the content mode of the label. * * @return the content mode of the label * * @see ContentMode * @since 8.0 */ public ContentMode getContentMode() { return getState(false).contentMode; } /** * Sets the content mode of the label. * * @param contentMode * the content mode to set * * @see ContentMode * @since 8.0 */ public void setContentMode(ContentMode contentMode) { if (contentMode == null) { throw new IllegalArgumentException("Content mode can not be null"); } getState().contentMode = contentMode; } /** * Sets the text to be shown in the label. * * @param value * the text to show in the label, null is converted to an empty * string */ public void setValue(String value) { if (value == null) { getState().text = ""; } else { getState().text = value; } } /** * Gets the text shown in the label. * * @return the text shown in the label, not null */ public String getValue() { return getState(false).text; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); String innerHtml = design.html(); boolean plainText = design.hasAttr(DESIGN_ATTR_PLAIN_TEXT); if (plainText) { setContentMode(ContentMode.TEXT); } else { setContentMode(ContentMode.HTML); } if (innerHtml != null && !innerHtml.isEmpty()) { if (plainText) { innerHtml = DesignFormatter.decodeFromTextNode(innerHtml); } setValue(innerHtml); } } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("value"); result.add("content-mode"); result.add("plain-text"); return result; } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); String content = getValue(); if (content != null) { switch (getContentMode()) { case TEXT: case PREFORMATTED: { // FIXME This attribute is not enough to be able to restore the // content mode in readDesign. The content mode should instead // be written directly in the attribute and restored in // readDesign. See ticket #19435 design.attr(DESIGN_ATTR_PLAIN_TEXT, true); String encodeForTextNode = DesignFormatter .encodeForTextNode(content); if (encodeForTextNode != null) { design.html(encodeForTextNode); } } break; case HTML: design.html(content); break; } } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 5439 Content-Disposition: inline; filename="Layout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "a82db1b7fcfca8d52b019750762234b721ca6e25" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.MarginInfo; /** * Extension to the {@link ComponentContainer} interface which adds the * layouting control to the elements in the container. This is required by the * various layout components to enable them to place other components in * specific locations in the UI. * * @author Vaadin Ltd. * @since 3.0 */ public interface Layout extends ComponentContainer { /** * AlignmentHandler is most commonly an advanced {@link Layout} that can * align its components. */ public interface AlignmentHandler extends Serializable { /** * Set alignment for one contained component in this layout. Use * predefined alignments from Alignment class. * * Example: * layout.setComponentAlignment(myComponent, Alignment.TOP_RIGHT); * * * @param childComponent * the component to align within it's layout cell. * @param alignment * the Alignment value to be set */ public void setComponentAlignment(Component childComponent, Alignment alignment); /** * Returns the current Alignment of given component. * * @param childComponent * @return the {@link Alignment} */ public Alignment getComponentAlignment(Component childComponent); /** * Sets the alignment used for new components added to this layout. The * default is {@link Alignment#TOP_LEFT}. * * @param defaultComponentAlignment * The new default alignment */ public void setDefaultComponentAlignment( Alignment defaultComponentAlignment); /** * Returns the alignment used for new components added to this layout. * * @return The default alignment */ public Alignment getDefaultComponentAlignment(); } /** * This type of layout supports automatic addition of space between its * components. * */ public interface SpacingHandler extends Serializable { /** * Enable spacing between child components within this layout. * *

* NOTE: This will only affect the space between * components, not the space around all the components in the layout * (i.e. do not confuse this with the cellspacing attribute of a HTML * Table). Use {@link MarginHandler#setMargin(boolean)} to add space * around the layout. *

* *

* See the reference manual for more information about CSS rules for * defining the amount of spacing to use. *

* * @param enabled * true if spacing should be turned on, false if it should be * turned off */ public void setSpacing(boolean enabled); /** * * @return true if spacing between child components within this layout * is enabled, false otherwise */ public boolean isSpacing(); } /** * This type of layout supports automatic addition of margins (space around * its components). */ public interface MarginHandler extends Serializable { /** * Enable layout margins. Affects all four sides of the layout. This * will tell the client-side implementation to leave extra space around * the layout. The client-side implementation decides the actual amount, * and it can vary between themes. * * @param enabled * true if margins should be enabled on all sides, false to * disable all margins */ public void setMargin(boolean enabled); /** * Enable margins for this layout. * *

* NOTE: This will only affect the space around the * components in the layout, not space between the components in the * layout. Use {@link SpacingHandler#setSpacing(boolean)} to add space * between the components in the layout. *

* *

* See the reference manual for more information about CSS rules for * defining the size of the margin. *

* * @param marginInfo * MarginInfo object containing the new margins. */ public void setMargin(MarginInfo marginInfo); /** * * @return MarginInfo containing the currently enabled margins. */ public MarginInfo getMargin(); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2378 Content-Disposition: inline; filename="LegacyComponent.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "5b8fe6043dd075d5d5b7fc4eb6377079b266ae79" /* * Copyright 2000-2022 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 com.vaadin.event.ConnectorEventListener; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.VariableOwner; /** * Interface provided to ease porting of Vaadin 6 components to Vaadin 7. By * implementing this interface your Component will be able to use * {@link #paintContent(PaintTarget)} and * {@link #changeVariables(Object, java.util.Map)} just like in Vaadin 6. * * @deprecated As of 7.0. This class is only intended to ease migration and * should not be used for new projects. * * @author Vaadin Ltd * @since 7.0.0 */ @Deprecated public interface LegacyComponent extends VariableOwner, Component, ConnectorEventListener { /** *

* Paints the Paintable into a UIDL stream. This method creates the UIDL * sequence describing it and outputs it to the given UIDL stream. *

* *

* It is called when the contents of the component should be painted in * response to the component first being shown or having been altered so * that its visual representation is changed. *

* * @param target * the target UIDL stream where the component should paint itself * to. * @throws PaintException * if the paint operation failed. */ public void paintContent(PaintTarget target) throws PaintException; /** * (non-Javadoc) {@inheritDoc} *

* For a LegacyComponent, markAsDirty will also cause * {@link #paintContent(PaintTarget)} to be called before sending changes to * the client. * * @see com.vaadin.server.ClientConnector#markAsDirty() */ @Override public void markAsDirty(); } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 17436 Content-Disposition: inline; filename="LegacyWindow.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "7cb227bb7388280d6041e35a0ab1b30fc47f6fb4" /* * Copyright 2000-2022 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.net.MalformedURLException; import java.net.URL; import com.vaadin.server.LegacyApplication; import com.vaadin.server.Page.BrowserWindowResizeListener; import com.vaadin.server.Resource; import com.vaadin.server.VaadinRequest; import com.vaadin.shared.ui.BorderStyle; /** * Helper class to emulate the main window from Vaadin 6 using UIs. This class * should be used in the same way as Window used as a browser level window in * Vaadin 6 with {@link com.vaadin.server.LegacyApplication} */ @Deprecated public class LegacyWindow extends UI { private String name; private LegacyApplication application; /** * Create a new legacy window. */ public LegacyWindow() { super(new VerticalLayout()); ((VerticalLayout) getContent()).setSpacing(false); } /** * Creates a new legacy window with the given caption. * * @param caption * the caption of the window */ public LegacyWindow(String caption) { this(); setCaption(caption); } /** * Creates a legacy window with the given caption and content layout. * * @param caption * @param content */ public LegacyWindow(String caption, ComponentContainer content) { super(content); setCaption(caption); } @Override protected void init(VaadinRequest request) { // Just empty } public void setApplication(LegacyApplication application) { this.application = application; } public LegacyApplication getApplication() { return application; } /** * Gets the unique name of the window. The name of the window is used to * uniquely identify it. *

* The name also determines the URL that can be used for direct access to a * window. All windows can be accessed through * {@code http://host:port/app/win} where {@code http://host:port/app} is * the application URL (as returned by {@link LegacyApplication#getURL()} * and {@code win} is the window name. *

*

* Note! Portlets do not support direct window access through URLs. *

* * @return the Name of the Window. */ public String getName() { return name; } /** * Sets the unique name of the window. The name of the window is used to * uniquely identify it inside the application. *

* The name also determines the URL that can be used for direct access to a * window. All windows can be accessed through * {@code http://host:port/app/win} where {@code http://host:port/app} is * the application URL (as returned by {@link LegacyApplication#getURL()} * and {@code win} is the window name. *

*

* This method can only be called before the window is added to an * application. *

* Note! Portlets do not support direct window access through URLs. *

* * @param name * the new name for the window or null if the application should * automatically assign a name to it * @throws IllegalStateException * if the window is attached to an application */ public void setName(String name) { this.name = name; // The name can not be changed in application if (isAttached()) { throw new IllegalStateException( "Window name can not be changed while " + "the window is in application"); } } /** * Gets the full URL of the window. The returned URL is window specific and * can be used to directly refer to the window. *

* Note! This method can not be used for portlets. *

* * @return the URL of the window or null if the window is not attached to an * application */ public URL getURL() { LegacyApplication application = getApplication(); if (application == null) { return null; } try { return new URL(application.getURL(), getName() + "/"); } catch (MalformedURLException e) { throw new RuntimeException( "Internal problem getting window URL, please report"); } } /** * Opens the given resource in this UI. The contents of this UI is replaced * by the {@code Resource}. * * @param resource * the resource to show in this UI * * @deprecated As of 7.0, use getPage().setLocation instead */ @Deprecated public void open(Resource resource) { open(resource, null, false); } /* ********************************************************************* */ /** * Opens the given resource in a window with the given name. *

* The supplied {@code windowName} is used as the target name in a * window.open call in the client. This means that special values such as * "_blank", "_self", "_top", "_parent" have special meaning. An empty or * null window name is also a special case. *

*

* "", null and "_self" as {@code windowName} all causes the resource to be * opened in the current window, replacing any old contents. For * downloadable content you should avoid "_self" as "_self" causes the * client to skip rendering of any other changes as it considers them * irrelevant (the page will be replaced by the resource). This can speed up * the opening of a resource, but it might also put the client side into an * inconsistent state if the window content is not completely replaced e.g., * if the resource is downloaded instead of displayed in the browser. *

*

* "_blank" as {@code windowName} causes the resource to always be opened in * a new window or tab (depends on the browser and browser settings). *

*

* "_top" and "_parent" as {@code windowName} works as specified by the HTML * standard. *

*

* Any other {@code windowName} will open the resource in a window with that * name, either by opening a new window/tab in the browser or by replacing * the contents of an existing window with that name. *

*

* As of Vaadin 7.0.0, the functionality for opening a Resource in a Page * has been replaced with similar methods based on a String URL. This is * because the usage of Resource is problematic with memory management and * with security features in some browsers. Is is recommended to instead use * {@link Link} for starting downloads. *

* * @param resource * the resource. * @param windowName * the name of the window. * @deprecated As of 7.0, use getPage().open instead */ @Deprecated public void open(Resource resource, String windowName) { open(resource, windowName, true); } /** * Opens the given resource in a window with the given name and optionally * tries to force the resource to open in a new window instead of a new tab. *

* The supplied {@code windowName} is used as the target name in a * window.open call in the client. This means that special values such as * "_blank", "_self", "_top", "_parent" have special meaning. An empty or * null window name is also a special case. *

*

* "", null and "_self" as {@code windowName} all causes the resource to be * opened in the current window, replacing any old contents. For * downloadable content you should avoid "_self" as "_self" causes the * client to skip rendering of any other changes as it considers them * irrelevant (the page will be replaced by the resource). This can speed up * the opening of a resource, but it might also put the client side into an * inconsistent state if the window content is not completely replaced e.g., * if the resource is downloaded instead of displayed in the browser. *

*

* "_blank" as {@code windowName} causes the resource to always be opened in * a new window or tab (depends on the browser and browser settings). *

*

* "_top" and "_parent" as {@code windowName} works as specified by the HTML * standard. *

*

* Any other {@code windowName} will open the resource in a window with that * name, either by opening a new window/tab in the browser or by replacing * the contents of an existing window with that name. *

*

* If {@code windowName} is set to open the resource in a new window or tab * and {@code tryToOpenAsPopup} is true, this method attempts to force the * browser to open a new window instead of a tab. NOTE: This is a * best-effort attempt and may not work reliably with all browsers and * different pop-up preferences. With most browsers using default settings, * {@code tryToOpenAsPopup} works properly. *

*

* As of Vaadin 7.0.0, the functionality for opening a Resource in a Page * has been replaced with similar methods based on a String URL. This is * because the usage of Resource is problematic with memory management and * with security features in some browsers. Is is recommended to instead use * {@link Link} for starting downloads. *

* * @param resource * the resource. * @param windowName * the name of the window. * @param tryToOpenAsPopup * Whether to try to force the resource to be opened in a new * window */ public void open(Resource resource, String windowName, boolean tryToOpenAsPopup) { getPage().open(resource, windowName, tryToOpenAsPopup); } /** * Opens the given resource in a window with the given size, border and * name. For more information on the meaning of {@code windowName}, see * {@link #open(Resource, String)}. *

* As of Vaadin 7.0.0, the functionality for opening a Resource in a Page * has been replaced with similar methods based on a String URL. This is * because the usage of Resource is problematic with memory management and * with security features in some browsers. Is is recommended to instead use * {@link Link} for starting downloads. *

* * @param resource * the resource. * @param windowName * the name of the window. * @param width * the width of the window in pixels * @param height * the height of the window in pixels * @param border * the border style of the window. * @deprecated As of 7.0, use getPage().open instead */ @Deprecated public void open(Resource resource, String windowName, int width, int height, BorderStyle border) { getPage().open(resource, windowName, width, height, border); } /** * Adds a new {@link BrowserWindowResizeListener} to this UI. The listener * will be notified whenever the browser window within which this UI resides * is resized. * * @param resizeListener * the listener to add * * @see BrowserWindowResizeListener#browserWindowResized(com.vaadin.server.Page.BrowserWindowResizeEvent) * BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent) * @see #setResizeLazy(boolean) * * @deprecated As of 7.0, use the similarly named api in Page instead */ @Deprecated public void addListener(BrowserWindowResizeListener resizeListener) { getPage().addBrowserWindowResizeListener(resizeListener); } /** * Removes a {@link BrowserWindowResizeListener} from this UI. The listener * will no longer be notified when the browser window is resized. * * @param resizeListener * the listener to remove * @deprecated As of 7.0, use the similarly named api in Page instead */ @Deprecated public void removeListener(BrowserWindowResizeListener resizeListener) { getPage().removeBrowserWindowResizeListener(resizeListener); } /** * Gets the last known height of the browser window in which this UI * resides. * * @return the browser window height in pixels * @deprecated As of 7.0, use the similarly named api in Page instead */ @Deprecated public int getBrowserWindowHeight() { return getPage().getBrowserWindowHeight(); } /** * Gets the last known width of the browser window in which this UI resides. * * @return the browser window width in pixels * * @deprecated As of 7.0, use the similarly named api in Page instead */ @Deprecated public int getBrowserWindowWidth() { return getPage().getBrowserWindowWidth(); } /** * Executes JavaScript in this window. * *

* This method allows one to inject javascript from the server to client. A * client implementation is not required to implement this functionality, * but currently all web-based clients do implement this. *

* *

* Executing javascript this way often leads to cross-browser compatibility * issues and regressions that are hard to resolve. Use of this method * should be avoided and instead it is recommended to create new widgets * with GWT. For more info on creating own, reusable client-side widgets in * Java, read the corresponding chapter in Book of Vaadin. *

* * @param script * JavaScript snippet that will be executed. * * @deprecated As of 7.0, use JavaScript.getCurrent().execute(String) * instead */ @Deprecated public void executeJavaScript(String script) { getPage().getJavaScript().execute(script); } @Override public void setCaption(String caption) { // Override to provide backwards compatibility getState().caption = caption; getPage().setTitle(caption); } @Override public ComponentContainer getContent() { return (ComponentContainer) super.getContent(); } /** * Set the content of the window. For a {@link LegacyWindow}, the content * must be a {@link ComponentContainer}. * * @param content */ @Override public void setContent(Component content) { if (!(content instanceof ComponentContainer)) { throw new IllegalArgumentException( "The content of a LegacyWindow must be a ComponentContainer"); } super.setContent(content); } /** * This implementation replaces a component in the content container ( * {@link #getContent()}) instead of in the actual UI. * * This method should only be called when the content is a * {@link ComponentContainer} (default {@link VerticalLayout} or explicitly * set). */ public void replaceComponent(Component oldComponent, Component newComponent) { getContent().replaceComponent(oldComponent, newComponent); } /** * Adds a component to this UI. The component is not added directly to the * UI, but instead to the content container ({@link #getContent()}). * * This method should only be called when the content is a * {@link ComponentContainer} (default {@link VerticalLayout} or explicitly * set). * * @param component * the component to add to this UI * * @see #getContent() */ public void addComponent(Component component) { getContent().addComponent(component); } /** * This implementation removes the component from the content container ( * {@link #getContent()}) instead of from the actual UI. * * This method should only be called when the content is a * {@link ComponentContainer} (default {@link VerticalLayout} or explicitly * set). */ public void removeComponent(Component component) { getContent().removeComponent(component); } /** * This implementation removes the components from the content container ( * {@link #getContent()}) instead of from the actual UI. * * This method should only be called when the content is a * {@link ComponentContainer} (default {@link VerticalLayout} or explicitly * set). */ public void removeAllComponents() { getContent().removeAllComponents(); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 6889 Content-Disposition: inline; filename="Link.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4eab8198f95df73a9c5800028852401d9faa0a53" /* * Copyright 2000-2022 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.util.Collection; import org.jsoup.nodes.Element; import com.vaadin.server.Resource; import com.vaadin.shared.ui.BorderStyle; import com.vaadin.shared.ui.link.LinkConstants; import com.vaadin.shared.ui.link.LinkState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * Link is used to create external or internal URL links. * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class Link extends AbstractComponent { /** * @deprecated As of 7.0, use {@link BorderStyle#NONE} instead */ @Deprecated public static final BorderStyle TARGET_BORDER_NONE = BorderStyle.NONE; /** * @deprecated As of 7.0, use {@link BorderStyle#MINIMAL} instead */ @Deprecated public static final BorderStyle TARGET_BORDER_MINIMAL = BorderStyle.MINIMAL; /** * @deprecated As of 7.0, use {@link BorderStyle#DEFAULT} instead */ @Deprecated public static final BorderStyle TARGET_BORDER_DEFAULT = BorderStyle.DEFAULT; /** * Creates a new link. */ public Link() { } /** * Creates a new instance of Link. * * @param caption * @param resource */ public Link(String caption, Resource resource) { setCaption(caption); setResource(resource); } /** * Creates a new instance of Link that opens a new window. * * * @param caption * the Link text. * @param targetName * the name of the target window where the link opens to. Empty * name of null implies that the target is opened to the window * containing the link. * @param width * the Width of the target window. * @param height * the Height of the target window. * @param border * the Border style of the target window. * */ public Link(String caption, Resource resource, String targetName, int width, int height, BorderStyle border) { setCaption(caption); setResource(resource); setTargetName(targetName); setTargetWidth(width); setTargetHeight(height); setTargetBorder(border); } @Override protected LinkState getState() { return (LinkState) super.getState(); } @Override protected LinkState getState(boolean markAsDirty) { return (LinkState) super.getState(markAsDirty); } /** * Returns the target window border. * * @return the target window border. */ public BorderStyle getTargetBorder() { return getState(false).targetBorder; } /** * Returns the target window height or -1 if not set. * * @return the target window height. */ public int getTargetHeight() { return getState(false).targetHeight < 0 ? -1 : getState(false).targetHeight; } /** * Returns the target window name. Empty name of null implies that the * target is opened to the window containing the link. * * @return the target window name. */ public String getTargetName() { return getState(false).target; } /** * Returns the target window width or -1 if not set. * * @return the target window width. */ public int getTargetWidth() { return getState(false).targetWidth < 0 ? -1 : getState(false).targetWidth; } /** * Sets the border of the target window. * * @param targetBorder * the targetBorder to set. */ public void setTargetBorder(BorderStyle targetBorder) { getState().targetBorder = targetBorder; } /** * Sets the target window height. * * @param targetHeight * the targetHeight to set. */ public void setTargetHeight(int targetHeight) { getState().targetHeight = targetHeight; } /** * Sets the target window name. * * @param targetName * the targetName to set. */ public void setTargetName(String targetName) { getState().target = targetName; } /** * Sets the target window width. * * @param targetWidth * the targetWidth to set. */ public void setTargetWidth(int targetWidth) { getState().targetWidth = targetWidth; } /** * Returns the resource this link opens. * * @return the Resource. */ public Resource getResource() { return getResource(LinkConstants.HREF_RESOURCE); } /** * Sets the resource this link opens. * * @param resource * the resource to set. */ public void setResource(Resource resource) { setResource(LinkConstants.HREF_RESOURCE, resource); } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); if (design.hasAttr("target")) { setTargetName(DesignAttributeHandler.getFormatter() .parse(design.attr("target"), String.class)); } if (design.hasAttr("href")) { setResource(DesignAttributeHandler.getFormatter() .parse(design.attr("href"), Resource.class)); } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); Link def = designContext.getDefaultInstance(this); DesignAttributeHandler.writeAttribute("target", design.attributes(), getTargetName(), def.getTargetName(), String.class, designContext); DesignAttributeHandler.writeAttribute("href", design.attributes(), getResource(), def.getResource(), Resource.class, designContext); } @Override protected Collection getCustomAttributes() { Collection a = super.getCustomAttributes(); a.add("target-name"); a.add("resource"); // Add custom attributes, see #19107 a.add("target"); a.add("href"); return a; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 3864 Content-Disposition: inline; filename="ListSelect.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "b4471cdbc230ded6abd94e600f4594cd6caadbba" /* * Copyright 2000-2022 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.util.Collection; import com.vaadin.data.HasDataProvider; import com.vaadin.data.provider.DataProvider; import com.vaadin.shared.ui.listselect.ListSelectState; /** * This is a simple list select without, for instance, support for new items, * lazyloading, and other advanced features. * * @author Vaadin Ltd * * @param * item type */ public class ListSelect extends AbstractMultiSelect implements HasDataProvider { /** Default number of rows visible for select. */ // protected to allow javadoc linking protected static final int DEFAULT_ROWS = 10; /** * Constructs a new ListSelect. */ public ListSelect() { setRows(DEFAULT_ROWS); } /** * Constructs a new ListSelect with the given caption. * * @param caption * the caption to set, can be {@code null} */ public ListSelect(String caption) { this(); setCaption(caption); } /** * Constructs a new ListSelect with caption and data provider for options. * * @param caption * the caption to set, can be {@code null} * @param dataProvider * the data provider, not {@code null} * @since 8.0 */ public ListSelect(String caption, DataProvider dataProvider) { this(caption); setDataProvider(dataProvider); } /** * Constructs a new ListSelect with caption and the given options. * * @param caption * the caption to set, can be {@code null} * @param options * the options, cannot be {@code null} */ public ListSelect(String caption, Collection options) { this(caption, DataProvider.ofCollection(options)); } /** * Returns the number of rows in the select. *

* Default value is {@link #DEFAULT_ROWS} * * @return the number of rows visible */ public int getRows() { return getState(false).rows; } /** * Sets the number of rows in the select. If the number of rows is set to 0, * the actual number of displayed rows is determined implicitly by the * select. *

* If a height if set (using {@link #setHeight(String)} or * {@link #setHeight(float, Unit)}) it overrides the number of rows. Leave * the height undefined to use this method. *

* Default value is {@link #DEFAULT_ROWS} * * @param rows * the number of rows to set. */ public void setRows(int rows) { if (rows < 0) { rows = 0; } if (getState(false).rows != rows) { getState().rows = rows; } } @Override protected ListSelectState getState() { return (ListSelectState) super.getState(); } @Override protected ListSelectState getState(boolean markAsDirty) { return (ListSelectState) super.getState(markAsDirty); } @Override public DataProvider getDataProvider() { return internalGetDataProvider(); } @Override public void setDataProvider(DataProvider dataProvider) { internalSetDataProvider(dataProvider); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 4377 Content-Disposition: inline; filename="LoadingIndicatorConfiguration.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "64ec58c9312f1ca25a6092441e1bcc4ecc0e2d12" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.ui.UIState.LoadingIndicatorConfigurationState; /** * Provides method for configuring the loading indicator. * * @author Vaadin Ltd * @since 7.1 */ public interface LoadingIndicatorConfiguration extends Serializable { /** * Sets the delay before the loading indicator is shown. The default is * 300ms. * * @param firstDelay * The first delay (in ms) */ public void setFirstDelay(int firstDelay); /** * Returns the delay before the loading indicator is shown. * * @return The first delay (in ms) */ public int getFirstDelay(); /** * Sets the delay before the loading indicator goes into the "second" state. * The delay is calculated from the time when the loading indicator was * triggered. The default is 1500ms. * * @param secondDelay * The delay before going into the "second" state (in ms) */ public void setSecondDelay(int secondDelay); /** * Returns the delay before the loading indicator goes into the "second" * state. The delay is calculated from the time when the loading indicator * was triggered. * * @return The delay before going into the "second" state (in ms) */ public int getSecondDelay(); /** * Sets the delay before the loading indicator goes into the "third" state. * The delay is calculated from the time when the loading indicator was * triggered. The default is 5000ms. * * @param thirdDelay * The delay before going into the "third" state (in ms) */ public void setThirdDelay(int thirdDelay); /** * Returns the delay before the loading indicator goes into the "third" * state. The delay is calculated from the time when the loading indicator * was triggered. * * @return The delay before going into the "third" state (in ms) */ public int getThirdDelay(); } class LoadingIndicatorConfigurationImpl implements LoadingIndicatorConfiguration { private final UI ui; public LoadingIndicatorConfigurationImpl(UI ui) { this.ui = ui; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#setFirstDelay(int) */ @Override public void setFirstDelay(int firstDelay) { getState().firstDelay = firstDelay; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#getFirstDelay() */ @Override public int getFirstDelay() { return getState(false).firstDelay; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#setSecondDelay(int) */ @Override public void setSecondDelay(int secondDelay) { getState().secondDelay = secondDelay; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#getSecondDelay() */ @Override public int getSecondDelay() { return getState(false).secondDelay; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#setThirdDelay(int) */ @Override public void setThirdDelay(int thirdDelay) { getState().thirdDelay = thirdDelay; } /* * (non-Javadoc) * * @see com.vaadin.ui.LoadingIndicator#getThirdDelay() */ @Override public int getThirdDelay() { return getState(false).thirdDelay; } private LoadingIndicatorConfigurationState getState() { return ui.getState().loadingIndicatorConfiguration; } private LoadingIndicatorConfigurationState getState(boolean markAsDirty) { return ui.getState(markAsDirty).loadingIndicatorConfiguration; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 12723 Content-Disposition: inline; filename="LoginForm.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "72d4e0159618ebdc0702ece3a55e6588c58bd6b9" /* * Copyright 2000-2022 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.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import com.vaadin.event.SerializableEventListener; import com.vaadin.server.StreamResource; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.loginform.LoginFormConstants; import com.vaadin.shared.ui.loginform.LoginFormRpc; import com.vaadin.shared.ui.loginform.LoginFormState; import com.vaadin.util.ReflectTools; /** * Login form with auto-completion and auto-fill for all major browsers. You can * derive from this class and implement the * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)} * method to build the layout using the text fields and login button that are * passed to that method. The supplied components are specially treated so that * they work with password managers. *

* To customize the fields or to replace them with your own implementations, you * can override {@link #createUsernameField()}, {@link #createPasswordField()} * and {@link #createLoginButton()}. These methods are called automatically and * cannot be called by your code. Captions can be reset by overriding * {@link #getUsernameCaption()}, {@link #getPasswordCaption()} and * {@link #getLoginButtonCaption()}. *

* Note that the API of LoginForm changed significantly in Vaadin 7.7. * * @since 5.3 */ public class LoginForm extends AbstractSingleComponentContainer { /** * Event sent when the login form is submitted. */ public static class LoginEvent extends Component.Event { private final Map params; /** * Creates a login event using the given source and the given * parameters. * * @param source * the source of the event * @param params */ private LoginEvent(LoginForm source, Map params) { super(source); this.params = params; } @Override public LoginForm getSource() { return (LoginForm) super.getSource(); } /** * Gets the login parameter with the given name. * * @param name * the name of the parameter * @return the value of the parameter or null if no such parameter is * present */ public String getLoginParameter(String name) { return params.get(name); } } /** * Listener triggered when a login occurs in a {@link LoginForm}. */ @FunctionalInterface public interface LoginListener extends SerializableEventListener { /** * Event method invoked when the login button is pressed in a login * form. * * @param event * the login event */ public void onLogin(LoginEvent event); } /** * Internal stream source for the login URL - always returns "Success" and * ignores the values received. */ private static class LoginStreamSource implements StreamResource.StreamSource { @Override public InputStream getStream() { return new ByteArrayInputStream( "Success".getBytes(StandardCharsets.UTF_8)); } } private static final Method ON_LOGIN_METHOD = ReflectTools .findMethod(LoginListener.class, "onLogin", LoginEvent.class); private boolean initialized; private String usernameCaption = "Username"; private String passwordCaption = "Password"; private String loginButtonCaption = "Login"; /** * Customize the user name field. Only for overriding, do not call. * * @return the user name field * @since 7.7 */ protected TextField createUsernameField() { throwIfInitialized(); TextField field = new TextField(getUsernameCaption()); field.focus(); return field; } /** * Gets the caption set with {@link #setUsernameCaption(String)}. Note that * this method might not match what is shown to the user if * {@link #createUsernameField()} has been overridden. * * @return the user name field caption */ public String getUsernameCaption() { return usernameCaption; } /** * Sets the caption of the user name field. Note that the caption can only * be set with this method before the login form has been initialized * (attached). *

* As an alternative to calling this method, the method * {@link #createUsernameField()} can be overridden. * * @param usernameCaption * the caption to set for the user name field */ public void setUsernameCaption(String usernameCaption) { this.usernameCaption = usernameCaption; } /** * Customize the password field. Only for overriding, do not call. * * @return the password field * @since 7.7 */ protected PasswordField createPasswordField() { throwIfInitialized(); return new PasswordField(getPasswordCaption()); } /** * Gets the caption set with {@link #setPasswordCaption(String)}. Note that * this method might not match what is shown to the user if * {@link #createPasswordField()} has been overridden. * * * @return the password field caption */ public String getPasswordCaption() { return passwordCaption; } /** * Set the caption of the password field. Note that the caption can only be * set with this method before the login form has been initialized * (attached). *

* As an alternative to calling this method, the method * {@link #createPasswordField()} can be overridden. * * @param passwordCaption * the caption for the password field */ public void setPasswordCaption(String passwordCaption) { this.passwordCaption = passwordCaption; } /** * Customize the login button. Only for overriding, do not call. * * @return the login button * @since 7.7 */ protected Button createLoginButton() { throwIfInitialized(); return new Button(getLoginButtonCaption()); } /** * Gets the caption set with {@link #setLoginButtonCaption(String)}. Note * that this method might not match what is shown to the user if * {@link #createLoginButton()} has been overridden. * * @return the login button caption */ public String getLoginButtonCaption() { return loginButtonCaption; } /** * Sets the caption of the login button. Note that the caption can only be * set with this method before the login form has been initialized * (attached). *

* As an alternative to calling this method, the method * {@link #createLoginButton()} can be overridden. * * @param loginButtonCaption * new caption */ public void setLoginButtonCaption(String loginButtonCaption) { this.loginButtonCaption = loginButtonCaption; } @Override protected LoginFormState getState() { return (LoginFormState) super.getState(); } @Override protected LoginFormState getState(boolean markAsDirty) { return (LoginFormState) super.getState(markAsDirty); } @Override public void attach() { super.attach(); init(); } private void throwIfInitialized() { if (initialized) { throw new IllegalStateException( "Already initialized. The create methods may not be called explicitly."); } } /** * Create the content for the login form with the supplied user name field, * password field and the login button. You cannot use any other text fields * or buttons for this purpose. To replace these components with your own * implementations, override {@link #createUsernameField()}, * {@link #createPasswordField()} and {@link #createLoginButton()}. If you * only want to change the default captions, override * {@link #getUsernameCaption()}, {@link #getPasswordCaption()} and * {@link #getLoginButtonCaption()}. You do not have to use the login button * in your layout. * * @param userNameField * the user name text field * @param passwordField * the password field * @param loginButton * the login button * @return content component * @since 7.7 */ protected Component createContent(TextField userNameField, PasswordField passwordField, Button loginButton) { VerticalLayout layout = new VerticalLayout(); layout.setSpacing(true); layout.setMargin(true); layout.addComponent(userNameField); layout.addComponent(passwordField); layout.addComponent(loginButton); return layout; } private void init() { if (initialized) { return; } LoginFormState state = getState(); state.userNameFieldConnector = createUsernameField(); state.passwordFieldConnector = createPasswordField(); state.loginButtonConnector = createLoginButton(); StreamResource resource = new StreamResource(new LoginStreamSource(), LoginFormConstants.LOGIN_RESOURCE_NAME); resource.setMIMEType(ApplicationConstants.CONTENT_TYPE_TEXT_HTML_UTF_8); resource.setCacheTime(-1); setResource(LoginFormConstants.LOGIN_RESOURCE_NAME, resource); registerRpc((LoginFormRpc) this::login); initialized = true; setContent(createContent(getUsernameField(), getPasswordField(), getLoginButton())); } private TextField getUsernameField() { assert initialized; return (TextField) getState(false).userNameFieldConnector; } private PasswordField getPasswordField() { assert initialized; return (PasswordField) getState(false).passwordFieldConnector; } private Button getLoginButton() { assert initialized; return (Button) getState(false).loginButtonConnector; } /** * Handles the login. *

* In deferred mode, this method is called after the dummy POST request that * triggers the password manager has been completed. In direct mode (the * default setting), it is called directly when the user hits the enter key * or clicks on the login button. In the latter case, you cannot change the * URL in the method or the password manager will not be triggered. */ private void login() { Map params = new HashMap<>(); params.put("username", getUsernameField().getValue()); params.put("password", getPasswordField().getValue()); LoginEvent event = new LoginEvent(LoginForm.this, params); fireEvent(event); } /** * Adds a {@link LoginListener}. *

* The listener is called when the user presses the login button. * * @param listener * the listener to add * @return a registration object for removing the listener * @since 8.0 */ public Registration addLoginListener(LoginListener listener) { return addListener(LoginEvent.class, listener, ON_LOGIN_METHOD); } /** * Removes a {@link LoginListener}. * * @param listener * the listener to remove * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #addLoginListener(LoginListener)}. */ @Deprecated public void removeLoginListener(LoginListener listener) { removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 39537 Content-Disposition: inline; filename="MenuBar.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "95fda4eaeaf393c1ad4b9eba41649cdc97b2da82" /* * Copyright 2000-2022 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.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Map; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import org.jsoup.parser.Tag; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.EventTrigger; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.menubar.MenuBarConstants; import com.vaadin.shared.ui.menubar.MenuBarState; import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** *

* A class representing a horizontal menu bar. The menu can contain MenuItem * objects, which in turn can contain more MenuBars. These sub-level MenuBars * are represented as vertical menu. *

*/ @SuppressWarnings("serial") public class MenuBar extends AbstractComponent implements LegacyComponent, Focusable { // Items of the top-level menu private final List menuItems; // Number of items in this menu private int numberOfItems = 0; private MenuItem moreItem; private boolean openRootOnHover; private boolean htmlContentAllowed; @Override protected MenuBarState getState() { return (MenuBarState) super.getState(); } @Override protected MenuBarState getState(boolean markAsDirty) { return (MenuBarState) super.getState(markAsDirty); } /** Paint (serialize) the component for the client. */ @Override public void paintContent(PaintTarget target) throws PaintException { target.addAttribute(MenuBarConstants.OPEN_ROOT_MENU_ON_HOWER, openRootOnHover); if (isHtmlContentAllowed()) { target.addAttribute(MenuBarConstants.HTML_CONTENT_ALLOWED, true); } target.startTag("options"); if (getWidth() > -1) { target.startTag("moreItem"); target.addAttribute("text", moreItem.getText()); if (moreItem.getIcon() != null) { target.addAttribute("icon", moreItem.getIcon()); } target.endTag("moreItem"); } target.endTag("options"); target.startTag("items"); // This generates the tree from the contents of the menu for (MenuItem item : menuItems) { paintItem(target, item); } target.endTag("items"); } private void paintItem(PaintTarget target, MenuItem item) throws PaintException { if (!item.isVisible()) { return; } target.startTag("item"); target.addAttribute("id", item.getId()); if (item.getStyleName() != null) { target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_STYLE, item.getStyleName()); } if (item.isSeparator()) { target.addAttribute("separator", true); } else { target.addAttribute("text", item.getText()); Command command = item.getCommand(); if (command != null) { target.addAttribute("command", true); } Resource icon = item.getIcon(); if (icon != null) { target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_ICON, icon); } if (!item.isEnabled()) { target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_DISABLED, true); } String description = item.getDescription(); if (description != null && !description.isEmpty()) { target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_DESCRIPTION, description); } ContentMode contentMode = item.getDescriptionContentMode(); // If the contentMode is equal to ContentMode.PREFORMATTED, we don't // add any attribute. if (contentMode != null && contentMode != ContentMode.PREFORMATTED) { target.addAttribute( MenuBarConstants.ATTRIBUTE_ITEM_DESCRIPTION_CONTENT_MODE, contentMode.name()); } if (item.isCheckable()) { // if the "checked" attribute is present (either true or false), // the item is checkable target.addAttribute(MenuBarConstants.ATTRIBUTE_CHECKED, item.isChecked()); } if (item.hasChildren()) { for (MenuItem child : item.getChildren()) { paintItem(target, child); } } } target.endTag("item"); } /** De-serialize changes received from client. */ @Override public void changeVariables(Object source, Map variables) { final Deque items = new ArrayDeque<>(); boolean found = false; if (variables.containsKey("clickedId")) { Integer clickedId = (Integer) variables.get("clickedId"); for (MenuItem i : getItems()) { items.push(i); } MenuItem tmpItem = null; // Go through all the items in the menu while (!found && !items.isEmpty()) { tmpItem = items.pop(); found = (clickedId == tmpItem.getId()); if (tmpItem.hasChildren()) { for (MenuItem i : tmpItem.getChildren()) { items.push(i); } } } // while // If we got the clicked item, launch the command. if (found && tmpItem.isEnabled()) { if (tmpItem.isCheckable()) { tmpItem.setChecked(!tmpItem.isChecked()); } if (null != tmpItem.getCommand()) { tmpItem.getCommand().menuSelected(tmpItem); } } } } /** * Constructs an empty, horizontal menu. */ public MenuBar() { menuItems = new ArrayList<>(); setMoreMenuItem(null); } /** * Adds a new menu item to the menu bar *

* Clicking on this menu item has no effect. Use * {@link #addItem(String, Command)} or {@link MenuItem#setCommand(Command)} * to assign an action to the menu item. * * @param caption * the text for the menu item * @throws IllegalArgumentException * * @since 8.4 */ public MenuBar.MenuItem addItem(String caption) { return addItem(caption, null, null); } /** * Add a new item to the menu bar. Command can be null, but a caption must * be given. * * @param caption * the text for the menu item * @param command * the command for the menu item * @throws IllegalArgumentException */ public MenuBar.MenuItem addItem(String caption, MenuBar.Command command) { return addItem(caption, null, command); } /** * Add a new item to the menu bar. Icon and command can be null, but a * caption must be given. * * @param caption * the text for the menu item * @param icon * the icon for the menu item * @param command * the command for the menu item * @throws IllegalArgumentException */ public MenuBar.MenuItem addItem(String caption, Resource icon, MenuBar.Command command) { if (caption == null) { throw new IllegalArgumentException("caption cannot be null"); } MenuItem newItem = new MenuItem(caption, icon, command); menuItems.add(newItem); markAsDirty(); return newItem; } /** * Add an item before some item. If the given item does not exist the item * is added at the end of the menu. Icon and command can be null, but a * caption must be given. * * @param caption * the text for the menu item * @param icon * the icon for the menu item * @param command * the command for the menu item * @param itemToAddBefore * the item that will be after the new item * @throws IllegalArgumentException */ public MenuBar.MenuItem addItemBefore(String caption, Resource icon, MenuBar.Command command, MenuBar.MenuItem itemToAddBefore) { if (caption == null) { throw new IllegalArgumentException("caption cannot be null"); } MenuItem newItem = new MenuItem(caption, icon, command); if (menuItems.contains(itemToAddBefore)) { int index = menuItems.indexOf(itemToAddBefore); menuItems.add(index, newItem); } else { menuItems.add(newItem); } markAsDirty(); return newItem; } /** * Returns a list with all the MenuItem objects in the menu bar. * * @return a list containing the MenuItem objects in the menu bar */ public List getItems() { return menuItems; } /** * Remove first occurrence the specified item from the main menu. * * @param item * The item to be removed */ public void removeItem(MenuBar.MenuItem item) { if (item != null) { menuItems.remove(item); } markAsDirty(); } /** * Empty the menu bar. */ public void removeItems() { menuItems.clear(); markAsDirty(); } /** * Returns the size of the menu. * * @return The size of the menu */ public int getSize() { return menuItems.size(); } /** * Set the item that is used when collapsing the top level menu. All * "overflowing" items will be added below this. The item command will be * ignored. If set to null, the default item with a downwards arrow is used. * * The item command (if specified) is ignored. * * @param item */ public void setMoreMenuItem(MenuItem item) { if (item != null) { moreItem = item; } else { moreItem = new MenuItem("", null, null); } markAsDirty(); } /** * Get the MenuItem used as the collapse menu item. * * @return */ public MenuItem getMoreMenuItem() { return moreItem; } /** * Using this method menubar can be put into a special mode where top level * menus opens without clicking on the menu, but automatically when mouse * cursor is moved over the menu. In this mode the menu also closes itself * if the mouse is moved out of the opened menu. *

* Note, that on touch devices the menu still opens on a click event. * * @param autoOpenTopLevelMenu * true if menus should be opened without click, the default is * false */ public void setAutoOpen(boolean autoOpenTopLevelMenu) { if (autoOpenTopLevelMenu != openRootOnHover) { openRootOnHover = autoOpenTopLevelMenu; markAsDirty(); } } /** * Detects whether the menubar is in a mode where top level menus are * automatically opened when the mouse cursor is moved over the menu. * Normally root menu opens only by clicking on the menu. Submenus always * open automatically. * * @return true if the root menus open without click, the default is false */ public boolean isAutoOpen() { return openRootOnHover; } /** * Sets whether html is allowed in the item captions. If set to true, the * captions are passed to the browser as html and the developer is * responsible for ensuring no harmful html is used. If set to false, the * content is passed to the browser as plain text. * * @param htmlContentAllowed * true if the captions are used as html, false if used as plain * text */ public void setHtmlContentAllowed(boolean htmlContentAllowed) { this.htmlContentAllowed = htmlContentAllowed; markAsDirty(); } /** * Checks whether item captions are interpreted as html or plain text. * * @return true if the captions are used as html, false if used as plain * text * @see #setHtmlContentAllowed(boolean) */ public boolean isHtmlContentAllowed() { return htmlContentAllowed; } @Override public int getTabIndex() { return getState(false).tabIndex; } /* * (non-Javadoc) * * @see com.vaadin.ui.Component.Focusable#setTabIndex(int) */ @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } /** * Returns the delay before executing update logic inside * {@link com.vaadin.client.ui.menubar.MenuBarConnector#updateFromUIDL(UIDL, ApplicationConnection)} * after mouseDownEvent * * @since 8.7 */ public int getDelayMs() { return getState(false).delayMs; } /** * Set the delay before executing update logic inside * {@link com.vaadin.client.ui.menubar.MenuBarConnector#updateFromUIDL(UIDL, ApplicationConnection)} * after mouseDownEvent * * @since 8.7 */ public void setDelayMs(int delayMs) { getState().delayMs = delayMs; } @Override public void focus() { // Overridden only to make public super.focus(); } /** * This interface contains the layer for menu commands of the * {@link com.vaadin.ui.MenuBar} class. It's method will fire when the user * clicks on the containing {@link com.vaadin.ui.MenuBar.MenuItem}. The * selected item is given as an argument. */ @FunctionalInterface public interface Command extends Serializable { public void menuSelected(MenuBar.MenuItem selectedItem); } /** * A composite class for menu items and sub-menus. You can set commands to * be fired on user click by implementing the * {@link com.vaadin.ui.MenuBar.Command} interface. You can also add * multiple MenuItems to a MenuItem and create a sub-menu. * */ public class MenuItem implements Serializable, EventTrigger { /** Private members * */ private final int itsId; private Command itsCommand; private String itsText; private List itsChildren; private Resource itsIcon; private MenuItem itsParent; private boolean enabled = true; private boolean visible = true; private boolean isSeparator = false; private String styleName; private String description; private ContentMode descriptionContentMode = ContentMode.PREFORMATTED; private boolean checkable = false; private boolean checked = false; /** * Constructs a new menu item that can optionally have an icon and a * command associated with it. Icon and command can be null, but a * caption must be given. * * @param caption * The text associated with the command * @param command * The command to be fired * @throws IllegalArgumentException */ public MenuItem(String caption, Resource icon, MenuBar.Command command) { if (caption == null) { throw new IllegalArgumentException("caption cannot be null"); } itsId = ++numberOfItems; itsText = caption; itsIcon = icon; itsCommand = command; } /** * Checks if the item has children (if it is a sub-menu). * * @return True if this item has children */ public boolean hasChildren() { return !isSeparator() && itsChildren != null; } /** * Adds a separator to this menu. A separator is a way to visually group * items in a menu, to make it easier for users to find what they are * looking for in the menu. * * @author Jouni Koivuviita / Vaadin Ltd. * @since 6.2.0 */ public MenuBar.MenuItem addSeparator() { MenuItem item = addItem("", null, null); item.setSeparator(true); return item; } public MenuBar.MenuItem addSeparatorBefore(MenuItem itemToAddBefore) { MenuItem item = addItemBefore("", null, null, itemToAddBefore); item.setSeparator(true); return item; } /** * Add a new menu item inside this menu item, creating a sub-menu. *

* Clicking on the new item has no effect. Use * {@link #addItem(String, Command)} or {@link #setCommand(Command)} to * assign an action to the menu item. * * @param caption * the text for the menu item * * @since 8.4 */ public MenuBar.MenuItem addItem(String caption) { return addItem(caption, null, null); } /** * Add a new item inside this item, thus creating a sub-menu. Command * can be null, but a caption must be given. * * @param caption * the text for the menu item * @param command * the command for the menu item */ public MenuBar.MenuItem addItem(String caption, MenuBar.Command command) { return addItem(caption, null, command); } /** * Add a new item inside this item, thus creating a sub-menu. Icon and * command can be null, but a caption must be given. * * @param caption * the text for the menu item * @param icon * the icon for the menu item * @param command * the command for the menu item * @throws IllegalStateException * If the item is checkable and thus cannot have children. */ public MenuBar.MenuItem addItem(String caption, Resource icon, MenuBar.Command command) throws IllegalStateException { if (isSeparator()) { throw new UnsupportedOperationException( "Cannot add items to a separator"); } if (isCheckable()) { throw new IllegalStateException( "A checkable item cannot have children"); } if (caption == null) { throw new IllegalArgumentException("Caption cannot be null"); } if (itsChildren == null) { itsChildren = new ArrayList<>(); } MenuItem newItem = new MenuItem(caption, icon, command); // The only place where the parent is set newItem.setParent(this); itsChildren.add(newItem); markAsDirty(); return newItem; } /** * Add an item before some item. If the given item does not exist the * item is added at the end of the menu. Icon and command can be null, * but a caption must be given. * * @param caption * the text for the menu item * @param icon * the icon for the menu item * @param command * the command for the menu item * @param itemToAddBefore * the item that will be after the new item * @throws IllegalStateException * If the item is checkable and thus cannot have children. */ public MenuBar.MenuItem addItemBefore(String caption, Resource icon, MenuBar.Command command, MenuBar.MenuItem itemToAddBefore) throws IllegalStateException { if (isCheckable()) { throw new IllegalStateException( "A checkable item cannot have children"); } MenuItem newItem = null; if (hasChildren() && itsChildren.contains(itemToAddBefore)) { int index = itsChildren.indexOf(itemToAddBefore); newItem = new MenuItem(caption, icon, command); newItem.setParent(this); itsChildren.add(index, newItem); } else { newItem = addItem(caption, icon, command); } markAsDirty(); return newItem; } /** * For the associated command. * * @return The associated command, or null if there is none */ public Command getCommand() { return itsCommand; } /** * Gets the objects icon. * * @return The icon of the item, null if the item doesn't have an icon */ public Resource getIcon() { return itsIcon; } /** * For the containing item. This will return null if the item is in the * top-level menu bar. * * @return The containing {@link com.vaadin.ui.MenuBar.MenuItem} , or * null if there is none */ public MenuBar.MenuItem getParent() { return itsParent; } /** * This will return the children of this item or null if there are none. * * @return List of children items, or null if there are none */ public List getChildren() { return itsChildren; } /** * Gets the objects text. * * @return The text */ public java.lang.String getText() { return itsText; } /** * Returns the number of children. * * @return The number of child items */ public int getSize() { if (itsChildren != null) { return itsChildren.size(); } return -1; } /** * Get the unique identifier for this item. * * @return The id of this item */ public int getId() { return itsId; } /** * Set the command for this item. Set null to remove. * * @param command * The MenuCommand of this item */ public void setCommand(MenuBar.Command command) { itsCommand = command; } /** * Sets the icon. Set null to remove. * * @param icon * The icon for this item */ public void setIcon(Resource icon) { itsIcon = icon; markAsDirty(); } /** * Set the text of this object. * * @param text * Text for this object */ public void setText(java.lang.String text) { if (text != null) { itsText = text; } markAsDirty(); } /** * Remove the first occurrence of the item. * * @param item * The item to be removed */ public void removeChild(MenuBar.MenuItem item) { if (item != null && itsChildren != null) { itsChildren.remove(item); if (itsChildren.isEmpty()) { itsChildren = null; } markAsDirty(); } } /** * Empty the list of children items. */ public void removeChildren() { if (itsChildren != null) { itsChildren.clear(); itsChildren = null; markAsDirty(); } } /** * Set the parent of this item. This is called by the addItem method. * * @param parent * The parent item */ protected void setParent(MenuBar.MenuItem parent) { itsParent = parent; } public void setEnabled(boolean enabled) { this.enabled = enabled; markAsDirty(); } public boolean isEnabled() { return enabled; } public void setVisible(boolean visible) { this.visible = visible; markAsDirty(); } public boolean isVisible() { return visible; } private void setSeparator(boolean isSeparator) { this.isSeparator = isSeparator; markAsDirty(); } public boolean isSeparator() { return isSeparator; } public void setStyleName(String styleName) { this.styleName = styleName; markAsDirty(); } public String getStyleName() { return styleName; } /** * Analogous method to {@link AbstractComponent#setDescription(String)}. * Sets the item's description. See {@link #getDescription()} for more * information on what the description is. * * @param description * the new description string for the component. */ public void setDescription(String description) { setDescription(description, ContentMode.PREFORMATTED); } /** * Analogous method to * {@link AbstractComponent#setDescription(String, ContentMode)}. Sets * the item's description using given content mode. See * {@link #getDescription()} for more information on what the * description is. *

* If the content {@code mode} is {@literal ContentMode.HTML} the * description is displayed as HTML in tooltips or directly in certain * components so care should be taken to avoid creating the possibility * for HTML injection and possibly XSS vulnerabilities. * * @see ContentMode * * @param description * the new description string for the component. * @param mode * the content mode for the description * @since 8.3 */ public void setDescription(String description, ContentMode mode) { this.description = description; this.descriptionContentMode = mode; markAsDirty(); } /** *

* Gets the item's description. The description can be used to briefly * describe the state of the item to the user. The description string * may contain certain XML tags: *

* *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
TagDescriptionExample
<b>boldbold text
<i>italicitalic text
<u>underlinedunderlined text
<br>linebreakN/A
<ul>
* <li>item1
* <li>item1
* </ul>
item list *
    *
  • item1 *
  • item2 *
*
*

* *

* These tags may be nested. *

* * @return item's description String */ public String getDescription() { return description; } /** * Gets the content mode of the description of the menu item. The * description is displayed as the tooltip of the menu item in the UI. *

* If no content mode was explicitly set using the * {@link #setDescription(String, ContentMode)} method, the content mode * will be {@link ContentMode#PREFORMATTED} *

* * @return the {@link ContentMode} of the description of this menu item * @see ContentMode * @since 8.3 */ public ContentMode getDescriptionContentMode() { return descriptionContentMode; } /** * Gets the checkable state of the item - whether the item has checked * and unchecked states. If an item is checkable its checked state (as * returned by {@link #isChecked()}) is indicated in the UI. * *

* An item is not checkable by default. *

* * @return true if the item is checkable, false otherwise * @since 6.6.2 */ public boolean isCheckable() { return checkable; } /** * Sets the checkable state of the item. If an item is checkable its * checked state (as returned by {@link #isChecked()}) is indicated in * the UI. * *

* An item is not checkable by default. *

* *

* Items with sub items cannot be checkable. *

* * @param checkable * true if the item should be checkable, false otherwise * @throws IllegalStateException * If the item has children * @since 6.6.2 */ public void setCheckable(boolean checkable) throws IllegalStateException { if (hasChildren()) { throw new IllegalStateException( "A menu item with children cannot be checkable"); } this.checkable = checkable; markAsDirty(); } /** * Gets the checked state of the item (checked or unchecked). Only used * if the item is checkable (as indicated by {@link #isCheckable()}). * The checked state is indicated in the UI with the item, if the item * is checkable. * *

* An item is not checked by default. *

* *

* The CSS style corresponding to the checked state is "-checked". *

* * @return true if the item is checked, false otherwise * @since 6.6.2 */ public boolean isChecked() { return checked; } /** * Sets the checked state of the item. Only used if the item is * checkable (indicated by {@link #isCheckable()}). The checked state is * indicated in the UI with the item, if the item is checkable. * *

* An item is not checked by default. *

* *

* The CSS style corresponding to the checked state is "-checked". *

* * @since 6.6.2 */ public void setChecked(boolean checked) { this.checked = checked; markAsDirty(); } /** * Gets the menu bar this item is part of. * * @return the menu bar this item is attached to * @since 8.4 */ public MenuBar getMenuBar() { return MenuBar.this; } @Override public AbstractClientConnector getConnector() { return getMenuBar(); } @Override public String getPartInformation() { return String.valueOf(getId()); } } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); for (MenuItem item : getItems()) { design.appendChild(createMenuElement(item, designContext)); } // in many cases there seems to be an empty more menu item if (getMoreMenuItem() != null && !getMoreMenuItem().getText().isEmpty()) { Element moreMenu = createMenuElement(getMoreMenuItem(), designContext); moreMenu.attr("more", true); design.appendChild(moreMenu); } if (!htmlContentAllowed) { design.attr(DESIGN_ATTR_PLAIN_TEXT, true); } } protected Element createMenuElement(MenuItem item, DesignContext context) { Element menuElement = new Element(Tag.valueOf("menu"), ""); // Defaults MenuItem def = new MenuItem("", null, null); Attributes attr = menuElement.attributes(); DesignAttributeHandler.writeAttribute("icon", attr, item.getIcon(), def.getIcon(), Resource.class, context); DesignAttributeHandler.writeAttribute("disabled", attr, !item.isEnabled(), !def.isEnabled(), boolean.class, context); DesignAttributeHandler.writeAttribute("visible", attr, item.isVisible(), def.isVisible(), boolean.class, context); DesignAttributeHandler.writeAttribute("separator", attr, item.isSeparator(), def.isSeparator(), boolean.class, context); DesignAttributeHandler.writeAttribute("checkable", attr, item.isCheckable(), def.isCheckable(), boolean.class, context); DesignAttributeHandler.writeAttribute("checked", attr, item.isChecked(), def.isChecked(), boolean.class, context); DesignAttributeHandler.writeAttribute("description", attr, item.getDescription(), def.getDescription(), String.class, context); DesignAttributeHandler.writeAttribute("descriptioncontentmode", attr, item.getDescriptionContentMode().name(), def.getDescriptionContentMode().name(), String.class, context); DesignAttributeHandler.writeAttribute("style-name", attr, item.getStyleName(), def.getStyleName(), String.class, context); menuElement.append(item.getText()); if (item.hasChildren()) { for (MenuItem subMenu : item.getChildren()) { menuElement.appendChild(createMenuElement(subMenu, context)); } } return menuElement; } protected MenuItem readMenuElement(Element menuElement) { Resource icon = null; if (menuElement.hasAttr("icon")) { icon = DesignAttributeHandler.getFormatter() .parse(menuElement.attr("icon"), Resource.class); } String caption = ""; List subMenus = new ArrayList<>(); for (Node node : menuElement.childNodes()) { if (node instanceof Element && ((Element) node).tagName().equals("menu")) { subMenus.add((Element) node); } else { caption += node.toString(); } } MenuItem menu = new MenuItem(caption.trim(), icon, null); Attributes attr = menuElement.attributes(); if (menuElement.hasAttr("icon")) { menu.setIcon(DesignAttributeHandler.readAttribute("icon", attr, Resource.class)); } if (menuElement.hasAttr("disabled")) { menu.setEnabled(!DesignAttributeHandler.readAttribute("disabled", attr, boolean.class)); } if (menuElement.hasAttr("visible")) { menu.setVisible(DesignAttributeHandler.readAttribute("visible", attr, boolean.class)); } if (menuElement.hasAttr("separator")) { menu.setSeparator(DesignAttributeHandler.readAttribute("separator", attr, boolean.class)); } if (menuElement.hasAttr("checkable")) { menu.setCheckable(DesignAttributeHandler.readAttribute("checkable", attr, boolean.class)); } if (menuElement.hasAttr("checked")) { menu.setChecked(DesignAttributeHandler.readAttribute("checked", attr, boolean.class)); } if (menuElement.hasAttr("description")) { String description = DesignAttributeHandler .readAttribute("description", attr, String.class); if (menuElement.hasAttr("descriptioncontentmode")) { String contentModeString = DesignAttributeHandler.readAttribute( "descriptioncontentmode", attr, String.class); menu.setDescription(description, ContentMode.valueOf(contentModeString)); } else { menu.setDescription(description); } } if (menuElement.hasAttr("style-name")) { menu.setStyleName(DesignAttributeHandler.readAttribute("style-name", attr, String.class)); } if (!subMenus.isEmpty()) { menu.itsChildren = new ArrayList<>(); } for (Element subMenu : subMenus) { MenuItem newItem = readMenuElement(subMenu); newItem.setParent(menu); menu.itsChildren.add(newItem); } return menu; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); for (Element itemElement : design.children()) { if (itemElement.tagName().equals("menu")) { MenuItem menuItem = readMenuElement(itemElement); if (itemElement.hasAttr("more")) { setMoreMenuItem(menuItem); } else { menuItems.add(menuItem); } } } setHtmlContentAllowed(!design.hasAttr(DESIGN_ATTR_PLAIN_TEXT)); } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add(DESIGN_ATTR_PLAIN_TEXT); result.add("html-content-allowed"); return result; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 4717 Content-Disposition: inline; filename="MultiSelect.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "eedee84911ad560ea208a82db2a5eb17acf9ac0d" /* * Copyright 2000-2022 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.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; import com.vaadin.data.HasValue; import com.vaadin.event.selection.MultiSelectionListener; import com.vaadin.shared.Registration; /** * Multi selection component which allows to select and deselect multiple items. * * @author Vaadin Ltd * * @param * the type of the items to select * * @since 8.0 * */ public interface MultiSelect extends HasValue> { /** * Adds the given items to the set of currently selected items. *

* By default this does not clear any previous selection. To do that, use * {@link #deselectAll()}. *

* If the all the items were already selected, this is a NO-OP. *

* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing * to deselect. * * @param items * to add to selection, not {@code null} */ public default void select(T... items) { Objects.requireNonNull(items); Stream.of(items).forEach(Objects::requireNonNull); updateSelection(new LinkedHashSet<>(Arrays.asList(items)), Collections.emptySet()); } /** * Removes the given items from the set of currently selected items. *

* If the none of the items were selected, this is a NO-OP. *

* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing * to select. * * @param items * to remove from selection, not {@code null} */ public default void deselect(T... items) { Objects.requireNonNull(items); Stream.of(items).forEach(Objects::requireNonNull); updateSelection(Collections.emptySet(), new LinkedHashSet<>(Arrays.asList(items))); } /** * Updates the selection by adding and removing the given items from it. *

* If all the added items were already selected and the removed items were * not selected, this is a NO-OP. *

* Duplicate items (in both add & remove sets) are ignored and removed from * the sets. * * @param addedItems * the items to add, not {@code null} * @param removedItems * the items to remove, not {@code null} */ public void updateSelection(Set addedItems, Set removedItems); /** * Returns an immutable set of the currently selected items. It is safe to * invoke other {@code SelectionModel} methods while iterating over the set. *

* Implementation note: the iteration order of the items in the * returned set should be well-defined and documented by the implementing * class. * * @return the items in the current selection, not null */ public Set getSelectedItems(); /** * Deselects all currently selected items. */ public default void deselectAll() { getSelectedItems().forEach(this::deselect); } /** * Returns whether the given item is currently selected. * * @param item * the item to check, not null * @return {@code true} if the item is selected, {@code false} otherwise */ public default boolean isSelected(T item) { return getSelectedItems().contains(item); } /** * Adds a selection listener that will be called when the selection is * changed either by the user or programmatically. * * @param listener * the value change listener, not {@code null} * @return a registration for the listener */ public Registration addSelectionListener( MultiSelectionListener listener); /** * MultiSelect empty value should always be an empty set by default and not * {@code null}. * * @return An empty set, not {@code null} */ @Override public default Set getEmptyValue() { return Collections.emptySet(); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 1249 Content-Disposition: inline; filename="NativeButton.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4bd50577895364051de1100fd5960446325b341b" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.button.NativeButtonState; @SuppressWarnings("serial") public class NativeButton extends Button { public NativeButton() { super(); } public NativeButton(String caption) { super(caption); } public NativeButton(String caption, ClickListener listener) { super(caption, listener); } @Override protected NativeButtonState getState() { return (NativeButtonState) super.getState(); } @Override protected NativeButtonState getState(boolean markAsDirty) { return (NativeButtonState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 7518 Content-Disposition: inline; filename="NativeSelect.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "3c2362576039cb3f8b93b24a208b8a04f146f858" /* * Copyright 2000-2022 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.util.Collection; import java.util.Objects; import com.vaadin.data.HasDataProvider; import com.vaadin.data.provider.DataProvider; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.ui.nativeselect.NativeSelectState; /** * A simple drop-down select component. Represented on the client side by a * "native" HTML {@code * element, setting size for Upload component is not supported. For some * browsers setting size may work to some extend. * * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings("serial") public class Upload extends AbstractComponent implements Component.Focusable, LegacyComponent { /** * Should the field be focused on next repaint? */ private final boolean focus = false; /** * The tab order number of this field. */ private int tabIndex = 0; /** * The output of the upload is redirected to this receiver. */ private Receiver receiver; private boolean isUploading; private long contentLength = -1; private int totalBytes; /** * ProgressListeners to which information about progress is sent during * upload */ private LinkedHashSet progressListeners; private boolean interrupted = false; private boolean notStarted; private int nextid; /** * Creates a new instance of Upload. * * The receiver must be set before performing an upload. */ public Upload() { registerRpc(new UploadServerRpc() { @Override public void change(String filename) { fireEvent(new ChangeEvent(Upload.this, filename)); } @Override public void poll() { // Nothing to do, called only to visit the server } }); } public Upload(String caption, Receiver uploadReceiver) { this(); setCaption(caption); receiver = uploadReceiver; } /** * Invoked when the value of a variable has changed. * * @see com.vaadin.ui.LegacyComponent#changeVariables(java.lang.Object, * java.util.Map) */ @Override public void changeVariables(Object source, Map variables) { if (variables.containsKey("pollForStart")) { int id = (Integer) variables.get("pollForStart"); if (!isUploading && id == nextid) { notStarted = true; markAsDirty(); } else { } } } /** * Paints the content of this component. * * @param target * Target to paint the content on. * @throws PaintException * if the paint operation failed. */ @Override public void paintContent(PaintTarget target) throws PaintException { if (notStarted) { target.addAttribute("notStarted", true); notStarted = false; return; } // The field should be focused if (focus) { target.addAttribute("focus", true); } // The tab ordering number if (tabIndex >= 0) { target.addAttribute("tabindex", tabIndex); } target.addAttribute("state", isUploading); target.addAttribute("nextid", nextid); // Post file to this stream variable target.addVariable(this, "action", getStreamVariable()); } /** * Interface that must be implemented by the upload receivers to provide the * Upload component an output stream to write the uploaded data. * * @author Vaadin Ltd. * @since 3.0 */ @FunctionalInterface public interface Receiver extends Serializable { /** * Invoked when a new upload arrives. * * @param filename * the desired filename of the upload, usually as specified * by the client. * @param mimeType * the MIME type of the uploaded file. * @return Stream to which the uploaded file should be written. */ public OutputStream receiveUpload(String filename, String mimeType); } /* Upload events */ private static final Method UPLOAD_FINISHED_METHOD; private static final Method UPLOAD_FAILED_METHOD; private static final Method UPLOAD_SUCCEEDED_METHOD; private static final Method UPLOAD_STARTED_METHOD; static { try { UPLOAD_FINISHED_METHOD = FinishedListener.class .getDeclaredMethod("uploadFinished", FinishedEvent.class); UPLOAD_FAILED_METHOD = FailedListener.class .getDeclaredMethod("uploadFailed", FailedEvent.class); UPLOAD_STARTED_METHOD = StartedListener.class .getDeclaredMethod("uploadStarted", StartedEvent.class); UPLOAD_SUCCEEDED_METHOD = SucceededListener.class .getDeclaredMethod("uploadSucceeded", SucceededEvent.class); } catch (final NoSuchMethodException e) { // This should never happen throw new RuntimeException( "Internal error finding methods in Upload"); } } /** * Upload.FinishedEvent is sent when the upload receives a file, regardless * of whether the reception was successful or failed. If you wish to * distinguish between the two cases, use either SucceededEvent or * FailedEvent, which are both subclasses of the FinishedEvent. * * @author Vaadin Ltd. * @since 3.0 */ public static class FinishedEvent extends Component.Event { /** * Length of the received file. */ private final long length; /** * MIME type of the received file. */ private final String type; /** * Received file name. */ private final String filename; /** * * @param source * the source of the file. * @param filename * the received file name. * @param mimeType * the MIME type of the received file. * @param length * the length of the received file. */ public FinishedEvent(Upload source, String filename, String mimeType, long length) { super(source); type = mimeType; this.filename = filename; this.length = length; } /** * Uploads where the event occurred. * * @return the Source of the event. */ public Upload getUpload() { return (Upload) getSource(); } /** * Gets the file name. * * @return the filename. */ public String getFilename() { return filename; } /** * Gets the MIME Type of the file. * * @return the MIME type. */ public String getMIMEType() { return type; } /** * Gets the length of the file. * * @return the length. */ public long getLength() { return length; } } /** * Upload.FailedEvent event is sent when the upload is received, but the * reception is interrupted for some reason. * * @author Vaadin Ltd. * @since 3.0 */ public static class FailedEvent extends FinishedEvent { private Exception reason = null; /** * * @param source * @param filename * @param mimeType * @param length * @param reason */ public FailedEvent(Upload source, String filename, String mimeType, long length, Exception reason) { this(source, filename, mimeType, length); this.reason = reason; } /** * * @param source * @param filename * @param mimeType * @param length */ public FailedEvent(Upload source, String filename, String mimeType, long length) { super(source, filename, mimeType, length); } /** * Gets the exception that caused the failure. * * @return the exception that caused the failure, null if n/a */ public Exception getReason() { return reason; } } /** * FailedEvent that indicates that an output stream could not be obtained. */ public static class NoOutputStreamEvent extends FailedEvent { /** * * @param source * @param filename * @param mimeType * @param length */ public NoOutputStreamEvent(Upload source, String filename, String mimeType, long length) { super(source, filename, mimeType, length); } } /** * FailedEvent that indicates that an input stream could not be obtained. */ public static class NoInputStreamEvent extends FailedEvent { /** * * @param source * @param filename * @param mimeType * @param length */ public NoInputStreamEvent(Upload source, String filename, String mimeType, long length) { super(source, filename, mimeType, length); } } /** * Upload.SucceededEvent event is sent when the upload is received * successfully. * * @author Vaadin Ltd. * @since 3.0 */ public static class SucceededEvent extends FinishedEvent { /** * * @param source * @param filename * @param mimeType * @param length */ public SucceededEvent(Upload source, String filename, String mimeType, long length) { super(source, filename, mimeType, length); } } /** * Upload.StartedEvent event is sent when the upload is started to received. * * @author Vaadin Ltd. * @since 5.0 */ public static class StartedEvent extends Component.Event { private final String filename; private final String type; /** * Length of the received file. */ private final long length; /** * * @param source * @param filename * @param mimeType * @param contentLength */ public StartedEvent(Upload source, String filename, String mimeType, long contentLength) { super(source); this.filename = filename; type = mimeType; length = contentLength; } /** * Uploads where the event occurred. * * @return the Source of the event. */ public Upload getUpload() { return (Upload) getSource(); } /** * Gets the file name. * * @return the filename. */ public String getFilename() { return filename; } /** * Gets the MIME Type of the file. * * @return the MIME type. */ public String getMIMEType() { return type; } /** * @return the length of the file that is being uploaded */ public long getContentLength() { return length; } } /** * Upload.ChangeEvent event is sent when the value (filename) of the upload * changes. * * @since 7.2 */ public static class ChangeEvent extends Component.Event { private final String filename; public ChangeEvent(Upload source, String filename) { super(source); this.filename = filename; } /** * Uploads where the event occurred. * * @return the Source of the event. */ @Override public Upload getSource() { return (Upload) super.getSource(); } /** * Gets the file name. * * @return the filename. */ public String getFilename() { return filename; } } /** * Receives the events when the upload starts. * * @author Vaadin Ltd. * @since 5.0 */ @FunctionalInterface public interface StartedListener extends SerializableEventListener { /** * Upload has started. * * @param event * the Upload started event. */ public void uploadStarted(StartedEvent event); } /** * Receives the events when the uploads are ready. * * @author Vaadin Ltd. * @since 3.0 */ @FunctionalInterface public interface FinishedListener extends SerializableEventListener { /** * Upload has finished. * * @param event * the Upload finished event. */ public void uploadFinished(FinishedEvent event); } /** * Receives events when the uploads are finished, but unsuccessful. * * @author Vaadin Ltd. * @since 3.0 */ @FunctionalInterface public interface FailedListener extends SerializableEventListener { /** * Upload has finished unsuccessfully. * * @param event * the Upload failed event. */ public void uploadFailed(FailedEvent event); } /** * Receives events when the uploads are successfully finished. * * @author Vaadin Ltd. * @since 3.0 */ @FunctionalInterface public interface SucceededListener extends SerializableEventListener { /** * Upload successful. * * @param event * the Upload successful event. */ public void uploadSucceeded(SucceededEvent event); } /** * Listener for {@link ChangeEvent}. * * @since 7.2 */ @FunctionalInterface public interface ChangeListener extends SerializableEventListener { Method FILENAME_CHANGED = ReflectTools.findMethod(ChangeListener.class, "filenameChanged", ChangeEvent.class); /** * A file has been selected but upload has not yet started. * * @param event * the change event */ public void filenameChanged(ChangeEvent event); } /** * Adds the upload started event listener. * * @param listener * the Listener to be added, not null * @since 8.0 */ public Registration addStartedListener(StartedListener listener) { return addListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD); } /** * Removes the upload started event listener. * * @param listener * the Listener to be removed. */ @Deprecated public void removeStartedListener(StartedListener listener) { removeListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD); } /** * Adds the upload received event listener. * * @param listener * the Listener to be added, not null * @since 8.0 */ public Registration addFinishedListener(FinishedListener listener) { return addListener(FinishedEvent.class, listener, UPLOAD_FINISHED_METHOD); } /** * Removes the upload received event listener. * * @param listener * the Listener to be removed. */ @Deprecated public void removeFinishedListener(FinishedListener listener) { removeListener(FinishedEvent.class, listener, UPLOAD_FINISHED_METHOD); } /** * Adds the upload interrupted event listener. * * @param listener * the Listener to be added, not null * @since 8.0 */ public Registration addFailedListener(FailedListener listener) { return addListener(FailedEvent.class, listener, UPLOAD_FAILED_METHOD); } /** * Removes the upload interrupted event listener. * * @param listener * the Listener to be removed. */ @Deprecated public void removeFailedListener(FailedListener listener) { removeListener(FailedEvent.class, listener, UPLOAD_FAILED_METHOD); } /** * Adds the upload success event listener. * * @param listener * the Listener to be added, not null * @since 8.0 */ public Registration addSucceededListener(SucceededListener listener) { return addListener(SucceededEvent.class, listener, UPLOAD_SUCCEEDED_METHOD); } /** * Removes the upload success event listener. * * @param listener * the Listener to be removed. */ @Deprecated public void removeSucceededListener(SucceededListener listener) { removeListener(SucceededEvent.class, listener, UPLOAD_SUCCEEDED_METHOD); } /** * Adds the upload progress event listener. * * @param listener * the progress listener to be added * @since 8.0 */ public Registration addProgressListener(ProgressListener listener) { Objects.requireNonNull(listener, "Listener must not be null."); if (progressListeners == null) { progressListeners = new LinkedHashSet<>(); } progressListeners.add(listener); return () -> { if (progressListeners != null) { progressListeners.remove(listener); } }; } /** * Removes the upload progress event listener. * * @param listener * the progress listener to be removed */ @Deprecated public void removeProgressListener(ProgressListener listener) { if (progressListeners != null) { progressListeners.remove(listener); } } /** * Adds a filename change event listener. * * @param listener * the Listener to add, not null * @since 8.0 */ public Registration addChangeListener(ChangeListener listener) { return addListener(EventId.CHANGE, ChangeEvent.class, listener, ChangeListener.FILENAME_CHANGED); } /** * Removes a filename change event listener. * * @param listener * the listener to be removed */ @Deprecated public void removeChangeListener(ChangeListener listener) { super.removeListener(EventId.CHANGE, ChangeEvent.class, listener); } /** * Emit upload received event. * * @param filename * @param mimeType */ protected void fireStarted(String filename, String mimeType) { fireEvent(new Upload.StartedEvent(this, filename, mimeType, contentLength)); } /** * Emits the upload failed event. * * @param filename * @param mimeType * @param length */ protected void fireUploadInterrupted(String filename, String mimeType, long length) { fireEvent(new Upload.FailedEvent(this, filename, mimeType, length)); } protected void fireNoInputStream(String filename, String mimeType, long length) { fireEvent(new Upload.NoInputStreamEvent(this, filename, mimeType, length)); } protected void fireNoOutputStream(String filename, String mimeType, long length) { fireEvent(new Upload.NoOutputStreamEvent(this, filename, mimeType, length)); } protected void fireUploadInterrupted(String filename, String mimeType, long length, Exception e) { fireEvent(new Upload.FailedEvent(this, filename, mimeType, length, e)); } /** * Emits the upload success event. * * @param filename * @param MIMEType * @param length * */ protected void fireUploadSuccess(String filename, String MIMEType, long length) { fireEvent(new Upload.SucceededEvent(this, filename, MIMEType, length)); } /** * Emits the progress event. * * @param totalBytes * bytes received so far * @param contentLength * actual size of the file being uploaded, if known * */ protected void fireUpdateProgress(long totalBytes, long contentLength) { // this is implemented differently than other listeners to maintain // backwards compatibility if (progressListeners != null) { for (ProgressListener l : progressListeners) { l.updateProgress(totalBytes, contentLength); } } } /** * Returns the current receiver. * * @return the StreamVariable. */ public Receiver getReceiver() { return receiver; } /** * Sets the receiver. * * @param receiver * the receiver to set. */ public void setReceiver(Receiver receiver) { this.receiver = receiver; } /** * {@inheritDoc} */ @Override public void focus() { super.focus(); } /** * Gets the Tabulator index of this Focusable component. * * @see com.vaadin.ui.Component.Focusable#getTabIndex() */ @Override public int getTabIndex() { return tabIndex; } /** * Sets the Tabulator index of this Focusable component. * * @see com.vaadin.ui.Component.Focusable#setTabIndex(int) */ @Override public void setTabIndex(int tabIndex) { this.tabIndex = tabIndex; } /** * Go into upload state. This is to prevent double uploading on same * component. * * Warning: this is an internal method used by the framework and should not * be used by user of the Upload component. Using it results in the Upload * component going in wrong state and not working. It is currently public * because it is used by another class. */ public void startUpload() { if (isUploading) { throw new IllegalStateException("uploading already started"); } isUploading = true; nextid++; } /** * Interrupts the upload currently being received. The interruption will be * done by the receiving thread so this method will return immediately and * the actual interrupt will happen a bit later. */ public void interruptUpload() { if (isUploading) { interrupted = true; } } /** * Go into state where new uploading can begin. * * Warning: this is an internal method used by the framework and should not * be used by user of the Upload component. */ private void endUpload() { isUploading = false; contentLength = -1; interrupted = false; markAsDirty(); } public boolean isUploading() { return isUploading; } /** * Gets read bytes of the file currently being uploaded. * * @return bytes */ public long getBytesRead() { return totalBytes; } /** * Returns size of file currently being uploaded. Value sane only during * upload. * * @return size in bytes */ public long getUploadSize() { return contentLength; } /** * ProgressListener receives events to track progress of upload. */ @FunctionalInterface public interface ProgressListener extends Serializable { /** * Updates progress to listener. * * @param readBytes * bytes transferred * @param contentLength * total size of file currently being uploaded, -1 if unknown */ public void updateProgress(long readBytes, long contentLength); } /** * Returns the string rendered into button that fires uploading. * * @return String to be rendered into button that fires uploading */ public String getButtonCaption() { return getState(false).buttonCaption; } /** * Returns the style name rendered into button that fires uploading. * * @return Style name to be rendered into button that fires uploading * @since 8.2 */ public String getButtonStyleName() { return getState(false).buttonStyleName; } /** * Checks whether the caption of the button that fires uploading is rendered * as HTML *

* The default is {@code false}, i.e. to render that caption as plain text. * * @return {@code true} if the caption is rendered as HTML, {@code false} if * rendered as plain text * @since 8.11 */ public boolean isButtonCaptionAsHtml() { return getState(false).buttonCaptionAsHtml; } /** * In addition to the actual file chooser, upload components have button * that starts actual upload progress. This method is used to set text in * that button. *

* In case the button text is set to null, the button is hidden. In this * case developer must explicitly initiate the upload process with * {@link #submitUpload()}. *

* In case the Upload is used in immediate mode using * {@link #setImmediateMode(boolean)}, the file chooser (HTML input with * type "file") is hidden and only the button with this text is shown. *

* *

* Note the string given is set as is to the button. HTML * formatting is not stripped. Be sure to properly validate your value * according to your needs. * * @param buttonCaption * text for upload components button. */ public void setButtonCaption(String buttonCaption) { getState().buttonCaption = buttonCaption; } /** * In addition to the actual file chooser, upload components have button * that starts actual upload progress. This method is used to set a style * name to that button. *

* Note: Unlike {@code Button.setStyleName()} this method overrides all the * styles from the button. If you wish to preserve the default styles, enter * the style name as {@code "v-button yourStyleName"}. * * @param buttonStyleName * style name for upload components button. * @see #setButtonCaption(String) about when the button is shown / hidden. * @since 8.2 */ public void setButtonStyleName(String buttonStyleName) { getState().buttonStyleName = buttonStyleName; } /** * In addition to the actual file chooser, upload components have button * that starts actual upload progress. This method is used to set whether * the caption on that button is rendered as HTML. *

* If set to {@code true}, the caption is rendered in the browser as HTML * and the developer is responsible for ensuring no harmful HTML is used. If * set to {@code false}, the caption is rendered in the browser as plain * text. *

* The default is {@code false}, i.e. to render the caption as plain text. * * @param buttonCaptionAsHtml * {@code true} if the caption is rendered as HTML, {@code false} * if rendered as plain text * @since 8.11 */ public void setButtonCaptionAsHtml(boolean buttonCaptionAsHtml) { getState().buttonCaptionAsHtml = buttonCaptionAsHtml; } /** * Instructs the upload component to send selected file to the server. *

* In case developer wants to use this feature, he/she will most probably * want to hide the upload component's internal submit button by setting its * caption to null with {@link #setButtonCaption(String)} method. *

* Note that the upload runs asynchronously. Developer should use normal * upload listeners to track the process of upload. If the file name field * is empty, no upload will be triggered. *

* Also note that the developer should not remove or modify the upload * component in the same user transaction where the upload submit is * requested. The upload component can be safely hidden or removed once the * upload started event has been fired. */ public void submitUpload() { markAsDirty(); getRpcProxy(UploadClientRpc.class).submitUpload(); } @Override public void markAsDirty() { super.markAsDirty(); } /* * Handle to terminal via Upload monitors and controls the upload during it * is being streamed. */ private com.vaadin.server.StreamVariable streamVariable; protected com.vaadin.server.StreamVariable getStreamVariable() { if (streamVariable == null) { streamVariable = new com.vaadin.server.StreamVariable() { private StreamingStartEvent lastStartedEvent; @Override public boolean listenProgress() { return progressListeners != null && !progressListeners.isEmpty(); } @Override public void onProgress(StreamingProgressEvent event) { fireUpdateProgress(event.getBytesReceived(), event.getContentLength()); } @Override public boolean isInterrupted() { return interrupted; } @Override public OutputStream getOutputStream() { if (getReceiver() == null) { throw new IllegalStateException( "Upload cannot be performed without a receiver set"); } OutputStream receiveUpload = getReceiver().receiveUpload( lastStartedEvent.getFileName(), lastStartedEvent.getMimeType()); lastStartedEvent = null; return receiveUpload; } @Override public void streamingStarted(StreamingStartEvent event) { startUpload(); contentLength = event.getContentLength(); fireStarted(event.getFileName(), event.getMimeType()); lastStartedEvent = event; } @Override public void streamingFinished(StreamingEndEvent event) { fireUploadSuccess(event.getFileName(), event.getMimeType(), event.getContentLength()); endUpload(); if (lastStartedEvent != null) lastStartedEvent.disposeStreamVariable(); } @Override public void streamingFailed(StreamingErrorEvent event) { try { Exception exception = event.getException(); if (exception instanceof NoInputStreamException) { fireNoInputStream(event.getFileName(), event.getMimeType(), 0); } else if (exception instanceof NoOutputStreamException) { fireNoOutputStream(event.getFileName(), event.getMimeType(), 0); } else { fireUploadInterrupted(event.getFileName(), event.getMimeType(), 0, exception); } } finally { endUpload(); if (lastStartedEvent != null) lastStartedEvent.disposeStreamVariable(); } } }; } return streamVariable; } @Override public java.util.Collection getListeners(java.lang.Class eventType) { if (StreamingProgressEvent.class.isAssignableFrom(eventType)) { if (progressListeners == null) { return Collections.emptyList(); } else { return Collections.unmodifiableCollection(progressListeners); } } return super.getListeners(eventType); } /** * Sets the immediate mode of the upload. *

* If the upload is in immediate mode, the file upload is started * immediately after the user has selected the file. *

* If the upload is not in immediate mode, after selecting the file the user * must click another button to start the upload. *

* The default mode of an Upload component is immediate. * * @param immediateMode * {@code true} for immediate mode, {@code false} for not * @since 8.0 */ public void setImmediateMode(boolean immediateMode) { getState().immediateMode = immediateMode; } /** * Returns the immediate mode of the upload. *

* The default mode of an Upload component is immediate. * * @return {@code true} if the upload is in immediate mode, {@code false} if * the upload is not in immediate mode * @see #setImmediateMode(boolean) * @since 8.0 */ public boolean isImmediateMode() { return getState(false).immediateMode; } @Override protected UploadState getState() { return (UploadState) super.getState(); } @Override protected UploadState getState(boolean markAsDirty) { return (UploadState) super.getState(markAsDirty); } /** * Returns the component's list of accepted content-types. According to RFC * 1867, if the attribute is present, the browser might constrain the file * patterns prompted for to match those with the corresponding appropriate * file extensions for the platform. * * @return comma-separated list of desired mime types to be uploaded * @see #setAcceptMimeTypes * @since 8.5 */ public String getAcceptMimeTypes() { return getState(false).acceptMimeTypes; } /** * Sets the component's list of accepted content-types. According to RFC * 1867, if the attribute is present, the browser might constrain the file * patterns prompted for to match those with the corresponding appropriate * file extensions for the platform. Good examples are: {@code image/*} or * {@code image/png,text/plain} * * @param acceptMimeTypes * comma-separated list of desired mime types to be uploaded * @see #getAcceptMimeTypes * @since 8.5 */ public void setAcceptMimeTypes(String acceptMimeTypes) { getState().acceptMimeTypes = acceptMimeTypes; } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2784 Content-Disposition: inline; filename="VerticalLayout.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "72c7abdb256b856168bb5dcd34470924110d0a07" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.orderedlayout.VerticalLayoutState; /** * Vertical layout * * VerticalLayout is a component container, which shows the * subcomponents in the order of their addition (vertically). A vertical layout * is by default 100% wide. * * @author Vaadin Ltd. * @since 5.3 */ @SuppressWarnings("serial") public class VerticalLayout extends AbstractOrderedLayout { /** * Constructs an empty VerticalLayout. */ public VerticalLayout() { setWidth("100%"); setSpacing(true); setMargin(true); } /** * Constructs a VerticalLayout with the given components. The components are * added in the given order. * * @see AbstractOrderedLayout#addComponents(Component...) * * @param children * The components to add. */ public VerticalLayout(Component... children) { this(); addComponents(children); } @Override protected VerticalLayoutState getState() { return (VerticalLayoutState) super.getState(); } @Override protected VerticalLayoutState getState(boolean markAsDirty) { return (VerticalLayoutState) super.getState(markAsDirty); } /** * Adds the given components to this layout and sets them as expanded. The * height of all added child components are set to 100% so that the * expansion will be effective. The height of this layout is also set to * 100% if it is currently undefined. *

* The components are added in the provided order to the end of this layout. * Any components that are already children of this layout will be moved to * new positions. * * @param components * the components to set, not null * @since 8.0 */ public void addComponentsAndExpand(Component... components) { addComponents(components); if (getHeight() < 0) { setHeight(100, Unit.PERCENTAGE); } for (Component child : components) { child.setHeight(100, Unit.PERCENTAGE); setExpandRatio(child, 1); } } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 2125 Content-Disposition: inline; filename="VerticalSplitPanel.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "0dc6e8adc48b53295258132fe1d0150151f5be04" /* * Copyright 2000-2022 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 com.vaadin.shared.ui.splitpanel.VerticalSplitPanelState; /** * A vertical split panel contains two components and lays them vertically. The * first component is above the second component. * *

 *      +--------------------------+
 *      |                          |
 *      |  The first component     |
 *      |                          |
 *      +==========================+  <-- splitter
 *      |                          |
 *      |  The second component    |
 *      |                          |
 *      +--------------------------+
 * 
* */ public class VerticalSplitPanel extends AbstractSplitPanel { public VerticalSplitPanel() { super(); setSizeFull(); } /** * Creates a horizontal split panel containing the given components. * * @param firstComponent * The component to be placed above the splitter * @param secondComponent * The component to be placed below of the splitter */ public VerticalSplitPanel(Component firstComponent, Component secondComponent) { this(); setFirstComponent(firstComponent); setSecondComponent(secondComponent); } @Override protected VerticalSplitPanelState getState() { return (VerticalSplitPanelState) super.getState(); } @Override protected VerticalSplitPanelState getState(boolean markAsDirty) { return (VerticalSplitPanelState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 4346 Content-Disposition: inline; filename="Video.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "25b32b07e2f9e08f58756e0d77db9e47ab826b6f" /* * Copyright 2000-2022 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.util.Collection; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.vaadin.server.Resource; import com.vaadin.shared.ui.video.VideoConstants; import com.vaadin.shared.ui.video.VideoState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * The Video component translates into an HTML5 <video> element and as * such is only supported in browsers that support HTML5 media markup. Browsers * that do not support HTML5 display the text or HTML set by calling * {@link #setAltText(String)}. * * A flash-player fallback can be implemented by setting HTML content allowed ( * {@link #setHtmlContentAllowed(boolean)} and calling * {@link #setAltText(String)} with the flash player markup. An example of flash * fallback can be found at the Mozilla Developer Network. * * Multiple sources can be specified. Which of the sources is used is selected * by the browser depending on which file formats it supports. See * wikipedia for a * table of formats supported by different browsers. * * @author Vaadin Ltd * @since 6.7.0 */ public class Video extends AbstractMedia { public Video() { this("", null); } /** * @param caption * The caption for this video. */ public Video(String caption) { this(caption, null); } /** * @param caption * The caption for this video. * @param source * The Resource containing the video to play. */ public Video(String caption, Resource source) { setCaption(caption); setSource(source); setShowControls(true); } /** * Sets the poster image, which is shown in place of the video before the * user presses play. * * @param poster */ public void setPoster(Resource poster) { setResource(VideoConstants.POSTER_RESOURCE, poster); } /** * @return The poster image. */ public Resource getPoster() { return getResource(VideoConstants.POSTER_RESOURCE); } @Override public void readDesign(Element design, DesignContext designContext) { Elements elems = design.getElementsByTag("poster"); for (Element poster : elems) { if (getPoster() == null && poster.hasAttr("href")) { setPoster(DesignAttributeHandler.readAttribute("href", poster.attributes(), Resource.class)); } poster.remove(); } // Poster is extracted so AbstractMedia does not include it in alt text super.readDesign(design, designContext); } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); if (getPoster() != null) { Attributes attr = design.appendElement("poster").attributes(); DesignAttributeHandler.writeAttribute("href", attr, getPoster(), null, Resource.class, designContext); } } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("poster"); return result; } @Override protected VideoState getState() { return (VideoState) super.getState(); } @Override protected VideoState getState(boolean markAsDirty) { return (VideoState) super.getState(markAsDirty); } } X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 50779 Content-Disposition: inline; filename="Window.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4f79ef483a6a1a772a397341c8108c6e5f94f050" /* * Copyright 2000-2022 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.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.vaadin.event.ConnectorEventListener; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.event.SerializableEventListener; import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; import com.vaadin.event.ShortcutListener; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.shared.ui.window.WindowRole; import com.vaadin.shared.ui.window.WindowServerRpc; import com.vaadin.shared.ui.window.WindowState; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; import com.vaadin.util.ReflectTools; /** * A component that represents a floating popup window that can be added to a * {@link UI}. A window is added to a {@code UI} using * {@link UI#addWindow(Window)}. *

* The contents of a window is set using {@link #setContent(Component)} or by * using the {@link #Window(String, Component)} constructor. *

*

* A window can be positioned on the screen using absolute coordinates (pixels) * or set to be centered using {@link #center()} *

*

* The caption is displayed in the window header. *

*

* In Vaadin versions prior to 7.0.0, Window was also used as application level * windows. This function is now covered by the {@link UI} class. *

* * @author Vaadin Ltd. * @since 3.0 */ @SuppressWarnings({ "serial", "deprecation" }) public class Window extends Panel implements FocusNotifier, BlurNotifier { private WindowServerRpc rpc = new WindowServerRpc() { @Override public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Window.this, mouseDetails)); } @Override public void windowModeChanged(WindowMode newState) { setWindowMode(newState); } @Override public void windowMoved(int x, int y) { if (x != getState(false).positionX) { setPositionX(x); } if (y != getState(false).positionY) { setPositionY(y); } } }; /** * Holds registered CloseShortcut instances for query and later removal */ private List closeShortcuts = new ArrayList<>(4); /** * Used to keep the window order position. Order position for unattached * window is {@code -1}. *

* Window with greatest order position value is on the top and window with 0 * position value is on the bottom. */ private int orderPosition = -1; /** * Creates a new, empty window. */ public Window() { this("", null); } /** * Creates a new, empty window with a given title. * * @param caption * the title of the window. */ public Window(String caption) { this(caption, null); } /** * Creates a new, empty window with the given content and title. * * @param caption * the title of the window. * @param content * the contents of the window */ public Window(String caption, Component content) { super(caption, content); registerRpc(rpc); setSizeUndefined(); setCloseShortcut(KeyCode.ESCAPE); } /* ********************************************************************* */ /* * (non-Javadoc) * * @see com.vaadin.ui.Panel#paintContent(com.vaadin.server.PaintTarget) */ @Override public synchronized void paintContent(PaintTarget target) throws PaintException { if (bringToFront != null) { target.addAttribute("bringToFront", bringToFront.intValue()); bringToFront = null; } // Contents of the window panel is painted super.paintContent(target); } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#setParent(com.vaadin.server. * ClientConnector ) */ @Override public void setParent(HasComponents parent) { if (parent == null || parent instanceof UI) { super.setParent(parent); } else { throw new IllegalArgumentException( "A Window can only be added to a UI using UI.addWindow(Window window)"); } } /* * (non-Javadoc) * * @see com.vaadin.ui.Panel#changeVariables(java.lang.Object, java.util.Map) */ @Override public void changeVariables(Object source, Map variables) { // TODO Are these for top level windows or sub windows? boolean sizeHasChanged = false; // size is handled in super class, but resize events only in windows -> // so detect if size change occurs before super.changeVariables() if (variables.containsKey("height") && (getHeightUnits() != Unit.PIXELS || (Integer) variables.get("height") != getHeight())) { sizeHasChanged = true; } if (variables.containsKey("width") && (getWidthUnits() != Unit.PIXELS || (Integer) variables.get("width") != getWidth())) { sizeHasChanged = true; } super.changeVariables(source, variables); // Positioning final Integer positionx = (Integer) variables.get("positionx"); if (positionx != null) { final int x = positionx.intValue(); // This is information from the client so it is already using the // position. No need to repaint. setPositionX(x < 0 ? -1 : x); } final Integer positiony = (Integer) variables.get("positiony"); if (positiony != null) { final int y = positiony.intValue(); // This is information from the client so it is already using the // position. No need to repaint. setPositionY(y < 0 ? -1 : y); } if (isClosable()) { // Closing final Boolean close = (Boolean) variables.get("close"); if (close != null && close.booleanValue()) { close(); } } // fire event if size has really changed if (sizeHasChanged) { fireResize(); } if (variables.containsKey(FocusEvent.EVENT_ID)) { fireEvent(new FocusEvent(this)); } else if (variables.containsKey(BlurEvent.EVENT_ID)) { fireEvent(new BlurEvent(this)); } } /** * Method that handles window closing (from UI). * *

* By default, windows are removed from their respective UIs and thus * visually closed on browser-side. *

* *

* To react to a window being closed (after it is closed), register a * {@link CloseListener}. *

*/ public void close() { UI uI = getUI(); // Don't do anything if not attached to a UI if (uI != null) { // window is removed from the UI uI.removeWindow(this); } } /** * Gets the distance of Window left border in pixels from left border of the * containing (main window) when the window is in {@link WindowMode#NORMAL}. * * @return the Distance of Window left border in pixels from left border of * the containing (main window).or -1 if unspecified * @since 4.0.0 */ public int getPositionX() { return getState(false).positionX; } /** * Sets the position of the window on the screen using * {@link #setPositionX(int)} and {@link #setPositionY(int)}. * * @since 7.5 * @param x * The new x coordinate for the window * @param y * The new y coordinate for the window */ public void setPosition(int x, int y) { setPositionX(x); setPositionY(y); } /** * Sets the distance of Window left border in pixels from left border of the * containing (main window). Has effect only if in {@link WindowMode#NORMAL} * mode. * * @param positionX * the Distance of Window left border in pixels from left border * of the containing (main window). or -1 if unspecified. * @since 4.0.0 */ public void setPositionX(int positionX) { getState().positionX = positionX; getState().centered = false; } /** * Gets the distance of Window top border in pixels from top border of the * containing (main window) when the window is in {@link WindowMode#NORMAL} * state, or when next set to that state. * * @return Distance of Window top border in pixels from top border of the * containing (main window). or -1 if unspecified * * @since 4.0.0 */ public int getPositionY() { return getState(false).positionY; } /** * Returns the position of this window in the order of all open windows for * this UI. *

* Window with position 0 is on the bottom, and window with greatest * position is at the top. If window has no position (it's not yet attached * or hidden) then position is {@code -1}. * * @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener) * * @since 8.0 * * @return window order position. */ public int getOrderPosition() { return orderPosition; } /** * Sets the distance of Window top border in pixels from top border of the * containing (main window). Has effect only if in {@link WindowMode#NORMAL} * mode. * * @param positionY * the Distance of Window top border in pixels from top border of * the containing (main window). or -1 if unspecified * * @since 4.0.0 */ public void setPositionY(int positionY) { getState().positionY = positionY; getState().centered = false; } private static final Method WINDOW_CLOSE_METHOD; static { try { WINDOW_CLOSE_METHOD = CloseListener.class .getDeclaredMethod("windowClose", CloseEvent.class); } catch (final NoSuchMethodException e) { // This should never happen throw new RuntimeException( "Internal error, window close method not found"); } } public static class CloseEvent extends Component.Event { /** * * @param source */ public CloseEvent(Component source) { super(source); } /** * Gets the Window. * * @return the window. */ public Window getWindow() { return (Window) getSource(); } } /** * Event which is fired when the window order position is changed. * * @see UI.WindowOrderUpdateEvent * * @author Vaadin Ltd * */ public static class WindowOrderChangeEvent extends Component.Event { private final int order; public WindowOrderChangeEvent(Component source, int order) { super(source); this.order = order; } /** * Gets the Window. * * @return the window */ public Window getWindow() { return (Window) getSource(); } /** * Gets the new window order position. * * @return the new order position */ public int getOrder() { return order; } } /** * An interface used for listening to Window order change events. * * @see UI.WindowOrderUpdateListener */ @FunctionalInterface public interface WindowOrderChangeListener extends ConnectorEventListener { public static final Method windowOrderChangeMethod = ReflectTools .findMethod(WindowOrderChangeListener.class, "windowOrderChanged", WindowOrderChangeEvent.class); /** * Called when the window order position is changed. Use * {@link WindowOrderChangeEvent#getWindow()} to get a reference to the * {@link Window} whose order position is changed. Use * {@link WindowOrderChangeEvent#getOrder()} to get a new order * position. * * @param event */ public void windowOrderChanged(WindowOrderChangeEvent event); } /** * Adds a WindowOrderChangeListener to the window. *

* The WindowOrderChangeEvent is fired when the order position is changed. * It can happen when some window (this or other) is brought to front or * detached. *

* The other way to listen positions of all windows in UI is * {@link UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener)} * * @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener) * * @param listener * the WindowModeChangeListener to add. * @since 8.0 */ public Registration addWindowOrderChangeListener( WindowOrderChangeListener listener) { addListener(EventId.WINDOW_ORDER, WindowOrderChangeEvent.class, listener, WindowOrderChangeListener.windowOrderChangeMethod); return () -> removeListener(EventId.WINDOW_ORDER, WindowOrderChangeEvent.class, listener); } protected void fireWindowOrderChange(Integer order) { if (order == null || orderPosition != order) { orderPosition = (order == null) ? -1 : order; fireEvent(new Window.WindowOrderChangeEvent(this, getOrderPosition())); } } /** * An interface used for listening to Window close events. Add the * CloseListener to a window and * {@link CloseListener#windowClose(CloseEvent)} will be called whenever the * user closes the window. * *

* Since Vaadin 6.5, removing a window using {@link #removeWindow(Window)} * fires the CloseListener. *

*/ @FunctionalInterface public interface CloseListener extends SerializableEventListener { /** * Called when the user closes a window. Use * {@link CloseEvent#getWindow()} to get a reference to the * {@link Window} that was closed. * * @param e * The triggered event */ public void windowClose(CloseEvent e); } /** * Adds a CloseListener to the window. * * For a window the CloseListener is fired when the user closes it (clicks * on the close button). * * For a browser level window the CloseListener is fired when the browser * level window is closed. Note that closing a browser level window does not * mean it will be destroyed. Also note that Opera does not send events like * all other browsers and therefore the close listener might not be called * if Opera is used. * *

* Since Vaadin 6.5, removing windows using {@link #removeWindow(Window)} * does fire the CloseListener. *

* * @param listener * the CloseListener to add, not null * @since 8.0 */ public Registration addCloseListener(CloseListener listener) { return addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD); } /** * Removes the CloseListener from the window. * *

* For more information on CloseListeners see {@link CloseListener}. *

* * @param listener * the CloseListener to remove. */ @Deprecated public void removeCloseListener(CloseListener listener) { removeListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD); } protected void fireClose() { fireEvent(new Window.CloseEvent(this)); } /** * Event which is fired when the mode of the Window changes. * * @author Vaadin Ltd * @since 7.1 * */ public static class WindowModeChangeEvent extends Component.Event { private final WindowMode windowMode; /** * * @param source */ public WindowModeChangeEvent(Component source, WindowMode windowMode) { super(source); this.windowMode = windowMode; } /** * Gets the Window. * * @return the window */ public Window getWindow() { return (Window) getSource(); } /** * Gets the new window mode. * * @return the new mode */ public WindowMode getWindowMode() { return windowMode; } } /** * An interface used for listening to Window maximize / restore events. Add * the WindowModeChangeListener to a window and * {@link WindowModeChangeListener#windowModeChanged(WindowModeChangeEvent)} * will be called whenever the window is maximized ( * {@link WindowMode#MAXIMIZED}) or restored ({@link WindowMode#NORMAL} ). */ @FunctionalInterface public interface WindowModeChangeListener extends SerializableEventListener { public static final Method windowModeChangeMethod = ReflectTools .findMethod(WindowModeChangeListener.class, "windowModeChanged", WindowModeChangeEvent.class); /** * Called when the user maximizes / restores a window. Use * {@link WindowModeChangeEvent#getWindow()} to get a reference to the * {@link Window} that was maximized / restored. Use * {@link WindowModeChangeEvent#getWindowMode()} to get a reference to * the new state. * * @param event */ public void windowModeChanged(WindowModeChangeEvent event); } /** * Adds a WindowModeChangeListener to the window. * * The WindowModeChangeEvent is fired when the user changed the display * state by clicking the maximize/restore button or by double clicking on * the window header. The event is also fired if the state is changed using * {@link #setWindowMode(WindowMode)}. * * @param listener * the WindowModeChangeListener to add. * @since 8.0 */ public Registration addWindowModeChangeListener( WindowModeChangeListener listener) { return addListener(WindowModeChangeEvent.class, listener, WindowModeChangeListener.windowModeChangeMethod); } /** * Removes the WindowModeChangeListener from the window. * * @param listener * the WindowModeChangeListener to remove. */ @Deprecated public void removeWindowModeChangeListener( WindowModeChangeListener listener) { removeListener(WindowModeChangeEvent.class, listener, WindowModeChangeListener.windowModeChangeMethod); } protected void fireWindowWindowModeChange() { fireEvent( new Window.WindowModeChangeEvent(this, getState().windowMode)); } /** * Method for the resize event. */ private static final Method WINDOW_RESIZE_METHOD; static { try { WINDOW_RESIZE_METHOD = ResizeListener.class .getDeclaredMethod("windowResized", ResizeEvent.class); } catch (final NoSuchMethodException e) { // This should never happen throw new RuntimeException( "Internal error, window resized method not found"); } } /** * Resize events are fired whenever the client-side fires a resize-event * (e.g. the browser window is resized). The frequency may vary across * browsers. */ public static class ResizeEvent extends Component.Event { /** * * @param source */ public ResizeEvent(Component source) { super(source); } /** * Get the window form which this event originated. * * @return the window */ public Window getWindow() { return (Window) getSource(); } } /** * Listener for window resize events. * * @see com.vaadin.ui.Window.ResizeEvent */ @FunctionalInterface public interface ResizeListener extends SerializableEventListener { public void windowResized(ResizeEvent e); } /** * Add a resize listener. * * @see Registration * * @param listener * the listener to add, not null * @return a registration object for removing the listener * @since 8.0 */ public Registration addResizeListener(ResizeListener listener) { return addListener(ResizeEvent.class, listener, WINDOW_RESIZE_METHOD); } /** * Remove a resize listener. * * @param listener */ @Deprecated public void removeResizeListener(ResizeListener listener) { removeListener(ResizeEvent.class, listener); } /** * Fire the resize event. */ protected void fireResize() { fireEvent(new ResizeEvent(this)); } /** * Used to keep the right order of windows if multiple windows are brought * to front in a single changeset. If this is not used, the order is quite * random (depends on the order getting to dirty list. e.g. which window got * variable changes). */ private Integer bringToFront = null; /** * If there are currently several windows visible, calling this method makes * this window topmost. *

* This method can only be called if this window connected a UI. Else an * illegal state exception is thrown. Also if there are modal windows and * this window is not modal, and illegal state exception is thrown. *

*/ public void bringToFront() { UI uI = getUI(); if (uI == null) { throw new IllegalStateException( "Window must be attached to parent before calling bringToFront method."); } int maxBringToFront = -1; for (Window w : uI.getWindows()) { if (!isModal() && w.isModal()) { throw new IllegalStateException( "The UI contains modal windows, non-modal window cannot be brought to front."); } if (w.bringToFront != null) { maxBringToFront = Math.max(maxBringToFront, w.bringToFront.intValue()); } } bringToFront = Integer.valueOf(maxBringToFront + 1); markAsDirty(); } /** * Sets window modality. When a modal window is open, components outside * that window cannot be accessed. *

* Keyboard navigation is restricted by blocking the tab key at the top and * bottom of the window by activating the tab stop function internally. * * @param modal * true if modality is to be turned on */ public void setModal(boolean modal) { getState().modal = modal; center(); } /** * @return true if this window is modal. */ public boolean isModal() { return getState(false).modal; } /** * Sets window resizable. * * @param resizable * true if resizability is to be turned on */ public void setResizable(boolean resizable) { getState().resizable = resizable; } /** * * @return true if window is resizable by the end-user, otherwise false. */ public boolean isResizable() { return getState(false).resizable; } /** * * @return true if a delay is used before recalculating sizes, false if * sizes are recalculated immediately. */ public boolean isResizeLazy() { return getState(false).resizeLazy; } /** * Should resize operations be lazy, i.e. should there be a delay before * layout sizes are recalculated. Speeds up resize operations in slow UIs * with the penalty of slightly decreased usability. * * Note, some browser send false resize events for the browser window and * are therefore always lazy. * * @param resizeLazy * true to use a delay before recalculating sizes, false to * calculate immediately. */ public void setResizeLazy(boolean resizeLazy) { getState().resizeLazy = resizeLazy; } /** * Sets this window to be centered relative to its parent window. Affects * windows only. If the window is resized as a result of the size of its * content changing, it will keep itself centered as long as its position is * not explicitly changed programmatically or by the user. *

* NOTE: This method has several issues as currently implemented. * Please refer to http://dev.vaadin.com/ticket/8971 for details. */ public void center() { getState().centered = true; } /** * Returns the closable status of the window. If a window is closable, it * typically shows an X in the upper right corner. Clicking on the X sends a * close event to the server. Setting closable to false will remove the X * from the window and prevent the user from closing the window. * * @return true if the window can be closed by the user. */ public boolean isClosable() { return getState(false).closable; } /** * Sets the closable status for the window. If a window is closable it * typically shows an X in the upper right corner. Clicking on the X sends a * close event to the server. Setting closable to false will remove the X * from the window and prevent the user from closing the window. * * @param closable * determines if the window can be closed by the user. */ public void setClosable(boolean closable) { if (closable != isClosable()) { getState().closable = closable; } } /** * Indicates whether a window can be dragged or not. By default a window is * draggable. * * @return {@code true} if window is draggable; {@code false} if not */ public boolean isDraggable() { return getState(false).draggable; } /** * Enables or disables that a window can be dragged (moved) by the user. By * default a window is draggable. *

* * @param draggable * true if the window can be dragged by the user */ public void setDraggable(boolean draggable) { getState().draggable = draggable; } /** * Gets the current mode of the window. * * @see WindowMode * @return the mode of the window. */ public WindowMode getWindowMode() { return getState(false).windowMode; } /** * Sets the mode for the window. * * @see WindowMode * @param windowMode * The new mode */ public void setWindowMode(WindowMode windowMode) { if (windowMode != getWindowMode()) { getState().windowMode = windowMode; fireWindowWindowModeChange(); } } /** * This is the old way of adding a keyboard shortcut to close a * {@link Window} - to preserve compatibility with existing code under the * new functionality, this method now first removes all registered close * shortcuts, then adds the default ESCAPE shortcut key, and then attempts * to add the shortcut provided as parameters to this method. This method, * and its companion {@link #removeCloseShortcut()}, are now considered * deprecated, as their main function is to preserve exact backwards * compatibility with old code. For all new code, use the new keyboard * shortcuts API: {@link #addCloseShortcut(int,int...)}, * {@link #removeCloseShortcut(int,int...)}, * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)} * and {@link #getCloseShortcuts()}. *

* Original description: Makes it possible to close the window by pressing * the given {@link KeyCode} and (optional) {@link ModifierKey}s.
* Note that this shortcut only reacts while the window has focus, closing * itself - if you want to close a window from a UI, use * {@link UI#addAction(com.vaadin.event.Action)} of the UI instead. * * @param keyCode * the keycode for invoking the shortcut * @param modifiers * the (optional) modifiers for invoking the shortcut. Can be set * to null to be explicit about not having modifiers. * * @deprecated Use {@link #addCloseShortcut(int, int...)} instead. */ @Deprecated public void setCloseShortcut(int keyCode, int... modifiers) { removeCloseShortcut(); addCloseShortcut(keyCode, modifiers); } /** * Removes all keyboard shortcuts previously set with * {@link #setCloseShortcut(int, int...)} and * {@link #addCloseShortcut(int, int...)}, then adds the default * {@link KeyCode#ESCAPE} shortcut. *

* This is the old way of removing the (single) keyboard close shortcut, and * is retained only for exact backwards compatibility. For all new code, use * the new keyboard shortcuts API: {@link #addCloseShortcut(int,int...)}, * {@link #removeCloseShortcut(int,int...)}, * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)} * and {@link #getCloseShortcuts()}. * * @deprecated Use {@link #removeCloseShortcut(int, int...)} instead. */ @Deprecated public void removeCloseShortcut() { for (CloseShortcut sc : closeShortcuts) { removeAction(sc); } closeShortcuts.clear(); addCloseShortcut(KeyCode.ESCAPE); } /** * Adds a close shortcut - pressing this key while holding down all (if any) * modifiers specified while this Window is in focus will close the Window. * * @since 7.6 * @param keyCode * the keycode for invoking the shortcut * @param modifiers * the (optional) modifiers for invoking the shortcut. Can be set * to null to be explicit about not having modifiers. */ public void addCloseShortcut(int keyCode, int... modifiers) { // Ignore attempts to re-add existing shortcuts if (hasCloseShortcut(keyCode, modifiers)) { return; } // Actually add the shortcut CloseShortcut shortcut = new CloseShortcut(this, keyCode, modifiers); addAction(shortcut); closeShortcuts.add(shortcut); } /** * Removes a close shortcut previously added with * {@link #addCloseShortcut(int, int...)}. * * @since 7.6 * @param keyCode * the keycode for invoking the shortcut * @param modifiers * the (optional) modifiers for invoking the shortcut. Can be set * to null to be explicit about not having modifiers. */ public void removeCloseShortcut(int keyCode, int... modifiers) { for (CloseShortcut shortcut : closeShortcuts) { if (shortcut.equals(keyCode, modifiers)) { removeAction(shortcut); closeShortcuts.remove(shortcut); return; } } } /** * Removes all close shortcuts. This includes the default ESCAPE shortcut. * It is up to the user to add back any and all keyboard close shortcuts * they may require. For more fine-grained control over shortcuts, use * {@link #removeCloseShortcut(int, int...)}. * * @since 7.6 */ public void removeAllCloseShortcuts() { for (CloseShortcut shortcut : closeShortcuts) { removeAction(shortcut); } closeShortcuts.clear(); } /** * Checks if a close window shortcut key has already been registered. * * @since 7.6 * @param keyCode * the keycode for invoking the shortcut * @param modifiers * the (optional) modifiers for invoking the shortcut. Can be set * to null to be explicit about not having modifiers. * @return true, if an exactly matching shortcut has been registered. */ public boolean hasCloseShortcut(int keyCode, int... modifiers) { for (CloseShortcut shortcut : closeShortcuts) { if (shortcut.equals(keyCode, modifiers)) { return true; } } return false; } /** * Returns an unmodifiable collection of {@link CloseShortcut} objects * currently registered with this {@link Window}. This method is provided * mainly so that users can implement their own serialization routines. To * check if a certain combination of keys has been registered as a close * shortcut, use the {@link #hasCloseShortcut(int, int...)} method instead. * * @since 7.6 * @return an unmodifiable Collection of CloseShortcut objects. */ public Collection getCloseShortcuts() { return Collections.unmodifiableCollection(closeShortcuts); } /** * A {@link ShortcutListener} specifically made to define a keyboard * shortcut that closes the window. * *

     * 
     *  // within the window using helper
     *  window.setCloseShortcut(KeyCode.ESCAPE, null);
     *
     *  // or globally
     *  getUI().addAction(new Window.CloseShortcut(window, KeyCode.ESCAPE));
     * 
     * 
* */ public static class CloseShortcut extends ShortcutListener { protected Window window; /** * Creates a keyboard shortcut for closing the given window using the * shorthand notation defined in {@link ShortcutAction}. * * @param window * to be closed when the shortcut is invoked * @param shorthandCaption * the caption with shortcut keycode and modifiers indicated */ public CloseShortcut(Window window, String shorthandCaption) { super(shorthandCaption); this.window = window; } /** * Creates a keyboard shortcut for closing the given window using the * given {@link KeyCode} and {@link ModifierKey}s. * * @param window * to be closed when the shortcut is invoked * @param keyCode * KeyCode to react to * @param modifiers * optional modifiers for shortcut */ public CloseShortcut(Window window, int keyCode, int... modifiers) { super(null, keyCode, modifiers); this.window = window; } /** * Creates a keyboard shortcut for closing the given window using the * given {@link KeyCode}. * * @param window * to be closed when the shortcut is invoked * @param keyCode * KeyCode to react to */ public CloseShortcut(Window window, int keyCode) { this(window, keyCode, null); } @Override public void handleAction(Object sender, Object target) { if (window.isClosable()) { window.close(); } } public boolean equals(int keyCode, int... modifiers) { if (keyCode != getKeyCode()) { return false; } if (getModifiers() != null) { int[] mods = null; if (modifiers != null) { // Modifiers provided by the parent ShortcutAction class // are guaranteed to be sorted. We still need to sort // the modifiers passed in as argument. mods = Arrays.copyOf(modifiers, modifiers.length); Arrays.sort(mods); } return Arrays.equals(mods, getModifiers()); } return true; } } /* * (non-Javadoc) * * @see * com.vaadin.event.FieldEvents.FocusNotifier#addFocusListener(com.vaadin * .event.FieldEvents.FocusListener) */ @Override public Registration addFocusListener(FocusListener listener) { return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); } /* * (non-Javadoc) * * @see * com.vaadin.event.FieldEvents.BlurNotifier#addBlurListener(com.vaadin. * event.FieldEvents.BlurListener) */ @Override public Registration addBlurListener(BlurListener listener) { return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod); } /** * {@inheritDoc} * * Cause the window to be brought on top of other windows and gain keyboard * focus. */ @Override public void focus() { /* * When focusing a window it basically means it should be brought to the * front. Instead of just moving the keyboard focus we focus the window * and bring it top-most. */ super.focus(); bringToFront(); } @Override protected WindowState getState() { return (WindowState) super.getState(); } @Override protected WindowState getState(boolean markAsDirty) { return (WindowState) super.getState(markAsDirty); } /** * Allows to specify which components contain the description for the * window. Text contained in these components will be read by assistive * devices when it is opened. * * @param components * the components to use as description */ public void setAssistiveDescription(Component... components) { if (components == null) { throw new IllegalArgumentException( "Parameter connectors must be non-null"); } else { getState().contentDescription = components; } } /** * Gets the components that are used as assistive description. Text * contained in these components will be read by assistive devices when the * window is opened. * * @return array of previously set components */ public Component[] getAssistiveDescription() { Connector[] contentDescription = getState(false).contentDescription; if (contentDescription == null) { return null; } Component[] target = new Component[contentDescription.length]; System.arraycopy(contentDescription, 0, target, 0, contentDescription.length); return target; } /** * Sets the accessibility prefix for the window caption. * * This prefix is read to assistive device users before the window caption, * but not visible on the page. * * @param prefix * String that is placed before the window caption */ public void setAssistivePrefix(String prefix) { getState().assistivePrefix = prefix; } /** * Gets the accessibility prefix for the window caption. * * This prefix is read to assistive device users before the window caption, * but not visible on the page. * * @return The accessibility prefix */ public String getAssistivePrefix() { return getState(false).assistivePrefix; } /** * Sets the accessibility postfix for the window caption. * * This postfix is read to assistive device users after the window caption, * but not visible on the page. * * @param assistivePostfix * String that is placed after the window caption */ public void setAssistivePostfix(String assistivePostfix) { getState().assistivePostfix = assistivePostfix; } /** * Gets the accessibility postfix for the window caption. * * This postfix is read to assistive device users after the window caption, * but not visible on the page. * * @return The accessibility postfix */ public String getAssistivePostfix() { return getState(false).assistivePostfix; } /** * Sets the WAI-ARIA role the window. * * This role defines how an assistive device handles a window. Available * roles are alertdialog and dialog (@see * Roles * Model). * * The default role is dialog. * * @param role * WAI-ARIA role to set for the window */ public void setAssistiveRole(WindowRole role) { getState().role = role; } /** * Gets the WAI-ARIA role the window. * * This role defines how an assistive device handles a window. Available * roles are alertdialog and dialog (@see * Roles * Model). * * @return WAI-ARIA role set for the window */ public WindowRole getAssistiveRole() { return getState(false).role; } /** * Set if it should be prevented to set the focus to a component outside a * non-modal window with the tab key. *

* This is meant to help users of assistive devices to not leaving the * window unintentionally. *

* For modal windows, this function is activated automatically, while * preserving the stored value of tabStop. * * @param tabStop * true to keep the focus inside the window when reaching the top * or bottom, false (default) to allow leaving the window */ public void setTabStopEnabled(boolean tabStop) { getState().assistiveTabStop = tabStop; } /** * Get if it is prevented to leave a window with the tab key. * * @return true when the focus is limited to inside the window, false when * focus can leave the window */ public boolean isTabStopEnabled() { return getState(false).assistiveTabStop; } /** * Sets the message that is provided to users of assistive devices when the * user reaches the top of the window when leaving a window with the tab key * is prevented. *

* This message is not visible on the screen. * * @param topMessage * String provided when the user navigates with Shift-Tab keys to * the top of the window */ public void setTabStopTopAssistiveText(String topMessage) { getState().assistiveTabStopTopText = topMessage; } /** * Sets the message that is provided to users of assistive devices when the * user reaches the bottom of the window when leaving a window with the tab * key is prevented. *

* This message is not visible on the screen. * * @param bottomMessage * String provided when the user navigates with the Tab key to * the bottom of the window */ public void setTabStopBottomAssistiveText(String bottomMessage) { getState().assistiveTabStopBottomText = bottomMessage; } /** * Gets the message that is provided to users of assistive devices when the * user reaches the top of the window when leaving a window with the tab key * is prevented. * * @return the top message */ public String getTabStopTopAssistiveText() { return getState(false).assistiveTabStopTopText; } /** * Gets the message that is provided to users of assistive devices when the * user reaches the bottom of the window when leaving a window with the tab * key is prevented. * * @return the bottom message */ public String getTabStopBottomAssistiveText() { return getState(false).assistiveTabStopBottomText; } @Override public void readDesign(Element design, DesignContext context) { super.readDesign(design, context); if (design.hasAttr("center")) { center(); } if (design.hasAttr("position")) { String[] position = design.attr("position").split(","); setPositionX(Integer.parseInt(position[0])); setPositionY(Integer.parseInt(position[1])); } // Parse shortcuts if defined, otherwise rely on default behavior if (design.hasAttr("close-shortcut")) { // Parse shortcuts String[] shortcutStrings = DesignAttributeHandler .readAttribute("close-shortcut", design.attributes(), String.class) .split("\\s+"); removeAllCloseShortcuts(); for (String part : shortcutStrings) { if (!part.isEmpty()) { ShortcutAction shortcut = DesignAttributeHandler .getFormatter() .parse(part.trim(), ShortcutAction.class); addCloseShortcut(shortcut.getKeyCode(), shortcut.getModifiers()); } } } } /** * Reads the content and possible assistive descriptions from the list of * child elements of a design. If an element has an * {@code :assistive-description} attribute, adds the parsed component to * the list of components used as the assistive description of this Window. * Otherwise, sets the component as the content of this Window. If there are * multiple non-description elements, throws a DesignException. * * @param children * child elements in a design * @param context * the DesignContext instance used to parse the design * * @throws DesignException * if there are multiple non-description child elements * @throws DesignException * if a child element could not be parsed as a Component * * @see #setContent(Component) * @see #setAssistiveDescription(Component...) */ @Override protected void readDesignChildren(Elements children, DesignContext context) { List descriptions = new ArrayList<>(); Elements content = new Elements(); for (Element child : children) { if (child.hasAttr(":assistive-description")) { descriptions.add(context.readDesign(child)); } else { content.add(child); } } super.readDesignChildren(content, context); setAssistiveDescription( descriptions.toArray(new Component[descriptions.size()])); } @Override public void writeDesign(Element design, DesignContext context) { super.writeDesign(design, context); Window def = context.getDefaultInstance(this); if (getState().centered) { design.attr("center", true); } DesignAttributeHandler.writeAttribute("position", design.attributes(), getPosition(), def.getPosition(), String.class, context); // Process keyboard shortcuts if (closeShortcuts.size() == 1 && hasCloseShortcut(KeyCode.ESCAPE)) { // By default, we won't write anything if we're relying on default // shortcut behavior } else { // Dump all close shortcuts to a string... String attrString = ""; // TODO: add canonical support for array data in XML attributes for (CloseShortcut shortcut : closeShortcuts) { String shortcutString = DesignAttributeHandler.getFormatter() .format(shortcut, CloseShortcut.class); attrString += shortcutString + " "; } // Write everything except the last "," to the design DesignAttributeHandler.writeAttribute("close-shortcut", design.attributes(), attrString.trim(), null, String.class, context); } for (Component c : getAssistiveDescription()) { Element child = context.createElement(c) .attr(":assistive-description", true); design.appendChild(child); } } private String getPosition() { return getPositionX() + "," + getPositionY(); } @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); result.add("center"); result.add("position"); result.add("position-y"); result.add("position-x"); result.add("close-shortcut"); return result; } } Content-Type: text/plain; charset=UTF-8 Content-Length: 50779 Content-Disposition: inline; filename="Window.java" Last-Modified: Sun, 27 Apr 2025 23:49:14 GMT Expires: Sun, 27 Apr 2025 23:54:14 GMT ETag: "4e0726f173391be1f04aba67c484300dccad48a8" /server/src/main/java/com/vaadin/ui/components/

/server/src/main/java/com/vaadin/ui/components/