com.sun.glass.ui.Application Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjfx-78-backport Show documentation
Show all versions of openjfx-78-backport Show documentation
This is a backport of OpenJFX 8 to run on Java 7.
The newest version!
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.glass.ui;
import com.sun.glass.events.KeyEvent;
import com.sun.glass.ui.CommonDialogs.ExtensionFilter;
import com.sun.glass.ui.CommonDialogs.FileChooserResult;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
public abstract class Application {
protected String name;
public static class EventHandler {
// currently used only on Mac OS X
public void handleWillFinishLaunchingAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleDidFinishLaunchingAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleWillBecomeActiveAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleDidBecomeActiveAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleWillResignActiveAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleDidResignActiveAction(Application app, long time) {
}
// currently used only on iOS
public void handleDidReceiveMemoryWarning(Application app, long time) {
}
// currently used only on Mac OS X
public void handleWillHideAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleDidHideAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleWillUnhideAction(Application app, long time) {
}
// currently used only on Mac OS X
public void handleDidUnhideAction(Application app, long time) {
}
// currently used only on Mac OS X
// the open files which started up the app will arrive before app becomes active
public void handleOpenFilesAction(Application app, long time, String files[]) {
}
// currently used only on Mac OS X
public void handleQuitAction(Application app, long time) {
}
}
private EventHandler eventHandler;
private boolean initialActiveEventReceived = false;
private String initialOpenedFiles[] = null;
private static boolean loaded = false;
private static Application application;
private static Thread eventThread;
private static final boolean disableThreadChecks =
AccessController.doPrivileged(new PrivilegedAction() {
public Boolean run() {
final String str =
System.getProperty("glass.disableThreadChecks", "true");
return "true".equalsIgnoreCase(str);
}
});
// May be called on any thread.
protected static synchronized void loadNativeLibrary(final String libname) {
// load the native library of the specified libname.
// the platform default by convention is "glass", all others should have a suffix, ie glass-x11
if (!loaded) {
com.sun.glass.utils.NativeLibLoader.loadLibrary(libname);
loaded = true;
}
}
// May be called on any thread.
protected static synchronized void loadNativeLibrary() {
// use the "platform default" name of "glass"
loadNativeLibrary("glass");
}
private static volatile Map deviceDetails = null;
// provides a means for the user to pass platorm specific details
// to the native glass impl. Can be null.
// May need be called before Run.
// May be called on any thread.
public static void setDeviceDetails(Map details) {
deviceDetails = details;
}
// May be called on any thread.
public static Map getDeviceDetails() {
return deviceDetails;
}
protected Application() {
}
// May be called on any thread.
public static void run(final Runnable launchable) {
if (application != null) {
throw new IllegalStateException("Application is already running");
}
application = PlatformFactory.getPlatformFactory().createApplication();
// each concrete Application should set the app name using its own platform mechanism:
// on Mac OS X - use NSBundle info, which can be overriden by -Xdock:name
// on Windows - TODO
// on Linux - TODO
application.name = "java"; // default
try {
application.runLoop(new Runnable() {
@Override public void run() {
Screen.initScreens();
launchable.run();
}
});
} catch (Throwable t) {
t.printStackTrace();
}
}
// runLoop never exits until app terminates
protected abstract void runLoop(Runnable launchable);
// should return after loop termination completion
protected void finishTerminating() {
// To make sure application object is not used outside of the run loop
application = null;
// The eventThread is null at this point, no need to check it
}
public String getName() {
checkEventThread();
return name;
}
private void notifyWillFinishLaunching() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleWillFinishLaunchingAction(this, System.nanoTime());
}
}
private void notifyDidFinishLaunching() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidFinishLaunchingAction(this, System.nanoTime());
}
}
private void notifyWillBecomeActive() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleWillBecomeActiveAction(this, System.nanoTime());
}
}
private void notifyDidBecomeActive() {
this.initialActiveEventReceived = true;
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidBecomeActiveAction(this, System.nanoTime());
}
}
private void notifyWillResignActive() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleWillResignActiveAction(this, System.nanoTime());
}
}
private void notifyDidResignActive() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidResignActiveAction(this, System.nanoTime());
}
}
private void notifyDidReceiveMemoryWarning() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidReceiveMemoryWarning(this, System.nanoTime());
}
}
private void notifyWillHide() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleWillHideAction(this, System.nanoTime());
}
}
private void notifyDidHide() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidHideAction(this, System.nanoTime());
}
}
private void notifyWillUnhide() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleWillUnhideAction(this, System.nanoTime());
}
}
private void notifyDidUnhide() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleDidUnhideAction(this, System.nanoTime());
}
}
// notificiation when user drag and drops files onto app icon
private void notifyOpenFiles(String files[]) {
if ((this.initialActiveEventReceived == false) && (this.initialOpenedFiles == null)) {
// rememeber the initial opened files
this.initialOpenedFiles = files;
}
EventHandler handler = getEventHandler();
if ((handler != null) && (files != null)) {
handler.handleOpenFilesAction(this, System.nanoTime(), files);
}
}
private void notifyWillQuit() {
EventHandler handler = getEventHandler();
if (handler != null) {
handler.handleQuitAction(this, System.nanoTime());
}
}
/**
* Install app's default native menus:
* on Mac OS X - Apple menu (showing the app name) with a single Quit menu item
* on Windows - NOP
* on Linux - NOP
*/
public void installDefaultMenus(MenuBar menubar) {
checkEventThread();
// To override in subclasses
}
public EventHandler getEventHandler() {
//checkEventThread(); // Glass (Mac)
// When an app is closing, Mac calls notify- Will/DidHide, Will/DidResignActive
// on a thread other than the Main thread
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
checkEventThread();
boolean resendOpenFiles = ((this.eventHandler != null) && (this.initialOpenedFiles != null));
this.eventHandler = eventHandler;
if (resendOpenFiles == true) {
// notify the new event handler with initial opened files
notifyOpenFiles(this.initialOpenedFiles);
}
}
private boolean terminateWhenLastWindowClosed = true;
public final boolean shouldTerminateWhenLastWindowClosed() {
checkEventThread();
return terminateWhenLastWindowClosed;
}
public final void setTerminateWhenLastWindowClosed(boolean b) {
checkEventThread();
terminateWhenLastWindowClosed = b;
}
public boolean shouldUpdateWindow() {
checkEventThread();
return false; // overridden in platform application class
}
public boolean hasWindowManager() {
//checkEventThread(); // Prism (Mac)
return true; // overridden in platform application class
}
public void terminate() {
checkEventThread();
try {
synchronized (this) {
final Vector windows = new Vector(Window.getWindows());
for (Window window : windows) {
//System.err.println("window setVisible false : "+window.getTitle());
// first make windows invisible
window.setVisible(false);
}
for (Window window : windows) {
//System.err.println("window close : "+window.getTitle());
// now we can close windows
window.close();
}
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
finishTerminating();
}
}
// May be called on any thread
static public Application GetApplication() {
return Application.application;
}
// May be called on any thread
protected static void setEventThread(Thread thread) {
Application.eventThread = thread;
}
// May be called on any thread
protected static Thread getEventThread() {
return Application.eventThread;
}
/**
* Returns {@code true} if the current thread is the event thread.
*/
public static boolean isEventThread() {
return Thread.currentThread() == Application.eventThread;
}
/**
* Verifies that the current thread is the event thread, and throws
* an exception if this is not so.
*
* The check can be disabled by setting the "glass.disableThreadChecks"
* system property. It is preferred, however, to fix the application code
* instead.
*
* @throws IllegalStateException if the current thread is not the event thread
*/
public static void checkEventThread() {
//TODO: we do NOT advertise the "glass.disableThreadChecks".
// If we never get a complaint about this check, we can consider
// dropping the system property and perform the check unconditionally
if (!disableThreadChecks &&
Thread.currentThread() != Application.eventThread)
{
throw new IllegalStateException(
"This operation is permitted on the event thread only; currentThread = "
+ Thread.currentThread().getName());
}
}
// Called from native, when a JNI exception has occurred
public static void reportException(Throwable t) {
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler handler =
currentThread.getUncaughtExceptionHandler();
handler.uncaughtException(currentThread, t);
}
abstract protected void _invokeAndWait(java.lang.Runnable runnable);
/**
* Block the current thread and wait until the given runnable finishes
* running on the native event loop thread.
*/
public static void invokeAndWait(java.lang.Runnable runnable) {
if (runnable == null) {
return;
}
if (isEventThread()) {
runnable.run();
} else {
GetApplication()._invokeAndWait(runnable);
}
}
abstract protected void _invokeLater(java.lang.Runnable runnable);
/**
* Schedule the given runnable to run on the native event loop thread
* some time in the future, and return immediately.
*/
public static void invokeLater(java.lang.Runnable runnable) {
if (runnable == null) {
return;
}
GetApplication()._invokeLater(runnable);
}
protected abstract Object _enterNestedEventLoop();
protected abstract void _leaveNestedEventLoop(Object retValue);
private static int nestedEventLoopCounter = 0;
/**
* Starts a nested event loop.
*
* Calling this method temporarily blocks processing of the current event,
* and starts a nested event loop to handle other native events. To
* proceed with the blocked execution path, the application should call the
* {@link #leaveNestedEventLoop(Object)} method.
*
* Note that this method may only be invoked on the main (event handling)
* thread.
*
* An application may enter several nested loops recursively. There's no
* limit of recursion other than that imposed by the native stack size.
*
* @return an object passed to the leaveNestedEventLoop() method
* @throws RuntimeException if the current thread is not the main thread
*/
static Object enterNestedEventLoop() {
checkEventThread();
nestedEventLoopCounter++;
try {
return GetApplication()._enterNestedEventLoop();
} finally {
nestedEventLoopCounter--;
}
}
/**
* Terminates the current nested event loop.
*
* After calling this method and returning from the current event handler,
* the execusion returns to the point where the {@link #enterNestedEventLoop}
* was called previously. You may specify a return value for the
* enterNestedEventLoop() method by passing the argument {@code retValue} to
* the leaveNestedEventLoop().
*
* Note that this method may only be invoked on the main (event handling)
* thread.
*
* @throws RuntimeException if the current thread is not the main thread
* @throws IllegalStateException if the application hasn't started a nested
* event loop
*/
static void leaveNestedEventLoop(Object retValue) {
checkEventThread();
if (nestedEventLoopCounter == 0) {
throw new IllegalStateException("Not in a nested event loop");
}
GetApplication()._leaveNestedEventLoop(retValue);
}
//TODO: move to the EventHandler
public void menuAboutAction() {
System.err.println("about");
}
// FACTORY METHODS
/**
* Create a window.
*
* The styleMask argument is a bitmask of window styles as defined in the
* Window class. Note, however, that visual kinds (UNTITLED, TITLED,
* or TRANSPARENT) can't be combined together. Also, functional types
* (NORMAL, POPUP, or UTILITY) can't be combined together. A window is
* allowed to be of exactly one visual kind, and exactly one functional
* type.
*/
public abstract Window createWindow(Window owner, Screen screen, int styleMask);
/**
* Create a window.
*
* The styleMask argument is a bitmask of window styles as defined in the
* Window class. Note, however, that visual kinds (UNTITLED, TITLED,
* or TRANSPARENT) can't be combined together. Also, functional types
* (NORMAL, POPUP, or UTILITY) can't be combined together. A window is
* allowed to be of exactly one visual kind, and exactly one functional
* type.
*/
public final Window createWindow(Screen screen, int styleMask) {
return createWindow(null, screen, styleMask);
}
public abstract Window createWindow(long parent);
public abstract View createView();
public abstract Cursor createCursor(int type);
public abstract Cursor createCursor(int x, int y, Pixels pixels);
protected abstract void staticCursor_setVisible(boolean visible);
protected abstract Size staticCursor_getBestSize(int width, int height);
public final Menu createMenu(String title) {
return new Menu(title);
}
public final Menu createMenu(String title, boolean enabled) {
return new Menu(title, enabled);
}
public final MenuBar createMenuBar() {
return new MenuBar();
}
public final MenuItem createMenuItem(String title) {
return createMenuItem(title, null);
}
public final MenuItem createMenuItem(String title, MenuItem.Callback callback) {
return createMenuItem(title, callback, KeyEvent.VK_UNDEFINED, KeyEvent.MODIFIER_NONE);
}
public final MenuItem createMenuItem(String title, MenuItem.Callback callback,
int shortcutKey, int shortcutModifiers) {
return createMenuItem(title, callback, shortcutKey, shortcutModifiers, null);
}
public final MenuItem createMenuItem(String title, MenuItem.Callback callback,
int shortcutKey, int shortcutModifiers, Pixels pixels) {
return new MenuItem(title, callback, shortcutKey, shortcutModifiers, pixels);
}
public abstract Pixels createPixels(int width, int height, ByteBuffer data);
public abstract Pixels createPixels(int width, int height, IntBuffer data);
public abstract Pixels createPixels(int width, int height, IntBuffer data, float scale);
protected abstract int staticPixels_getNativeFormat();
/* utility method called from native code */
static Pixels createPixels(int width, int height, int[] data, float scale) {
return Application.GetApplication().createPixels(width, height, IntBuffer.wrap(data), scale);
}
/* utility method called from native code */
static float getScaleFactor(final int x, final int y, final int w, final int h) {
float scale = 0.0f;
// Find the maximum scale for screens this area overlaps
for (Screen s : Screen.getScreens()) {
final int sx = s.getX(), sy = s.getY(), sw = s.getWidth(), sh = s.getHeight();
if (x < (sx + sw) && (x + w) > sx && y < (sy + sh) && (y + h) > sy) {
if (scale < s.getScale()) {
scale = s.getScale();
}
}
}
return scale == 0.0f ? 1.0f : scale;
}
public abstract Robot createRobot();
protected abstract double staticScreen_getVideoRefreshPeriod();
protected abstract Screen[] staticScreen_getScreens();
public abstract Timer createTimer(Runnable runnable);
protected abstract int staticTimer_getMinPeriod();
protected abstract int staticTimer_getMaxPeriod();
public final EventLoop createEventLoop() {
return new EventLoop();
}
protected abstract FileChooserResult staticCommonDialogs_showFileChooser(Window owner, String folder, String filename, String title, int type,
boolean multipleMode, ExtensionFilter[] extensionFilters, int defaultFilterIndex);
protected abstract File staticCommonDialogs_showFolderChooser(Window owner, String folder, String title);
protected abstract long staticView_getMultiClickTime();
protected abstract int staticView_getMultiClickMaxX();
protected abstract int staticView_getMultiClickMaxY();
protected abstract boolean _supportsTransparentWindows();
public boolean supportsTransparentWindows() {
checkEventThread();
return _supportsTransparentWindows();
}
public boolean hasTwoLevelFocus() {
return false;
}
public boolean hasVirtualKeyboard() {
return false;
}
public boolean hasTouch() {
return false;
}
public boolean hasMultiTouch() {
return false;
}
public boolean hasPointer() {
return true;
}
protected abstract boolean _supportsUnifiedWindows();
public boolean supportsUnifiedWindows() {
checkEventThread();
return _supportsUnifiedWindows();
}
protected boolean _supportsSystemMenu() {
//Only Mac supports system menu
return false;
}
public boolean supportsSystemMenu() {
checkEventThread();
return _supportsSystemMenu();
}
}