/*
* Copyright 2010 IT Mill 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.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import com.vaadin.Application;
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.ShortcutAction;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.event.ShortcutListener;
import com.vaadin.terminal.DownloadStream;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.ParameterHandler;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.Terminal;
import com.vaadin.terminal.URIHandler;
import com.vaadin.terminal.gwt.client.ui.VWindow;
/**
* A component that represents an application (browser native) window or a sub
* window.
* <p>
* If the window is a application window or a sub window depends on how it is
* added to the application. Adding a {@code Window} to a {@code Window} using
* {@link Window#addWindow(Window)} makes it a sub window and adding a
* {@code Window} to the {@code Application} using
* {@link Application#addWindow(Window)} makes it an application window.
* </p>
* <p>
* An application window is the base of any view in a Vaadin application. All
* applications contain a main application window (set using
* {@link Application#setMainWindow(Window)} which is what is initially shown to
* the user. The contents of a window is set using
* {@link #setContent(ComponentContainer)}. The contents can in turn contain
* other components. For multi-tab applications there is one window instance per
* opened tab.
* </p>
* <p>
* A sub window is floating popup style window that can be added to an
* application window. Like the application window its content is set using
* {@link #setContent(ComponentContainer)}. A sub window can be positioned on
* the screen using absolute coordinates (pixels). The default content of the
* Window is set to be suitable for application windows. For sub windows it
* might be necessary to set the size of the content to work as expected.
* </p>
* <p>
* Window caption is displayed in the browser title bar for application level
* windows and in the window header for sub windows.
* </p>
* <p>
* Certain methods in this class are only meaningful for sub windows and other
* parts only for application windows. These are marked using <b>Sub window
* only</b> and <b>Application window only</b> respectively in the javadoc.
* </p>
* <p>
* Sub window is to be split into a separate component in Vaadin 7.
* </p>
*
* @author IT Mill Ltd.
* @version
* 6.5.4
* @since 3.0
*/
@SuppressWarnings("serial")
@ClientWidget(VWindow.class)
public class Window extends Panel implements URIHandler, ParameterHandler,
FocusNotifier, BlurNotifier {
/**
* <b>Application window only</b>. A border style used for opening resources
* in a window without a border.
*/
public static final int BORDER_NONE = 0;
/**
* <b>Application window only</b>. A border style used for opening resources
* in a window with a minimal border.
*/
public static final int BORDER_MINIMAL = 1;
/**
* <b>Application window only</b>. A border style that indicates that the
* default border style should be used when opening resources.
*/
public static final int BORDER_DEFAULT = 2;
/**
* <b>Application window only</b>. The user terminal for this window.
*/
private Terminal terminal = null;
/**
* <b>Application window only</b>. The application this window is attached
* to or null.
*/
private Application application = null;
/**
* <b>Application window only</b>. List of URI handlers for this window.
*/
private LinkedList<URIHandler> uriHandlerList = null;
/**
* <b>Application window only</b>. List of parameter handlers for this
* window.
*/
private LinkedList<ParameterHandler> parameterHandlerList = null;
/**
* <b>Application window only</b>. List of sub windows in this window. A sub
* window cannot have other sub windows.
*/
private final LinkedHashSet<Window> subwindows = new LinkedHashSet<Window>();
/**
* <b>Application window only</b>. Explicitly specified theme of this window
* or null if the application theme should be used.
*/
private String theme = null;
/**
* <b>Application window only</b>. Resources to be opened automatically on
* next repaint. The list is automatically cleared when it has been sent to
* the client.
*/
private final LinkedList<OpenResource> openList = new LinkedList<OpenResource>();
/**
* <b>Application window only</b>. Unique name of the window used to
* identify it.
*/
private String name = null;
/**
* <b>Application window only.</b> Border mode of the Window.
*/
private int border = BORDER_DEFAULT;
/**
* <b>Sub window only</b>. Top offset in pixels for the sub window (relative
* to the parent application window) or -1 if unspecified.
*/
private int positionY = -1;
/**
* <b>Sub window only</b>. Left offset in pixels for the sub window
* (relative to the parent application window) or -1 if unspecified.
*/
private int positionX = -1;
/**
* <b>Application window only</b>. A list of notifications that are waiting
* to be sent to the client. Cleared (set to null) when the notifications
* have been sent.
*/
private LinkedList<Notification> notifications;
/**
* <b>Sub window only</b>. Modality flag for sub window.
*/
private boolean modal = false;
/**
* <b>Sub window only</b>. Controls if the end user can resize the window.
*/
private boolean resizable = true;
/**
* <b>Sub window only</b>. Controls if the end user can move the window by
* dragging.
*/
private boolean draggable = true;
/**
* <b>Sub window only</b>. Flag which is true if the window is centered on
* the screen.
*/
private boolean centerRequested = false;
/**
* Component that should be focused after the next repaint. Null if no focus
* change should take place.
*/
private Focusable pendingFocus;
/**
* <b>Application window only</b>. A list of javascript commands that are
* waiting to be sent to the client. Cleared (set to null) when the commands
* have been sent.
*/
private ArrayList<String> jsExecQueue = null;
/**
* The component that should be scrolled into view after the next repaint.
* Null if nothing should be scrolled into view.
*/
private Component scrollIntoView;
/**
* Creates a new unnamed window with a default layout.
*/
public Window() {
this("", null);
}
/**
* Creates a new unnamed window with a default layout and given title.
*
* @param caption
* the title of the window.
*/
public Window(String caption) {
this(caption, null);
}
/**
* Creates a new unnamed 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, ComponentContainer content) {
super(caption, content);
setScrollable(true);
setSizeUndefined();
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.Panel#addComponent(com.vaadin.ui.Component)
*/
@Override
public void addComponent(Component c) {
if (c instanceof Window) {
throw new IllegalArgumentException(
"Window cannot be added to another via addComponent. "
+ "Use addWindow(Window) instead.");
}
super.addComponent(c);
}
/**
* <b>Application window only</b>. Gets the user terminal.
*
* @return the user terminal
*/
public Terminal getTerminal() {
return terminal;
}
/* ********************************************************************* */
/**
* Gets the parent window of the component.
* <p>
* This is always the window itself.
* </p>
*
* @see Component#getWindow()
* @return the window itself
*/
@Override
public final Window getWindow() {
return this;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.AbstractComponent#getApplication()
*/
@Override
public final Application getApplication() {
if (getParent() == null) {
return application;
}
return (getParent()).getApplication();
}
/**
* Gets the parent component of the window.
*
* <p>
* The parent of an application window is always null. The parent of a sub
* window is the application window the sub window is attached to.
* </p>
*
* @return the parent window
* @see Component#getParent()
*/
@Override
public final Window getParent() {
return (Window) super.getParent();
}
/* ********************************************************************* */
/**
* <b>Application window only</b>. Adds a new URI handler to this window. If
* this is a sub window the URI handler is attached to the parent
* application window.
*
* @param handler
* the URI handler to add.
*/
public void addURIHandler(URIHandler handler) {
if (getParent() != null) {
// this is subwindow, attach to main level instead
// TODO hold internal list also and remove on detach
Window mainWindow = getParent();
mainWindow.addURIHandler(handler);
} else {
if (uriHandlerList == null) {
uriHandlerList = new LinkedList<URIHandler>();
}
synchronized (uriHandlerList) {
if (!uriHandlerList.contains(handler)) {
uriHandlerList.addLast(handler);
}
}
}
}
/**
* <b>Application window only</b>. Removes the URI handler from this window.
* If this is a sub window the URI handler is removed from the parent
* application window.
*
* @param handler
* the URI handler to remove.
*/
public void removeURIHandler(URIHandler handler) {
if (getParent() != null) {
// this is subwindow
Window mainWindow = getParent();
mainWindow.removeURIHandler(handler);
} else {
if (handler == null || uriHandlerList == null) {
return;
}
synchronized (uriHandlerList) {
uriHandlerList.remove(handler);
if (uriHandlerList.isEmpty()) {
uriHandlerList = null;
}
}
}
}
/**
* <b>Application window only</b>. Handles an URI by passing the URI to all
* URI handlers defined using {@link #addURIHandler(URIHandler)}. All URI
* handlers are called for each URI but no more than one handler may return
* a {@link DownloadStream}. If more than one stream is returned a
* {@code RuntimeException} is thrown.
*
* @param context
* The URL of the application
* @param relativeUri
* The URI relative to {@code context}
* @return A {@code DownloadStream} that one of the URI handlers returned,
* null if no {@code DownloadStream} was returned.
*/
public DownloadStream handleURI(URL context, String relativeUri) {
DownloadStream result = null;
if (uriHandlerList != null) {
Object[] handlers;
synchronized (uriHandlerList) {
handlers = uriHandlerList.toArray();
}
for (int i = 0; i < handlers.length; i++) {
final DownloadStream ds = ((URIHandler) handlers[i]).handleURI(
context, relativeUri);
if (ds != null) {
if (result != null) {
throw new RuntimeException("handleURI for " + context
+ " uri: '" + relativeUri
+ "' returns ambigious result.");
}
result = ds;
}
}
}
return result;
}
/* ********************************************************************* */
/**
* <b>Application window only</b>. Adds a new parameter handler to this
* window. If this is a sub window the parameter handler is attached to the
* parent application window.
*
* @param handler
* the parameter handler to add.
*/
public void addParameterHandler(ParameterHandler handler) {
if (getParent() != null) {
// this is subwindow
// TODO hold internal list also and remove on detach
Window mainWindow = getParent();
mainWindow.addParameterHandler(handler);
} else {
if (parameterHandlerList == null) {
parameterHandlerList = new LinkedList<ParameterHandler>();
}
synchronized (parameterHandlerList) {
if (!parameterHandlerList.contains(handler)) {
parameterHandlerList.addLast(handler);
}
}
}
}
/**
* <b>Application window only</b>. Removes the parameter handler from this
* window. If this is a sub window the parameter handler is removed from the
* parent application window.
*
* @param handler
* the parameter handler to remove.
*/
public void removeParameterHandler(ParameterHandler handler) {
if (getParent() != null) {
// this is subwindow
Window mainWindow = getParent();
mainWindow.removeParameterHandler(handler);
} else {
if (handler == null || parameterHandlerList == null) {
return;
}
synchronized (parameterHandlerList) {
parameterHandlerList.remove(handler);
if (parameterHandlerList.isEmpty()) {
parameterHandlerList = null;
}
}
}
}
/**
* <b>Application window only</b>. Handles parameters by passing the
* parameters to all {@code ParameterHandler}s defined using
* {@link #addParameterHandler(ParameterHandler)}. All
* {@code ParameterHandler}s are called for each set of parameters.
*
* @param parameters
* a map containing the parameter names and values
* @see ParameterHandler#handleParameters(Map)
*/
public void handleParameters(Map<String, String[]> parameters) {
if (parameterHandlerList != null) {
Object[] handlers;
synchronized (parameterHandlerList) {
handlers = parameterHandlerList.toArray();
}
for (int i = 0; i < handlers.length; i++) {
((ParameterHandler) handlers[i]).handleParameters(parameters);
}
}
}
/* ********************************************************************* */
/**
* <b>Application window only</b>. Gets the theme for this window.
* <p>
* If the theme for this window is not explicitly set, the application theme
* name is returned. If the window is not attached to an application, the
* terminal default theme name is returned. If the theme name cannot be
* determined, null is returned
* </p>
* <p>
* Subwindows do not support themes and return the theme used by the parent
* window
* </p>
*
* @return the name of the theme used for the window
*/
public String getTheme() {
if (getParent() != null) {
return (getParent()).getTheme();
}
if (theme != null) {
return theme;
}
if ((application != null) && (application.getTheme() != null)) {
return application.getTheme();
}
if (terminal != null) {
return terminal.getDefaultTheme();
}
return null;
}
/**
* <b>Application window only</b>. Sets the name of the theme to use for
* this window. Changing the theme will cause the page to be reloaded.
*
* @param theme
* the name of the new theme for this window or null to use the
* application theme.
*/
public void setTheme(String theme) {
if (getParent() != null) {
throw new UnsupportedOperationException(
"Setting theme for sub-windows is not supported.");
}
this.theme = theme;
requestRepaint();
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.Panel#paintContent(com.vaadin.terminal.PaintTarget)
*/
@Override
public synchronized void paintContent(PaintTarget target)
throws PaintException {
// Sets the window name
final String name = getName();
target.addAttribute("name", name == null ? "" : name);
// Sets the window theme
final String theme = getTheme();
target.addAttribute("theme", theme == null ? "" : theme);
if (modal) {
target.addAttribute("modal", true);
}
if (resizable) {
target.addAttribute("resizable", true);
}
if (!draggable) {
// Inverted to prevent an extra attribute for almost all sub windows
target.addAttribute("fixedposition", true);
}
if (bringToFront != null) {
target.addAttribute("bringToFront", bringToFront.intValue());
bringToFront = null;
}
if (centerRequested) {
target.addAttribute("center", true);
centerRequested = false;
}
if (scrollIntoView != null) {
target.addAttribute("scrollTo", scrollIntoView);
scrollIntoView = null;
}
// Marks the main window
if (getApplication() != null
&& this == getApplication().getMainWindow()) {
target.addAttribute("main", true);
}
if (getContent() != null) {
if (getContent().getHeightUnits() == Sizeable.UNITS_PERCENTAGE) {
target.addAttribute("layoutRelativeHeight", true);
}
if (getContent().getWidthUnits() == Sizeable.UNITS_PERCENTAGE) {
target.addAttribute("layoutRelativeWidth", true);
}
}
// Open requested resource
synchronized (openList) {
if (!openList.isEmpty()) {
for (final Iterator<OpenResource> i = openList.iterator(); i
.hasNext();) {
(i.next()).paintContent(target);
}
openList.clear();
}
}
// Contents of the window panel is painted
super.paintContent(target);
// Add executable javascripts if needed
if (jsExecQueue != null) {
for (String script : jsExecQueue) {
target.startTag("execJS");
target.addAttribute("script", script);
target.endTag("execJS");
}
jsExecQueue = null;
}
// Window position
target.addVariable(this, "positionx", getPositionX());
target.addVariable(this, "positiony", getPositionY());
// Window closing
target.addVariable(this, "close", false);
if (getParent() == null) {
// Paint subwindows
for (final Iterator<Window> i = subwindows.iterator(); i.hasNext();) {
final Window w = i.next();
w.paint(target);
}
} else {
// mark subwindows
target.addAttribute("sub", true);
}
// Paint notifications
if (notifications != null) {
target.startTag("notifications");
for (final Iterator<Notification> it = notifications.iterator(); it
.hasNext();) {
final Notification n = it.next();
target.startTag("notification");
if (n.getCaption() != null) {
target.addAttribute("caption", n.getCaption());
}
if (n.getMessage() != null) {
target.addAttribute("message", n.getMessage());
}
if (n.getIcon() != null) {
target.addAttribute("icon", n.getIcon());
}
target.addAttribute("position", n.getPosition());
target.addAttribute("delay", n.getDelayMsec());
if (n.getStyleName() != null) {
target.addAttribute("style", n.getStyleName());
}
target.endTag("notification");
}
target.endTag("notifications");
notifications = null;
}
if (pendingFocus != null) {
// ensure focused component is still attached to this main window
if (pendingFocus.getWindow() == this
|| (pendingFocus.getWindow() != null && pendingFocus
.getWindow().getParent() == this)) {
target.addAttribute("focused", pendingFocus);
}
pendingFocus = null;
}
}
/* ********************************************************************* */
/**
* Scrolls any component between the component and window to a suitable
* position so the component is visible to the user. The given component
* must be inside this window.
*
* @param component
* the component to be scrolled into view
* @throws IllegalArgumentException
* if {@code component} is not inside this window
*/
public void scrollIntoView(Component component)
throws IllegalArgumentException {
if (component.getWindow() != this) {
throw new IllegalArgumentException(
"The component where to scroll must be inside this window.");
}
scrollIntoView = component;
requestRepaint();
}
/**
* Opens the given resource in this window. The contents of this Window is
* replaced by the {@code Resource}.
*
* @param resource
* the resource to show in this window
*/
public void open(Resource resource) {
synchronized (openList) {
if (!openList.contains(resource)) {
openList.add(new OpenResource(resource, null, -1, -1,
BORDER_DEFAULT));
}
}
requestRepaint();
}
/* ********************************************************************* */
/**
* Opens the given resource in a window with the given name.
* <p>
* 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
* <code>null</code> window name is also a special case.
* </p>
* <p>
* "", 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.
* </p>
* <p>
* "_blank" as {@code windowName} causes the resource to always be opened in
* a new window or tab (depends on the browser and browser settings).
* </p>
* <p>
* "_top" and "_parent" as {@code windowName} works as specified by the HTML
* standard.
* </p>
* <p>
* 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.
* </p>
*
* @param resource
* the resource.
* @param windowName
* the name of the window.
*/
public void open(Resource resource, String windowName) {
synchronized (openList) {
if (!openList.contains(resource)) {
openList.add(new OpenResource(resource, windowName, -1, -1,
BORDER_DEFAULT));
}
}
requestRepaint();
}
/**
* 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)}.
*
* @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. See {@link #BORDER_NONE
* Window.BORDER_* constants}
*/
public void open(Resource resource, String windowName, int width,
int height, int border) {
synchronized (openList) {
if (!openList.contains(resource)) {
openList.add(new OpenResource(resource, windowName, width,
height, border));
}
}
requestRepaint();
}
/* ********************************************************************* */
/**
* Gets the full URL of the window. The returned URL is window specific and
* can be used to directly refer to the window.
* <p>
* Note! This method can not be used for portlets.
* </p>
*
* @return the URL of the window or null if the window is not attached to an
* application
*/
public URL getURL() {
if (application == null) {
return null;
}
try {
return new URL(application.getURL(), getName() + "/");
} catch (final MalformedURLException e) {
throw new RuntimeException(
"Internal problem getting window URL, please report");
}
}
/**
* <b>Application window only</b>. Gets the unique name of the window. The
* name of the window is used to uniquely identify it.
* <p>
* 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 Application#getURL()} and
* {@code win} is the window name.
* </p>
* <p>
* Note! Portlets do not support direct window access through URLs.
* </p>
*
* @return the Name of the Window.
*/
public String getName() {
return name;
}
/**
* Returns the border style of the window.
*
* @see #setBorder(int)
* @return the border style for the window
*/
public int getBorder() {
return border;
}
/**
* Sets the border style for this window. Valid values are
* {@link Window#BORDER_NONE}, {@link Window#BORDER_MINIMAL},
* {@link Window#BORDER_DEFAULT}.
* <p>
* <b>Note!</b> Setting this seems to currently have no effect whatsoever on
* the window.
* </p>
*
* @param border
* the border style to set
*/
public void setBorder(int border) {
this.border = border;
}
/**
* Sets the application this window is attached to.
*
* <p>
* This method is called by the framework and should not be called directly
* from application code. {@link com.vaadin.Application#addWindow(Window)}
* should be used to add the window to an application and
* {@link com.vaadin.Application#removeWindow(Window)} to remove the window
* from the application.
* </p>
* <p>
* This method invokes {@link Component#attach()} and
* {@link Component#detach()} methods when necessary.
* <p>
*
* @param application
* the application the window is attached to
*/
public void setApplication(Application application) {
// If the application is not changed, dont do nothing
if (application == this.application) {
return;
}
// Sends detach event if the window is connected to application
if (this.application != null) {
detach();
}
// Connects to new parent
this.application = application;
// Sends the attach event if connected to a window
if (application != null) {
attach();
}
}
/**
* <b>Application window only</b>. Sets the unique name of the window. The
* name of the window is used to uniquely identify it inside the
* application.
* <p>
* 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 Application#getURL()} and
* {@code win} is the window name.
* </p>
* <p>
* This method can only be called before the window is added to an
* application.
* </p>
* <p>
* Note! Portlets do not support direct window access through URLs.
* </p>
*
* @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) throws IllegalStateException {
// The name can not be changed in application
if (getApplication() != null) {
throw new IllegalStateException(
"Window name can not be changed while "
+ "the window is in application");
}
this.name = name;
}
/**
* Sets the user terminal. Used by the terminal adapter, should never be
* called from application code.
*
* @param type
* the terminal to set.
*/
public void setTerminal(Terminal type) {
terminal = type;
}
/**
* Private class for storing properties related to opening resources.
*/
private class OpenResource implements Serializable {
/**
* The resource to open
*/
private final Resource resource;
/**
* The name of the target window
*/
private final String name;
/**
* The width of the target window
*/
private final int width;
/**
* The height of the target window
*/
private final int height;
/**
* The border style of the target window
*/
private final int border;
/**
* Creates a new open resource.
*
* @param resource
* The resource to open
* @param name
* The name of the target window
* @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
*/
private OpenResource(Resource resource, String name, int width,
int height, int border) {
this.resource = resource;
this.name = name;
this.width = width;
this.height = height;
this.border = border;
}
/**
* Paints the open request. Should be painted inside the window.
*
* @param target
* the paint target
* @throws PaintException
* if the paint operation fails
*/
private void paintContent(PaintTarget target) throws PaintException {
target.startTag("open");
target.addAttribute("src", resource);
if (name != null && name.length() > 0) {
target.addAttribute("name", name);
}
if (width >= 0) {
target.addAttribute("width", width);
}
if (height >= 0) {
target.addAttribute("height", height);
}
switch (border) {
case Window.BORDER_MINIMAL:
target.addAttribute("border", "minimal");
break;
case Window.BORDER_NONE:
target.addAttribute("border", "none");
break;
}
target.endTag("open");
}
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.Panel#changeVariables(java.lang.Object, java.util.Map)
*/
@Override
public void changeVariables(Object source, Map<String, Object> variables) {
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() != UNITS_PIXELS || (Integer) variables
.get("height") != getHeight())) {
sizeHasChanged = true;
}
if (variables.containsKey("width")
&& (getWidthUnits() != UNITS_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, false);
}
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, false);
}
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, sub-windows are removed from their respective parent windows
* and thus visually closed on browser-side. Browser-level windows also
* closed on the client-side, but they are not implicitly removed from the
* application.
* </p>
*
* <p>
* To explicitly close a sub-window, use {@link #removeWindow(Window)}. To
* react to a window being closed (after it is closed), register a
* {@link CloseListener}.
* </p>
*/
protected void close() {
Window parent = getParent();
if (parent == null) {
fireClose();
} else {
// focus is restored to the parent window
parent.focus();
// subwindow is removed from parent
parent.removeWindow(this);
}
}
/**
* Gets the distance of Window left border in pixels from left border of the
* containing (main window).
*
* @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 positionX;
}
/**
* Sets the distance of Window left border in pixels from left border of the
* containing (main window).
*
* @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) {
setPositionX(positionX, true);
}
/**
* Sets the distance of Window left border in pixels from left border of the
* containing (main window).
*
* @param positionX
* the Distance of Window left border in pixels from left border
* of the containing (main window). or -1 if unspecified.
* @param repaintRequired
* true if the window needs to be repainted, false otherwise
* @since 6.3.4
*/
private void setPositionX(int positionX, boolean repaintRequired) {
this.positionX = positionX;
centerRequested = false;
if (repaintRequired) {
requestRepaint();
}
}
/**
* Gets the distance of Window top border in pixels from top border of the
* containing (main window).
*
* @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 positionY;
}
/**
* Sets the distance of Window top border in pixels from top border of the
* containing (main window).
*
* @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) {
setPositionY(positionY, true);
}
/**
* Sets the distance of Window top border in pixels from top border of the
* containing (main window).
*
* @param positionY
* the Distance of Window top border in pixels from top border of
* the containing (main window). or -1 if unspecified
* @param repaintRequired
* true if the window needs to be repainted, false otherwise
*
* @since 6.3.4
*/
private void setPositionY(int positionY, boolean repaintRequired) {
this.positionY = positionY;
centerRequested = false;
if (repaintRequired) {
requestRepaint();
}
}
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 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 browser level window or a sub window and
* {@link CloseListener#windowClose(CloseEvent)} will be called whenever the
* user closes the window.
*
* <p>
* Since Vaadin 6.5, removing windows using {@link #removeWindow(Window)}
* does fire 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 sub 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.
*
* <p>
* Since Vaadin 6.5, removing windows using {@link #removeWindow(Window)}
* does fire the CloseListener.
* </p>
*
* @param listener
* the CloseListener to add.
*/
public void addListener(CloseListener listener) {
addListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
}
/**
* Removes the CloseListener from the window.
*
* <p>
* For more information on CloseListeners see {@link CloseListener}.
* </p>
*
* @param listener
* the CloseListener to remove.
*/
public void removeListener(CloseListener listener) {
removeListener(CloseEvent.class, listener, WINDOW_CLOSE_METHOD);
}
protected void fireClose() {
fireEvent(new Window.CloseEvent(this));
}
/**
* 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 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 addListener(ResizeListener listener) {
addListener(ResizeEvent.class, listener, WINDOW_RESIZE_METHOD);
}
/**
* Remove a resize listener.
*
* @param listener
*/
public void removeListener(ResizeListener listener) {
removeListener(ResizeEvent.class, this);
}
/**
* Fire the resize event.
*/
protected void fireResize() {
fireEvent(new ResizeEvent(this));
}
private void attachWindow(Window w) {
subwindows.add(w);
w.setParent(this);
requestRepaint();
}
/**
* Adds a window inside another window.
*
* <p>
* Adding windows inside another window creates "subwindows". These windows
* should not be added to application directly and are not accessible
* directly with any url. Addding windows implicitly sets their parents.
* </p>
*
* <p>
* Only one level of subwindows are supported. Thus you can add windows
* inside such windows whose parent is <code>null</code>.
* </p>
*
* @param window
* @throws IllegalArgumentException
* if a window is added inside non-application level window.
* @throws NullPointerException
* if the given <code>Window</code> is <code>null</code>.
*/
public void addWindow(Window window) throws IllegalArgumentException,
NullPointerException {
if (window == null) {
throw new NullPointerException("Argument must not be null");
}
if (window.getApplication() != null) {
throw new IllegalArgumentException(
"Window was already added to application"
+ " - it can not be added to another window also.");
} else if (getParent() != null) {
throw new IllegalArgumentException(
"You can only add windows inside application-level windows.");
} else if (window.subwindows.size() > 0) {
throw new IllegalArgumentException(
"Only one level of subwindows are supported.");
}
attachWindow(window);
}
/**
* Remove the given subwindow from this window.
*
* Since Vaadin 6.5, {@link CloseListener}s are called also when explicitly
* removing a window by calling this method.
*
* Since Vaadin 6.5, returns a boolean indicating if the window was removed
* or not.
*
* @param window
* Window to be removed.
* @return true if the subwindow was removed, false otherwise
*/
public boolean removeWindow(Window window) {
if (!subwindows.remove(window)) {
// Window window is not a subwindow of this window.
return false;
}
window.setParent(null);
window.fireClose();
requestRepaint();
return true;
}
private Integer bringToFront = null;
/*
* This sequesnce is used to keep the right order of windows if multiple
* windows are brought to front in a single changeset. Incremented and saved
* by childwindows. If sequence is not used, the order is quite random
* (depends on the order getting to dirty list. e.g. which window got
* variable changes).
*/
private int bringToFrontSequence = 0;
/**
* If there are currently several sub windows visible, calling this method
* makes this window topmost.
* <p>
* This method can only be called if this window is a sub window and
* connected a top level window. 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>
* <strong> Note, this API works on sub windows only. Browsers can't reorder
* OS windows.</strong>
*/
public void bringToFront() {
Window parent = getParent();
if (parent == null) {
throw new IllegalStateException(
"Window must be attached to parent before calling bringToFront method.");
}
for (Window w : parent.getChildWindows()) {
if (w.isModal() && !isModal()) {
throw new IllegalStateException(
"There are modal windows currently visible, non-modal window cannot be brought to front.");
}
}
bringToFront = getParent().bringToFrontSequence++;
requestRepaint();
}
/**
* Get the set of all child windows.
*
* @return Set of child windows.
*/
public Set<Window> getChildWindows() {
return Collections.unmodifiableSet(subwindows);
}
/**
* Sets sub-window modal, so that widgets behind it cannot be accessed.
* <b>Note:</b> affects sub-windows only.
*
* @param modality
* true if modality is to be turned on
*/
public void setModal(boolean modality) {
modal = modality;
center();
requestRepaint();
}
/**
* @return true if this window is modal.
*/
public boolean isModal() {
return modal;
}
/**
* Sets sub-window resizable. <b>Note:</b> affects sub-windows only.
*
* @param resizable
* true if resizability is to be turned on
*/
public void setResizable(boolean resizeability) {
resizable = resizeability;
requestRepaint();
}
/**
*
* @return true if window is resizable by the end-user, otherwise false.
*/
public boolean isResizable() {
return resizable;
}
/**
* Request to center this window on the screen. <b>Note:</b> affects
* sub-windows only.
*/
public void center() {
centerRequested = true;
requestRepaint();
}
/**
* Shows a notification message on the middle of the window. The message
* automatically disappears ("humanized message").
*
* @see #showNotification(com.vaadin.ui.Window.Notification)
* @see Notification
*
* @param caption
* The message
*/
public void showNotification(String caption) {
addNotification(new Notification(caption));
}
/**
* Shows a notification message the window. The position and behavior of the
* message depends on the type, which is one of the basic types defined in
* {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
*
* @see #showNotification(com.vaadin.ui.Window.Notification)
* @see Notification
*
* @param caption
* The message
* @param type
* The message type
*/
public void showNotification(String caption, int type) {
addNotification(new Notification(caption, type));
}
/**
* Shows a notification consisting of a bigger caption and a smaller
* description on the middle of the window. The message automatically
* disappears ("humanized message").
*
* @see #showNotification(com.vaadin.ui.Window.Notification)
* @see Notification
*
* @param caption
* The caption of the message
* @param description
* The message description
*
*/
public void showNotification(String caption, String description) {
addNotification(new Notification(caption, description));
}
/**
* Shows a notification consisting of a bigger caption and a smaller
* description. The position and behavior of the message depends on the
* type, which is one of the basic types defined in {@link Notification},
* for instance Notification.TYPE_WARNING_MESSAGE.
*
* @see #showNotification(com.vaadin.ui.Window.Notification)
* @see Notification
*
* @param caption
* The caption of the message
* @param description
* The message description
* @param type
* The message type
*/
public void showNotification(String caption, String description, int type) {
addNotification(new Notification(caption, description, type));
}
/**
* Shows a notification message.
*
* @see Notification
* @see #showNotification(String)
* @see #showNotification(String, int)
* @see #showNotification(String, String)
* @see #showNotification(String, String, int)
*
* @param notification
* The notification message to show
*/
public void showNotification(Notification notification) {
addNotification(notification);
}
private void addNotification(Notification notification) {
if (notifications == null) {
notifications = new LinkedList<Notification>();
}
notifications.add(notification);
requestRepaint();
}
/**
* This method is used by Component.Focusable objects to request focus to
* themselves. Focus renders must be handled at window level (instead of
* Component.Focusable) due we want the last focused component to be focused
* in client too. Not the one that is rendered last (the case we'd get if
* implemented in Focusable only).
*
* To focus component from Vaadin application, use Focusable.focus(). See
* {@link Focusable}.
*
* @param focusable
* to be focused on next paint
*/
void setFocusedComponent(Focusable focusable) {
if (getParent() != null) {
// focus is handled by main windows
(getParent()).setFocusedComponent(focusable);
} else {
pendingFocus = focusable;
requestRepaint();
}
}
/**
* A notification message, used to display temporary messages to the user -
* for example "Document saved", or "Save failed".
* <p>
* The notification message can consist of several parts: caption,
* description and icon. It is usually used with only caption - one should
* be wary of filling the notification with too much information.
* </p>
* <p>
* The notification message tries to be as unobtrusive as possible, while
* still drawing needed attention. There are several basic types of messages
* that can be used in different situations:
* <ul>
* <li>TYPE_HUMANIZED_MESSAGE fades away quickly as soon as the user uses
* the mouse or types something. It can be used to show fairly unimportant
* messages, such as feedback that an operation succeeded ("Document Saved")
* - the kind of messages the user ignores once the application is familiar.
* </li>
* <li>TYPE_WARNING_MESSAGE is shown for a short while after the user uses
* the mouse or types something. It's default style is also more noticeable
* than the humanized message. It can be used for messages that do not
* contain a lot of important information, but should be noticed by the
* user. Despite the name, it does not have to be a warning, but can be used
* instead of the humanized message whenever you want to make the message a
* little more noticeable.</li>
* <li>TYPE_ERROR_MESSAGE requires to user to click it before disappearing,
* and can be used for critical messages.</li>
* <li>TYPE_TRAY_NOTIFICATION is shown for a while in the lower left corner
* of the window, and can be used for "convenience notifications" that do
* not have to be noticed immediately, and should not interfere with the
* current task - for instance to show "You have a new message in your
* inbox" while the user is working in some other area of the application.</li>
* </ul>
* </p>
* <p>
* In addition to the basic pre-configured types, a Notification can also be
* configured to show up in a custom position, for a specified time (or
* until clicked), and with a custom stylename. An icon can also be added.
* </p>
*
*/
public static class Notification implements Serializable {
public static final int TYPE_HUMANIZED_MESSAGE = 1;
public static final int TYPE_WARNING_MESSAGE = 2;
public static final int TYPE_ERROR_MESSAGE = 3;
public static final int TYPE_TRAY_NOTIFICATION = 4;
public static final int POSITION_CENTERED = 1;
public static final int POSITION_CENTERED_TOP = 2;
public static final int POSITION_CENTERED_BOTTOM = 3;
public static final int POSITION_TOP_LEFT = 4;
public static final int POSITION_TOP_RIGHT = 5;
public static final int POSITION_BOTTOM_LEFT = 6;
public static final int POSITION_BOTTOM_RIGHT = 7;
public static final int DELAY_FOREVER = -1;
public static final int DELAY_NONE = 0;
private String caption;
private String description;
private Resource icon;
private int position = POSITION_CENTERED;
private int delayMsec = 0;
private String styleName;
/**
* Creates a "humanized" notification message.
*
* @param caption
* The message to show
*/
public Notification(String caption) {
this(caption, null, TYPE_HUMANIZED_MESSAGE);
}
/**
* Creates a notification message of the specified type.
*
* @param caption
* The message to show
* @param type
* The type of message
*/
public Notification(String caption, int type) {
this(caption, null, type);
}
/**
* Creates a "humanized" notification message with a bigger caption and
* smaller description.
*
* @param caption
* The message caption
* @param description
* The message description
*/
public Notification(String caption, String description) {
this(caption, description, TYPE_HUMANIZED_MESSAGE);
}
/**
* Creates a notification message of the specified type, with a bigger
* caption and smaller description.
*
* @param caption
* The message caption
* @param description
* The message description
* @param type
* The type of message
*/
public Notification(String caption, String description, int type) {
this.caption = caption;
this.description = description;
setType(type);
}
private void setType(int type) {
switch (type) {
case TYPE_WARNING_MESSAGE:
delayMsec = 1500;
styleName = "warning";
break;
case TYPE_ERROR_MESSAGE:
delayMsec = -1;
styleName = "error";
break;
case TYPE_TRAY_NOTIFICATION:
delayMsec = 3000;
position = POSITION_BOTTOM_RIGHT;
styleName = "tray";
case TYPE_HUMANIZED_MESSAGE:
default:
break;
}
}
/**
* Gets the caption part of the notification message.
*
* @return The message caption
*/
public String getCaption() {
return caption;
}
/**
* Sets the caption part of the notification message
*
* @param caption
* The message caption
*/
public void setCaption(String caption) {
this.caption = caption;
}
/**
* @deprecated Use {@link #getDescription()} instead.
* @return
*/
@Deprecated
public String getMessage() {
return description;
}
/**
* @deprecated Use {@link #setDescription(String)} instead.
* @param description
*/
@Deprecated
public void setMessage(String description) {
this.description = description;
}
/**
* Gets the description part of the notification message.
*
* @return The message description.
*/
public String getDescription() {
return description;
}
/**
* Sets the description part of the notification message.
*
* @param description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Gets the position of the notification message.
*
* @return The position
*/
public int getPosition() {
return position;
}
/**
* Sets the position of the notification message.
*
* @param position
* The desired notification position
*/
public void setPosition(int position) {
this.position = position;
}
/**
* Gets the icon part of the notification message.
*
* @return The message icon
*/
public Resource getIcon() {
return icon;
}
/**
* Sets the icon part of the notification message.
*
* @param icon
* The desired message icon
*/
public void setIcon(Resource icon) {
this.icon = icon;
}
/**
* Gets the delay before the notification disappears.
*
* @return the delay in msec, -1 indicates the message has to be
* clicked.
*/
public int getDelayMsec() {
return delayMsec;
}
/**
* Sets the delay before the notification disappears.
*
* @param delayMsec
* the desired delay in msec, -1 to require the user to click
* the message
*/
public void setDelayMsec(int delayMsec) {
this.delayMsec = delayMsec;
}
/**
* Sets the style name for the notification message.
*
* @param styleName
* The desired style name.
*/
public void setStyleName(String styleName) {
this.styleName = styleName;
}
/**
* Gets the style name for the notification message.
*
* @return
*/
public String getStyleName() {
return styleName;
}
}
/**
* Executes JavaScript in this window.
*
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* @param script
* JavaScript snippet that will be executed.
*/
public void executeJavaScript(String script) {
if (getParent() != null) {
throw new UnsupportedOperationException(
"Only application level windows can execute javascript.");
}
if (jsExecQueue == null) {
jsExecQueue = new ArrayList<String>();
}
jsExecQueue.add(script);
requestRepaint();
}
/**
* Returns the closable status of the sub window. If a sub 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 sub window and prevent the user from closing the
* window.
*
* Note! For historical reasons readonly controls the closability of the sub
* window and therefore readonly and closable affect each other. Setting
* readonly to true will set closable to false and vice versa.
* <p/>
* Closable only applies to sub windows, not to browser level windows.
*
* @return true if the sub window can be closed by the user.
*/
public boolean isClosable() {
return !isReadOnly();
}
/**
* Sets the closable status for the sub window. If a sub 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 sub window and prevent the user from closing the window.
*
* Note! For historical reasons readonly controls the closability of the sub
* window and therefore readonly and closable affect each other. Setting
* readonly to true will set closable to false and vice versa.
* <p/>
* Closable only applies to sub windows, not to browser level windows.
*
* @param closable
* determines if the sub window can be closed by the user.
*/
public void setClosable(boolean closable) {
setReadOnly(!closable);
}
/**
* Indicates whether a sub window can be dragged or not. By default a sub
* window is draggable.
* <p/>
* Draggable only applies to sub windows, not to browser level windows.
*
* @param draggable
* true if the sub window can be dragged by the user
*/
public boolean isDraggable() {
return draggable;
}
/**
* Enables or disables that a sub window can be dragged (moved) by the user.
* By default a sub window is draggable.
* <p/>
* Draggable only applies to sub windows, not to browser level windows.
*
* @param draggable
* true if the sub window can be dragged by the user
*/
public void setDraggable(boolean draggable) {
this.draggable = draggable;
requestRepaint();
}
/*
* Actions
*/
protected CloseShortcut closeShortcut;
/**
* Makes is 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 subwindow from a parent window, use
* {@link #addAction(com.vaadin.event.Action)} of the parent window instead.
*
* @param keyCode
* the keycode for invoking the shortcut
* @param modifiers
* the (optional) modifiers for invoking the shortcut, null for
* none
*/
public void setCloseShortcut(int keyCode, int... modifiers) {
if (closeShortcut != null) {
removeAction(closeShortcut);
}
closeShortcut = new CloseShortcut(this, keyCode, modifiers);
addAction(closeShortcut);
}
/**
* Removes the keyboard shortcut previously set with
* {@link #setCloseShortcut(int, int...)}.
*/
public void removeCloseShortcut() {
if (closeShortcut != null) {
removeAction(closeShortcut);
closeShortcut = null;
}
}
/**
* A {@link ShortcutListener} specifically made to define a keyboard
* shortcut that closes the window.
*
* <pre class='code'>
* <code>
* // within the window using helper
* subWindow.setCloseShortcut(KeyCode.ESCAPE, null);
*
* // or globally
* getWindow().addAction(new Window.CloseShortcut(subWindow, 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();
}
}
/**
* Note, that focus/blur listeners in Window class are only supported by sub
* windows. Also note that Window is not considered focused if its contained
* component currently has focus.
*
* @see com.vaadin.event.FieldEvents.FocusNotifier#addListener(com.vaadin.event.FieldEvents.FocusListener)
*/
public void addListener(FocusListener listener) {
addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
FocusListener.focusMethod);
}
public void removeListener(FocusListener listener) {
removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
}
/**
* Note, that focus/blur listeners in Window class are only supported by sub
* windows. Also note that Window is not considered focused if its contained
* component currently has focus.
*
* @see com.vaadin.event.FieldEvents.BlurNotifier#addListener(com.vaadin.event.FieldEvents.BlurListener)
*/
public void addListener(BlurListener listener) {
addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
BlurListener.blurMethod);
}
public void removeListener(BlurListener listener) {
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
}
/**
* {@inheritDoc}
*
* If the window is a sub-window focusing will cause the sub-window to be
* brought on top of other sub-windows on gain keyboard focus.
*/
@Override
public void focus() {
if (getParent() != null) {
/*
* When focusing a sub-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.
*/
bringToFront();
} else {
super.focus();
}
}
}