/* @ITMillApache2LicenseForJavaFiles@ */ 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.ShortcutAction; import com.vaadin.event.ShortcutListener; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; 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.VView; /** * A component that represents an application (browser native) window or a sub * window. *
* 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. *
** 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. *
** 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. *
** Window caption is displayed in the browser title bar for application level * windows and in the window header for sub windows. *
** Certain methods in this class are only meaningful for sub windows and other * parts only for application windows. These are marked using Sub window * only and Application window only respectively in the javadoc. *
** Sub window is to be split into a separate component in Vaadin 7. *
* * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ @SuppressWarnings("serial") @ClientWidget(VView.class) public class Window extends Panel implements URIHandler, ParameterHandler { /** * Application window only. A border style used for opening resources * in a window without a border. */ public static final int BORDER_NONE = 0; /** * Application window only. A border style used for opening resources * in a window with a minimal border. */ public static final int BORDER_MINIMAL = 1; /** * Application window only. A border style that indicates that the * default border style should be used when opening resources. */ public static final int BORDER_DEFAULT = 2; /** * Application window only. The terminal this window is attached to. */ private Terminal terminal = null; /** * Application window only. The application this window is attached * to or null. */ private Application application = null; /** * Application window only. List of URI handlers for this window. */ private LinkedList* Parent is the visual parent of a component. Each component can belong to * only one ComponentContainer at time. *
* *
* For windows attached directly to the application, parent is
* null
. For windows inside other windows, parent is the window
* containing this window.
*
* Parent is the visual parent of a component. This is mostly called by * containers add method and should not be called directly *
* * @param parent * the New value of property parent. */ @Override public void setParent(Component parent) { super.setParent(parent); } /* ********************************************************************* */ /** * Adds the new URI handler to this window. For sub-windows, URI handlers * are attached to root level 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 = (Window) getParent(); mainWindow.addURIHandler(handler); } else { if (uriHandlerList == null) { uriHandlerList = new LinkedList
* Note, that instead of overriding this method developer should consider
* using {@link Window#addURIHandler(URIHandler)} to add uri handler to
* Window.
*
* @param context
* @param relativeUri
*/
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;
}
/* ********************************************************************* */
/**
* Adds the new parameter handler to this window. For sub windows, parameter
* handlers are attached to parent windows.
*
* @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 = (Window) getParent();
mainWindow.addParameterHandler(handler);
} else {
if (parameterHandlerList == null) {
parameterHandlerList = new LinkedList
* Subwindows do not support themes and thus return theme used by the parent
*
* Note! When opening browser window with name "_self", client will skip
* rendering rest of the changes as it considers them irrelevant. This may
* speed up opening resource, but it may also put client side into an
* inconsistent state with server in case nothing is actually opened to
* window (like if browser decided to download the resource instead of
* displaying it).
*
* @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 named terminal window with given size and
* border properties. Empty or
* Name identifies the URL used to access application-level windows, but is
* not used for windows inside other windows. all application-level windows
* can be accessed by their names in url
*
* Note! Setting this seems to currently have no effect whatsoever on
* the window.
*
* This method should not be invoked directly. Instead the
* {@link com.vaadin.Application#addWindow(Window)} method should be used to
* add the window to an application and
* {@link com.vaadin.Application#removeWindow(Window)} method for removing
* the window from the applicion. These methods call this method implicitly.
*
* The method invokes {@link Component#attach()} and
* {@link Component#detach()} methods when necessary.
*
*
* @param application
* the application to set.
*/
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();
}
}
/**
* Sets the name.
*
* The name of the window must be unique inside the application.
*
* If the name is null, the the window is given name automatically when it
* is added to an application.
*
* 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.
*
* If one wants change the default behavior, register a window close
* listenter and do something else. For example, you could re-open the
* browser-level window with mainWindow.open(), re-add the removed
* sub-window back to its parent or remove browser-level window
* automatically from the application.
*
* Note that removing windows using {@link #removeWindow(Window)} will not
* fire the CloseListener.
*
* Note that removing windows using {@link #removeWindow(Window)} will not
* fire the CloseListener.
*
* For more information on CloseListeners see {@link CloseListener}.
*
* 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.
*
* Only one level of subwindows are supported. Thus you can add windows
* inside such windows whose parent is
* 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.
*
* 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:
* null
window name results the resource to be opened in this
* window.
*
* null
window name results the
* resource to be opened in this window.
*
* @param resource
* @param windowName
* @param width
* @param height
* @param border
*/
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();
}
/* ********************************************************************* */
/**
* Returns the full url of the window, this returns window specific url even
* for the main window.
*
* @return the URL of the window.
*/
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");
}
}
/**
* Gets the unique name of the window that indentifies it on the terminal.
*
* http://host:port/foo/bar/
where
* http://host:port/foo/
is the application url as returned by
* getURL() and bar
is the name of the window. Also note that
* not all windows should be added to application - one can also add windows
* inside other windows - these windows show as smaller windows inside those
* windows.
* null
.
* Window
is null
.
*/
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.
*
* @param window
* Window to be removed.
*/
public void removeWindow(Window window) {
subwindows.remove(window);
window.setParent(null);
requestRepaint();
}
/**
* Get the set of all child windows.
*
* @return Set of child windows.
*/
public Set
*
*
* 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. *
* */ 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. * ** This method allows one to inject javascript from the server to client. A * client implementation is not required to implement this functionality, * but currently all web-based clients do implement this. *
* ** Executing javascript this way often leads to cross-browser compatibility * issues and regressions that are hard to resolve. Use of this method * should be avoided and instead it is recommended to create new widgets * with GWT. For more info on creating own, reusable client-side widgets in * Java, read the corresponding chapter in Book of Vaadin. *
* * @param script * JavaScript snippet that will be executed. */ public void executeJavaScript(String script) { if (getParent() != null) { throw new UnsupportedOperationException( "Only application level windows can execute javascript."); } if (jsExecQueue == null) { jsExecQueue = new ArrayList
*
* // within the window using helper
* subWindow.setCloseShortcut(KeyCode.ESCAPE, null);
*
* // or globally
* getWindow().addAction(new Window.CloseShortcut(subWindow, KeyCode.ESCAPE));
*
*
*
*/
public static class CloseShortcut extends ShortcutListener {
protected Window window;
/**
* Creates a keyboard shortcut for closing the given window using the
* shorthand notation defined in {@link ShortcutAction}.
*
* @param window
* to be closed when the shortcut is invoked
* @param shorthandCaption
* the caption with shortcut keycode and modifiers indicated
*/
public CloseShortcut(Window window, String shorthandCaption) {
super(shorthandCaption);
this.window = window;
}
/**
* Creates a keyboard shortcut for closing the given window using the
* given {@link KeyCode} and {@link ModifierKey}s.
*
* @param window
* to be closed when the shortcut is invoked
* @param keyCode
* KeyCode to react to
* @param modifiers
* optional modifiers for shortcut
*/
public CloseShortcut(Window window, int keyCode, int... modifiers) {
super(null, keyCode, modifiers);
this.window = window;
}
/**
* Creates a keyboard shortcut for closing the given window using the
* given {@link KeyCode}.
*
* @param window
* to be closed when the shortcut is invoked
* @param keyCode
* KeyCode to react to
*/
public CloseShortcut(Window window, int keyCode) {
this(window, keyCode, null);
}
@Override
public void handleAction(Object sender, Object target) {
window.close();
}
}
}