blob: 7bb126c8d8f8ffeb1ff33874fc8f413d7cf8bde1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.pivot.wtk;
import java.awt.AWTEvent;
import static java.awt.Frame.MAXIMIZED_BOTH;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.SplashScreen;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.Locale;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.collections.immutable.ImmutableMap;
import org.apache.pivot.util.StringUtils;
import org.apache.pivot.util.Utils;
import org.apache.pivot.wtk.media.Image;
import org.apache.pivot.wtk.media.Picture;
/**
* Application context used to execute applications in a native frame window.
*/
public final class DesktopApplicationContext extends ApplicationContext {
/**
* Display listener interface.
*/
public interface DisplayListener {
/**
* DisplayListener adapter.
*/
public static class Adapter implements DisplayListener {
@Override
public void hostWindowOpened(Display display) {
// empty block
}
@Override
public void hostWindowClosed(Display display) {
// empty block
}
}
/**
* Called when the host window for secondary display has been opened.
*
* @param display The new secondary display.
*/
public void hostWindowOpened(Display display);
/**
* Called when the host window for secondary display has been closed.
*
* @param display The secondary display.
*/
public void hostWindowClosed(Display display);
}
// Custom display host that sets the title of the host frame to match the
// title of the root Pivot owner window
private static class DesktopDisplayHost extends DisplayHost {
private static final long serialVersionUID = 0;
private transient Window rootOwner = null;
private transient Runnable updateHostWindowTitleBarCallback = null;
private transient WindowListener rootOwnerListener = new WindowListener() {
@Override
public void titleChanged(Window window, String previousTitle) {
updateFrameTitleBar();
}
@Override
public void iconAdded(Window window, Image addedIcon) {
updateFrameTitleBar();
}
@Override
public void iconsRemoved(Window window, int index, Sequence<Image> removed) {
updateFrameTitleBar();
}
};
public DesktopDisplayHost() {
Display display = getDisplay();
display.getContainerListeners().add(new ContainerListener() {
@Override
public void componentInserted(Container container, int index) {
if (index == container.getLength() - 1) {
updateFrameTitleBar();
}
}
@Override
public void componentsRemoved(Container container, int index,
Sequence<Component> removed) {
if (index == container.getLength()) {
updateFrameTitleBar();
}
}
@Override
public void componentMoved(Container container, int from, int to) {
int n = container.getLength();
if (from == n - 1 || to == n - 1) {
updateFrameTitleBar();
}
}
});
}
private void updateFrameTitleBar() {
Display display = getDisplay();
int n = display.getLength();
Window rootOwnerLocal;
if (n == 0) {
rootOwnerLocal = null;
} else {
Window topWindow = (Window) display.get(display.getLength() - 1);
rootOwnerLocal = topWindow.getRootOwner();
}
Window previousRootOwner = this.rootOwner;
if (rootOwnerLocal != previousRootOwner) {
if (previousRootOwner != null) {
previousRootOwner.getWindowListeners().remove(this.rootOwnerListener);
}
if (rootOwnerLocal != null) {
rootOwnerLocal.getWindowListeners().add(this.rootOwnerListener);
}
this.rootOwner = rootOwnerLocal;
}
if (this.updateHostWindowTitleBarCallback == null) {
this.updateHostWindowTitleBarCallback = new Runnable() {
@Override
public void run() {
java.awt.Window hostWindow = getDisplay().getHostWindow();
Window ownerWindow = DesktopDisplayHost.this.rootOwner;
if (ownerWindow == null) {
((TitledWindow) hostWindow).setTitle(DEFAULT_HOST_WINDOW_TITLE);
hostWindow.setIconImage(null);
} else {
((TitledWindow) hostWindow).setTitle(ownerWindow.getTitle());
java.util.ArrayList<BufferedImage> iconImages = new java.util.ArrayList<>();
for (Image icon : ownerWindow.getIcons()) {
if (icon instanceof Picture) {
iconImages.add(((Picture) icon).getBufferedImage());
}
}
if (iconImages.size() == 1) {
hostWindow.setIconImage(iconImages.get(0));
} else if (iconImages.size() > 1) {
hostWindow.setIconImages(iconImages);
}
}
DesktopDisplayHost.this.updateHostWindowTitleBarCallback = null;
}
};
queueCallback(this.updateHostWindowTitleBarCallback);
}
}
}
// The AWT Window class does not define a title property; this interface allows
// the HostFrame and HostDialog titles to be handled polymorphically
private interface TitledWindow {
public String getTitle();
public void setTitle(String title);
}
// Native host frame
private static class HostFrame extends java.awt.Frame implements TitledWindow {
private static final long serialVersionUID = 5340356674429280196L;
public HostFrame() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK | AWTEvent.WINDOW_STATE_EVENT_MASK);
// Disable focus traversal keys
setFocusTraversalKeysEnabled(false);
// Clear the background
setBackground(null);
}
@Override
public void update(Graphics graphics) {
paint(graphics);
}
@Override
public String getTitle() {
return super.getTitle();
}
@Override
public void setTitle(String title) {
super.setTitle(title);
}
@Override
public void processWindowEvent(WindowEvent event) {
super.processWindowEvent(event);
switch (event.getID()) {
case WindowEvent.WINDOW_CLOSING:
exit();
break;
case WindowEvent.WINDOW_CLOSED:
System.exit(0);
break;
default:
break;
}
}
@Override
protected void processWindowStateEvent(WindowEvent event) {
super.processWindowStateEvent(event);
switch (event.getID()) {
case WindowEvent.WINDOW_ICONIFIED:
try {
application.suspend();
} catch (Throwable exception) {
handleUncaughtException(exception);
}
break;
case WindowEvent.WINDOW_DEICONIFIED:
try {
application.resume();
} catch (Throwable exception) {
handleUncaughtException(exception);
}
break;
default:
break;
}
}
}
// Native host dialog for secondary displays
private static class HostDialog extends java.awt.Dialog implements TitledWindow {
private static final long serialVersionUID = 5340356674429280196L;
private DisplayHost displayHost = new DesktopDisplayHost();
private DisplayListener displayCloseListener;
public HostDialog(java.awt.Window owner, boolean modal, DisplayListener displayCloseListener) {
super(owner, modal ? java.awt.Dialog.ModalityType.APPLICATION_MODAL
: java.awt.Dialog.ModalityType.MODELESS);
this.displayCloseListener = displayCloseListener;
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
// Disable focus traversal keys
setFocusTraversalKeysEnabled(false);
// Clear the background
setBackground(null);
// Add the display host
add(this.displayHost);
}
@Override
public void update(Graphics graphics) {
paint(graphics);
}
public Display getDisplay() {
return this.displayHost.getDisplay();
}
@Override
public String getTitle() {
return super.getTitle();
}
@Override
public void setTitle(String title) {
super.setTitle(title);
}
@Override
public void processWindowEvent(WindowEvent event) {
super.processWindowEvent(event);
Display display;
switch (event.getID()) {
case WindowEvent.WINDOW_OPENED:
display = this.displayHost.getDisplay();
displays.add(display);
if (this.displayCloseListener != null) {
this.displayCloseListener.hostWindowOpened(display);
}
this.displayHost.requestFocus();
break;
case WindowEvent.WINDOW_CLOSING:
dispose();
break;
case WindowEvent.WINDOW_CLOSED:
display = this.displayHost.getDisplay();
displays.remove(display);
if (this.displayCloseListener != null) {
this.displayCloseListener.hostWindowClosed(display);
}
break;
default:
break;
}
}
}
private static String applicationClassName = null;
private static HashMap<String, String> properties = null;
private static Application application = null;
private static DisplayHost primaryDisplayHost = null;
private static HostFrame windowedHostFrame = null;
private static HostFrame fullScreenHostFrame = null;
public static final String DEFAULT_HOST_WINDOW_TITLE = "Apache Pivot";
public static final String X_ARGUMENT = "x";
public static final String Y_ARGUMENT = "y";
public static final String WIDTH_ARGUMENT = "width";
public static final String HEIGHT_ARGUMENT = "height";
public static final String CENTER_ARGUMENT = "center";
public static final String RESIZABLE_ARGUMENT = "resizable";
public static final String MAXIMIZED_ARGUMENT = "maximized";
public static final String UNDECORATED_ARGUMENT = "undecorated";
public static final String FULL_SCREEN_ARGUMENT = "fullScreen";
public static final String PRESERVE_SPLASH_SCREEN_ARGUMENT = "preserveSplashScreen";
public static final String ORIGIN_ARGUMENT = "origin";
public static final String USE_APPLICATION_INSTANCE_ARGUMENT = "useApplicationInstance";
private static final String INVALID_PROPERTY_FORMAT_MESSAGE = "\"%s\" is not a valid startup "
+ "property (expected format is \"--name[=value]\").";
private static final String INVALID_PROPERTY_VALUE_MESSAGE = "\"%s\" is not a valid value for "
+ "startup property \"%s\".";
public static boolean isActive() {
return (application != null);
}
/**
* Terminates the application context. this call is the same as exit(true)
*/
public static void exit() {
exit(true);
}
/**
* Terminates the application context.
*
* @param optional If <tt>true</tt>, shutdown is optional and may be
* cancelled. If <tt>false</tt>, shutdown cannot be cancelled.
* @return Whether shutdown was canceled by the application.
*/
public static boolean exit(boolean optional) {
boolean cancelShutdown = false;
if (application != null) {
try {
cancelShutdown = application.shutdown(optional);
} catch (Throwable exception) {
handleUncaughtException(exception);
} finally {
if (!cancelShutdown) {
// Remove the application from the application list
applications.remove(application);
}
}
}
if (!cancelShutdown) {
try {
Preferences preferences = Preferences.userNodeForPackage(DesktopApplicationContext.class);
preferences = preferences.node(applicationClassName);
boolean maximized = (windowedHostFrame.getExtendedState() & MAXIMIZED_BOTH) == MAXIMIZED_BOTH;
if (!maximized) {
preferences.putInt(X_ARGUMENT, windowedHostFrame.getX());
preferences.putInt(Y_ARGUMENT, windowedHostFrame.getY());
preferences.putInt(WIDTH_ARGUMENT, windowedHostFrame.getWidth());
preferences.putInt(HEIGHT_ARGUMENT, windowedHostFrame.getHeight());
}
preferences.putBoolean(MAXIMIZED_ARGUMENT, maximized);
preferences.flush();
} catch (SecurityException exception) {
// No-op
} catch (BackingStoreException exception) {
// No-op
}
windowedHostFrame.dispose();
fullScreenHostFrame.dispose();
}
return cancelShutdown;
}
/**
* Calculate the entire virtual desktop bounding rectangle.
* @return The entire bounding rectangle of all the screen devices.
*/
private static Rectangle getVirtualDesktopBounds() {
Rectangle virtualBounds = new Rectangle();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (GraphicsDevice gd : ge.getScreenDevices()) {
for (GraphicsConfiguration gc : gd.getConfigurations()) {
virtualBounds = virtualBounds.union(gc.getBounds());
}
}
return virtualBounds;
}
/**
* Primary application entry point.
*
* @param args application arguments
*/
public static void main(String[] args) {
// Get the application class name
if (args.length == 0) {
System.err.println("Application class name is required.");
return;
}
applicationClassName = args[0];
// Get the startup properties
properties = new HashMap<>();
int x = 0;
int y = 0;
int width = 800;
int height = 600;
boolean center = false;
boolean resizable = true;
boolean maximized = false;
boolean undecorated = false;
boolean fullScreen = false;
boolean preserveSplashScreen = false;
boolean useApplicationInstance = false;
try {
Preferences preferences = Preferences.userNodeForPackage(DesktopApplicationContext.class);
preferences = preferences.node(applicationClassName);
x = preferences.getInt(X_ARGUMENT, x);
y = preferences.getInt(Y_ARGUMENT, y);
width = preferences.getInt(WIDTH_ARGUMENT, width);
height = preferences.getInt(HEIGHT_ARGUMENT, height);
maximized = preferences.getBoolean(MAXIMIZED_ARGUMENT, maximized);
} catch (SecurityException exception) {
System.err.println("Unable to retrieve startup preferences: " + exception);
}
for (int i = 1; i < args.length; i++) {
String arg = args[i];
if (arg.startsWith("--")) {
arg = arg.substring(2);
String[] property = arg.split("=", -1);
String key = property[0];
if (property.length == 2) {
String value = property[1];
if (value.isEmpty()) {
System.err.println(String.format(INVALID_PROPERTY_FORMAT_MESSAGE, arg));
} else {
try {
switch (key) {
case X_ARGUMENT:
x = Integer.parseInt(value);
break;
case Y_ARGUMENT:
y = Integer.parseInt(value);
break;
case WIDTH_ARGUMENT:
width = Integer.parseInt(value);
break;
case HEIGHT_ARGUMENT:
height = Integer.parseInt(value);
break;
case CENTER_ARGUMENT:
center = StringUtils.toBoolean(value);
break;
case RESIZABLE_ARGUMENT:
resizable = StringUtils.toBoolean(value);
break;
case MAXIMIZED_ARGUMENT:
maximized = StringUtils.toBoolean(value);
break;
case UNDECORATED_ARGUMENT:
undecorated = StringUtils.toBoolean(value);
break;
case FULL_SCREEN_ARGUMENT:
fullScreen = StringUtils.toBoolean(value);
break;
case PRESERVE_SPLASH_SCREEN_ARGUMENT:
preserveSplashScreen = StringUtils.toBoolean(value);
break;
case ORIGIN_ARGUMENT:
origin = new URL(value);
break;
case USE_APPLICATION_INSTANCE_ARGUMENT:
useApplicationInstance = StringUtils.toBoolean(value);
break;
default:
properties.put(key, value);
break;
}
} catch (Exception exception) {
System.err.println(String.format(INVALID_PROPERTY_VALUE_MESSAGE, value, key));
}
}
} else if (property.length == 1) {
// Options of the form "--option" have a value of TRUE
properties.put(key, Boolean.TRUE.toString());
} else {
System.err.println(String.format(INVALID_PROPERTY_FORMAT_MESSAGE, arg));
}
} else {
System.err.println(String.format(INVALID_PROPERTY_FORMAT_MESSAGE, arg));
}
}
// Start the timer
createTimer();
// Create the display host
primaryDisplayHost = new DesktopDisplayHost();
displays.add(primaryDisplayHost.getDisplay());
// Create the windowed host frame
windowedHostFrame = new HostFrame();
windowedHostFrame.add(primaryDisplayHost);
windowedHostFrame.setUndecorated(undecorated);
windowedHostFrame.setTitle(DEFAULT_HOST_WINDOW_TITLE);
windowedHostFrame.setSize(width, height);
windowedHostFrame.setResizable(resizable);
Rectangle screenSize = getVirtualDesktopBounds();
if (center) {
// Center on the virtual desktop (which could span multiple physical displays)
windowedHostFrame.setLocation(
((screenSize.width - width) / 2) + screenSize.x,
((screenSize.height - height) / 2) + screenSize.y);
} else {
// Ensure that if the position was from a display configuration that is no longer
// supported (like we last closed the app when there were two monitors and there
// is now only one) that we don't end up completely offscreen.
// This computation will always place the upper left corner of the app window somewhere
// on the virtual screen (noting that the virtual x,y location could be negative when
// secondary displays are left / above the primary one).
if (x >= screenSize.width) {
x = Math.max(screenSize.width - width, screenSize.x);
}
if (x < screenSize.x) {
x = screenSize.x;
}
if (y >= screenSize.height) {
y = Math.max(screenSize.height - height, screenSize.y);
}
if (y < screenSize.y) {
y = screenSize.y;
}
windowedHostFrame.setLocation(x, y);
}
if (maximized) {
windowedHostFrame.setExtendedState(MAXIMIZED_BOTH);
}
// Create the full-screen host frame
fullScreenHostFrame = new HostFrame();
fullScreenHostFrame.setUndecorated(true);
// Add a key listener to the display host to toggle between full-screen
// and windowed mode
primaryDisplayHost.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_F
&& (keyEvent.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) > 0
&& (keyEvent.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) > 0) {
setFullScreen(!isFullScreen());
}
}
});
// Load the application
try {
Class<?> applicationClass = Class.forName(applicationClassName);
if (useApplicationInstance) {
// application has already been set, before calling this method
} else {
application = (Application) applicationClass.newInstance();
}
} catch (ClassNotFoundException exception) {
exception.printStackTrace();
} catch (InstantiationException exception) {
exception.printStackTrace();
} catch (IllegalAccessException exception) {
exception.printStackTrace();
}
if (application != null) {
// Add the application to the application list
applications.add(application);
// Initialize OS-specific extensions
initializeOSExtensions();
// Don't make the window visible if there is a SplashScreen that the
// application intends to use
boolean visible = (!preserveSplashScreen || SplashScreen.getSplashScreen() == null);
// Initial configuration of the windows
setFullScreen(fullScreen, visible);
// TODO This is a workaround for Java bug #6365898 on Linux (fixed only in Java 7),
// revisit / remove later when we'll require Java 7
if (maximized && visible) {
windowedHostFrame.setExtendedState(MAXIMIZED_BOTH);
}
// Start the application in a callback to allow the host window to open first
queueCallback(() -> {
try {
application.startup(primaryDisplayHost.getDisplay(), new ImmutableMap<>(properties));
} catch (Throwable exception) {
handleUncaughtException(exception);
}
});
}
}
private static void initializeOSExtensions() {
String osName = System.getProperty("os.name");
if (osName.toLowerCase(Locale.ENGLISH).startsWith("mac os x")) {
try {
// Get the EAWT classes and methods
Class<?> eawtApplicationClass = Class.forName("com.apple.eawt.Application");
Class<?> eawtApplicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
Class<?> eawtApplicationEventClass = Class.forName("com.apple.eawt.ApplicationEvent");
Method setEnabledAboutMenuMethod = eawtApplicationClass.getMethod(
"setEnabledAboutMenu", new Class<?>[] {Boolean.TYPE});
Method addApplicationListenerMethod = eawtApplicationClass.getMethod(
"addApplicationListener", new Class<?>[] {eawtApplicationListenerClass});
final Method setHandledMethod = eawtApplicationEventClass.getMethod("setHandled",
new Class<?>[] {Boolean.TYPE});
// Create the proxy handler
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
boolean handled = true;
String methodName = method.getName();
if (methodName.equals("handleAbout")) {
Application.AboutHandler aboutHandler = (Application.AboutHandler) application;
aboutHandler.aboutRequested();
} else if (methodName.equals("handleQuit")) {
handled = !exit(true);
}
// Invoke setHandled()
setHandledMethod.invoke(args[0], new Object[] {Boolean.valueOf(handled)});
return null;
}
};
Object eawtApplication = eawtApplicationClass.newInstance();
setEnabledAboutMenuMethod.invoke(eawtApplication,
Boolean.valueOf(application instanceof Application.AboutHandler));
Object eawtApplicationListener = Proxy.newProxyInstance(
DesktopApplicationContext.class.getClassLoader(),
new Class<?>[] {eawtApplicationListenerClass}, handler);
// Invoke the addApplicationListener() method with the proxy listener
addApplicationListenerMethod.invoke(eawtApplication,
new Object[] {eawtApplicationListener});
} catch (Throwable throwable) {
System.err.println("Unable to attach EAWT hooks: " + throwable);
}
}
}
/**
* Returns the full-screen mode flag.
* @return Whether or not the application is/should be displayed full screen.
*/
public static boolean isFullScreen() {
return fullScreenHostFrame.isVisible();
}
/**
* Sets the full-screen mode flag.
*
* @param fullScreen Whether to display the application full screen.
*/
public static void setFullScreen(boolean fullScreen) {
setFullScreen(fullScreen, true);
}
private static void setFullScreen(boolean fullScreen, boolean visible) {
GraphicsDevice graphicsDevice = windowedHostFrame.getGraphicsConfiguration().getDevice();
if (fullScreen) {
// Go to full screen mode
if (windowedHostFrame.isVisible()) {
windowedHostFrame.remove(primaryDisplayHost);
}
windowedHostFrame.setVisible(false);
// Setting the full screen window now will cause a SplashScreen to
// be dismissed, so don't do so if the --preserveSplashScreen
// startup property was true, and a SplashScreen was supplied.
// When the SplashScreen needs to be dismissed, users can call
// replaceSplashScreen(Display) which will set the full screen
// window, if required.
if (visible) {
graphicsDevice.setFullScreenWindow(fullScreenHostFrame);
}
fullScreenHostFrame.add(primaryDisplayHost);
fullScreenHostFrame.setTitle(windowedHostFrame.getTitle());
fullScreenHostFrame.setVisible(visible);
} else {
// Go to windowed mode
if (fullScreenHostFrame.isVisible()) {
fullScreenHostFrame.remove(primaryDisplayHost);
}
fullScreenHostFrame.setVisible(false);
graphicsDevice.setFullScreenWindow(null);
windowedHostFrame.add(primaryDisplayHost);
windowedHostFrame.setTitle(fullScreenHostFrame.getTitle());
windowedHostFrame.setVisible(visible);
}
primaryDisplayHost.requestFocusInWindow();
}
/**
* Gets the window hosting the specified Display and makes it visible.<br>
* This will cause a visible {@link SplashScreen} to be closed.<br> It is
* intended to be called one time when the Pivot application has initialized
* its UI and the SplashScreen is ready to be dismissed, but can be safely
* called regardless of whether there is now, or used to be, a visible
* SplashScreen.
*
* @param display Display to make visible
* @see java.awt.SplashScreen
*/
public static void replaceSplashScreen(Display display) {
java.awt.Window hostWindow = display.getHostWindow();
GraphicsDevice device = windowedHostFrame.getGraphicsConfiguration().getDevice();
if ((hostWindow == fullScreenHostFrame) && (device.getFullScreenWindow() == null)) {
device.setFullScreenWindow(fullScreenHostFrame);
}
hostWindow.setVisible(true);
}
/**
* Sizes the window's native host frame to match its preferred size.
*
* @param window The window to size.
* @throws IllegalArgumentException if the window parameter is {@code null}.
* @throws IllegalStateException if the application is being displayed full screen.
*/
public static void sizeHostToFit(Window window) {
Utils.checkNull(window, "window");
if (isFullScreen()) {
throw new IllegalStateException("Full screen window cannot be sized to fit.");
}
Dimensions size = window.getPreferredSize();
java.awt.Window hostWindow = window.getDisplay().getHostWindow();
java.awt.Insets frameInsets = hostWindow.getInsets();
hostWindow.setSize(size.width + (frameInsets.left + frameInsets.right), size.height
+ (frameInsets.top + frameInsets.bottom));
}
/**
* Creates a new secondary display.
*
* @param width The new width for the secondary display.
* @param height The height for the secondary display.
* @param x The new X-position for the display.
* @param y The new Y-position for the display.
* @param modal Whether or not the new display is to be modal.
* @param resizable Whether or not to make the new display resizable by the user.
* @param undecorated Whether the new display should be undecorated (that is just
* a bare window) or normal.
* @param owner The owner for the new display.
* @param displayCloseListener The listener for the dialog being closed.
* @return The newly created display.
* @throws IllegalStateException if the full screen flag is set.
*/
public static Display createDisplay(int width, int height, int x, int y, boolean modal,
boolean resizable, boolean undecorated, java.awt.Window owner,
DisplayListener displayCloseListener) {
if (isFullScreen()) {
throw new IllegalStateException(
"Secondary display cannot be created for full screen application.");
}
final HostDialog hostDialog = new HostDialog(owner, modal, displayCloseListener);
hostDialog.setLocation(x, y);
hostDialog.setSize(width, height);
hostDialog.setResizable(resizable);
hostDialog.setUndecorated(undecorated);
// Open the window in a callback; otherwise, if it is modal, it will
// block the calling thread
ApplicationContext.queueCallback(() -> hostDialog.setVisible(true));
return hostDialog.getDisplay();
}
/**
* Utility method to make it easier to define <tt>main()</tt> entry-points
* into applications. For example:
* <pre> public class MyApp implements Application {
* public static void main(String[] args) throws Exception {
* DesktopApplicationContext.main(MyApp.class, args);
* }
* } </pre>
*
* @param applicationClass the class of Application entry point
* @param applicationArgs application arguments
*/
public static final void main(Class<? extends Application> applicationClass,
String[] applicationArgs) {
String[] args = new String[applicationArgs.length + 1];
System.arraycopy(applicationArgs, 0, args, 1, applicationArgs.length);
args[0] = applicationClass.getName();
main(args);
}
/**
* Utility method to make it easier to define <tt>main()</tt> entry-points
* into applications.<br> This is useful if application instance has
* already been created, for example from a scripting environment and I set
* some external properties in the application for later reuse, so I must
* use that instance.<br> But it's important to NOT call usual methods of
* application lifecycle before passing it here, to avoid side effects.
*
* @param applicationInstance an instance of Application entry point
* @param applicationArgs application arguments
*/
public static final void main(Application applicationInstance, String[] applicationArgs) {
String[] args = new String[applicationArgs.length + 2];
System.arraycopy(applicationArgs, 0, args, 1, applicationArgs.length);
args[0] = applicationInstance.getClass().getName();
args[applicationArgs.length + 1] = "--" + USE_APPLICATION_INSTANCE_ARGUMENT + "=" + "true";
application = applicationInstance;
main(args);
}
}