aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com/vaadin/ui/Window.java
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-03-10 22:40:51 +0200
committerTeemu Suo-Anttila <teemusa@vaadin.com>2016-03-14 07:59:12 +0200
commita6653d3fe49e6a97468ac09f7f2f4d621bea1078 (patch)
tree96c82e20ca6551ee4c14c8877f0258b25c63cddf /server/src/main/java/com/vaadin/ui/Window.java
parentf7e57d77ce621ee39167369c31d989edc5633266 (diff)
downloadvaadin-framework-a6653d3fe49e6a97468ac09f7f2f4d621bea1078.tar.gz
vaadin-framework-a6653d3fe49e6a97468ac09f7f2f4d621bea1078.zip
Migrate vaadin-server build to maven
Change-Id: I5c740f4e9cb28103bab199f9a552153d82277e7e
Diffstat (limited to 'server/src/main/java/com/vaadin/ui/Window.java')
-rw-r--r--server/src/main/java/com/vaadin/ui/Window.java1503
1 files changed, 1503 insertions, 0 deletions
diff --git a/server/src/main/java/com/vaadin/ui/Window.java b/server/src/main/java/com/vaadin/ui/Window.java
new file mode 100644
index 0000000000..b5cd384f53
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/Window.java
@@ -0,0 +1,1503 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.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.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.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.MouseEventDetails;
+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)}. </p>
+ * <p>
+ * The contents of a window is set using {@link #setContent(Component)} or by
+ * using the {@link #Window(String, Component)} constructor.
+ * </p>
+ * <p>
+ * A window can be positioned on the screen using absolute coordinates (pixels)
+ * or set to be centered using {@link #center()}
+ * </p>
+ * <p>
+ * The caption is displayed in the window header.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+@SuppressWarnings({ "serial", "deprecation" })
+public class Window extends Panel implements FocusNotifier, BlurNotifier,
+ LegacyComponent {
+
+ 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<CloseShortcut> closeShortcuts = new ArrayList<CloseShortcut>(4);
+
+ /**
+ * 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<String, Object> 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).
+ *
+ * <p>
+ * By default, windows are removed from their respective UIs and thus
+ * visually closed on browser-side.
+ * </p>
+ *
+ * <p>
+ * To react to a window being closed (after it is closed), register a
+ * {@link CloseListener}.
+ * </p>
+ */
+ 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;
+ }
+
+ /**
+ * 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", new Class[] { CloseEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.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();
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * <p>
+ * Since Vaadin 6.5, removing a window using {@link #removeWindow(Window)}
+ * fires the CloseListener.
+ * </p>
+ */
+ public interface CloseListener extends Serializable {
+ /**
+ * Called when the user closes a window. Use
+ * {@link CloseEvent#getWindow()} to get a reference to the
+ * {@link Window} that was closed.
+ *
+ * @param e
+ * Event containing
+ */
+ 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.
+ *
+ * <p>
+ * Since Vaadin 6.5, removing windows using {@link #removeWindow(Window)}
+ * does fire the CloseListener.
+ * </p>
+ *
+ * @param listener
+ * the CloseListener to add.
+ */
+ public void addCloseListener(CloseListener listener) {
+ addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addCloseListener(CloseListener)}
+ **/
+ @Deprecated
+ public void addListener(CloseListener listener) {
+ addCloseListener(listener);
+ }
+
+ /**
+ * Removes the CloseListener from the window.
+ *
+ * <p>
+ * For more information on CloseListeners see {@link CloseListener}.
+ * </p>
+ *
+ * @param listener
+ * the CloseListener to remove.
+ */
+ public void removeCloseListener(CloseListener listener) {
+ removeListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeCloseListener(CloseListener)}
+ **/
+ @Deprecated
+ public void removeListener(CloseListener listener) {
+ removeCloseListener(listener);
+ }
+
+ 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} ).
+ */
+ public interface WindowModeChangeListener extends Serializable {
+
+ 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.
+ */
+ public void addWindowModeChangeListener(WindowModeChangeListener listener) {
+ addListener(WindowModeChangeEvent.class, listener,
+ WindowModeChangeListener.windowModeChangeMethod);
+ }
+
+ /**
+ * Removes the WindowModeChangeListener from the window.
+ *
+ * @param listener
+ * the WindowModeChangeListener to remove.
+ */
+ 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", new Class[] { ResizeEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.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
+ */
+ public interface ResizeListener extends Serializable {
+ public void windowResized(ResizeEvent e);
+ }
+
+ /**
+ * Add a resize listener.
+ *
+ * @param listener
+ */
+ public void addResizeListener(ResizeListener listener) {
+ addListener(ResizeEvent.class, listener, WINDOW_RESIZE_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addResizeListener(ResizeListener)}
+ **/
+ @Deprecated
+ public void addListener(ResizeListener listener) {
+ addResizeListener(listener);
+ }
+
+ /**
+ * Remove a resize listener.
+ *
+ * @param listener
+ */
+ public void removeResizeListener(ResizeListener listener) {
+ removeListener(ResizeEvent.class, listener);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeResizeListener(ResizeListener)}
+ **/
+ @Deprecated
+ public void removeListener(ResizeListener listener) {
+ removeResizeListener(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.
+ * <p>
+ * 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.
+ * <p>
+ */
+ 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.
+ * <p>
+ * 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.
+ * <p>
+ * <b>NOTE:</b> 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.
+ *
+ * Note! For historical reasons readonly controls the closability of the
+ * window and therefore readonly and closable affect each other. Setting
+ * readonly to true will set closable to false and vice versa.
+ * <p/>
+ *
+ * @return true if the window can be closed by the user.
+ */
+ public boolean isClosable() {
+ return !isReadOnly();
+ }
+
+ /**
+ * 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.
+ *
+ * Note! For historical reasons readonly controls the closability of the
+ * window and therefore readonly and closable affect each other. Setting
+ * readonly to true will set closable to false and vice versa.
+ * <p/>
+ *
+ * @param closable
+ * determines if the window can be closed by the user.
+ */
+ public void setClosable(boolean closable) {
+ setReadOnly(!closable);
+ }
+
+ /**
+ * Indicates whether a window can be dragged or not. By default a window is
+ * draggable.
+ * <p/>
+ *
+ * @param draggable
+ * true if the window can be dragged by the user
+ */
+ 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.
+ * <p/>
+ *
+ * @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()}.
+ * <p>
+ * Original description: Makes it possible to close the window by pressing
+ * the given {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
+ * 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.
+ * <p>
+ * 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 (int i = 0; i < closeShortcuts.size(); ++i) {
+ CloseShortcut sc = closeShortcuts.get(i);
+ 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<CloseShortcut> getCloseShortcuts() {
+ return Collections.unmodifiableCollection(closeShortcuts);
+ }
+
+ /**
+ * A {@link ShortcutListener} specifically made to define a keyboard
+ * shortcut that closes the window.
+ *
+ * <pre>
+ * <code>
+ * // within the window using helper
+ * window.setCloseShortcut(KeyCode.ESCAPE, null);
+ *
+ * // or globally
+ * getUI().addAction(new Window.CloseShortcut(window, KeyCode.ESCAPE));
+ * </code>
+ * </pre>
+ *
+ */
+ 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) {
+ 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 void addFocusListener(FocusListener listener) {
+ addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
+ FocusListener.focusMethod);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addFocusListener(FocusListener)}
+ **/
+ @Override
+ @Deprecated
+ public void addListener(FocusListener listener) {
+ addFocusListener(listener);
+ }
+
+ @Override
+ public void removeFocusListener(FocusListener listener) {
+ removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeFocusListener(FocusListener)}
+ **/
+ @Override
+ @Deprecated
+ public void removeListener(FocusListener listener) {
+ removeFocusListener(listener);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.event.FieldEvents.BlurNotifier#addBlurListener(com.vaadin.
+ * event.FieldEvents.BlurListener)
+ */
+ @Override
+ public void addBlurListener(BlurListener listener) {
+ addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
+ BlurListener.blurMethod);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by {@link #addBlurListener(BlurListener)}
+ **/
+ @Override
+ @Deprecated
+ public void addListener(BlurListener listener) {
+ addBlurListener(listener);
+ }
+
+ @Override
+ public void removeBlurListener(BlurListener listener) {
+ removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeBlurListener(BlurListener)}
+ **/
+ @Override
+ @Deprecated
+ public void removeListener(BlurListener listener) {
+ removeBlurListener(listener);
+ }
+
+ /**
+ * {@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 prefix
+ * 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 <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * 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 <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * @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.
+ * <p>
+ * This is meant to help users of assistive devices to not leaving the
+ * window unintentionally.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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<Component> descriptions = new ArrayList<Component>();
+ 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[0]));
+ }
+
+ @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);
+
+ // 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);
+ }
+
+ 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<String> getCustomAttributes() {
+ Collection<String> result = super.getCustomAttributes();
+ result.add("center");
+ result.add("position");
+ result.add("position-y");
+ result.add("position-x");
+ result.add("close-shortcut");
+ return result;
+ }
+}