diff options
author | Simon Rettberg | 2015-07-15 11:56:41 +0200 |
---|---|---|
committer | Simon Rettberg | 2015-07-15 11:56:41 +0200 |
commit | f1240b0ddef62b03da3ca9d87812e3be2ff36e15 (patch) | |
tree | 3d65208981043fd7e4ca2c61d462d6c602175d95 /dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java | |
parent | [client] Use vmx parser to check validity of VM (work in progress) (diff) | |
download | tutor-module-f1240b0ddef62b03da3ca9d87812e3be2ff36e15.tar.gz tutor-module-f1240b0ddef62b03da3ca9d87812e3be2ff36e15.tar.xz tutor-module-f1240b0ddef62b03da3ca9d87812e3be2ff36e15.zip |
[client] Clean up SWT resources when exiting
Diffstat (limited to 'dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java')
-rw-r--r-- | dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java new file mode 100644 index 00000000..8e5231ae --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java @@ -0,0 +1,269 @@ +package org.openslx.dozmod.gui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.log4j.Logger; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Monitor; +import org.eclipse.swt.widgets.Shell; +import org.openslx.dozmod.gui.helper.MessageType; + +public class Gui { + + private static final Logger LOGGER = Logger.getLogger(Gui.class); + + /** + * The one and only display to use throughout the application + */ + public static final Display display = new Display(); + + /** + * All active shells - we don't use display.getShells() as it is slow... + */ + private static final List<Shell> shells = new ArrayList<>(); + + private static volatile int exitCode = 0; + + /** + * Center the given shell on the {@link Monitor} it is displayed on. + * + * @param shell Some shell + */ + public static void centerShell(Shell shell) { + Monitor activeMonitor = getMonitorFromRectangle(shell.getBounds(), true); + Rectangle bounds = activeMonitor.getClientArea(); + Rectangle rect = shell.getBounds(); + int x = bounds.x + (bounds.width - rect.width) / 2; + int y = bounds.y + (bounds.height - rect.height) / 2; + shell.setLocation(x, y); + } + + /** + * Make sure the given shell fits the {@link Monitor} it is displayed on. + * + * @param shell Some shell + */ + public static void limitShellSize(Shell shell) { + Monitor activeMonitor = getMonitorFromRectangle(shell.getBounds(), true); + Rectangle bounds = activeMonitor.getClientArea(); + Rectangle rect = shell.getBounds(); + boolean changed = false; + if (rect.width + 20 > bounds.width) { + rect.width = bounds.width - 20; + changed = true; + } + if (rect.height + 20 > bounds.height) { + rect.height = bounds.height - 20; + changed = true; + } + if (changed) { + shell.setSize(rect.width, rect.height); + rect = shell.getBounds(); + } + changed = false; + if (rect.x + rect.width >= bounds.x + bounds.width) { + rect.x = 5; + changed = true; + } + if (rect.y + rect.height >= bounds.y + bounds.height) { + rect.y = 5; + changed = true; + } + if (changed) { + shell.setLocation(rect.x, rect.y); + } + } + + /** + * Get the {@link Monitor} which the given {@link Point} lies in. + * + * @param point The point in question + * @param defaultToPrimary if no monitor matches the check, return the + * primary monitor if true, <code>null</code> otherwise + * @return the {@link Monitor} + */ + public static Monitor getMonitorFromPoint(Point point, boolean defaultToPrimary) { + Monitor[] monitors = display.getMonitors(); + for (Monitor monitor : monitors) { + Rectangle bounds = monitor.getBounds(); + if (bounds.contains(point)) + return monitor; + } + if (defaultToPrimary) + return display.getPrimaryMonitor(); + return null; + } + + /** + * Get the {@link Monitor} which most of the given rectangle overlaps. + * + * @param rect The rectangle to check + * @param defaultToPrimary if no monitor matches the check, return the + * primary monitor if true, <code>null</code> otherwise + * @return the {@link Monitor} + */ + public static Monitor getMonitorFromRectangle(Rectangle rect, boolean defaultToPrimary) { + // Make sure rectangle is in bounds. This is not completely accurate + // in case there are multiple monitors that have different resolutions. + Rectangle bounds = display.getBounds(); + if (rect.x + rect.width >= bounds.x + bounds.width) { + rect.width -= (rect.x + rect.width) - (bounds.x + bounds.width); + if (rect.width < 1) + rect.width = 1; + } + if (rect.y + rect.height >= bounds.y + bounds.height) { + rect.height -= (rect.y + rect.height) - (bounds.y + bounds.height); + if (rect.height < 1) + rect.height = 1; + } + if (rect.x < bounds.x) { + rect.width -= bounds.x - rect.x; + rect.x = bounds.x; + } + if (rect.y < bounds.y) { + rect.height -= bounds.y - rect.y; + rect.y = bounds.y; + } + // Now just use the same code as *FromPoint by using the rectangle's center + return getMonitorFromPoint(new Point(rect.x + rect.width / 2, rect.y + rect.height / 2), + defaultToPrimary); + } + + /** + * Run given task in the GUI thread, blocking the calling thread until the + * task is done. + * + * @param task Task to run + * @return return value of the task + */ + public static <T> T syncExec(final GuiCallable<T> task) { + final AtomicReference<T> instance = new AtomicReference<>(); + display.syncExec(new Runnable() { + @Override + public void run() { + try { + instance.set(task.run()); + } catch (Throwable e) { + LOGGER.warn("syncExec failed!", e); + } + } + }); + return instance.get(); + } + + /** + * Run given task as soon as possible in the GUI thread, but don't block + * calling thread. + * + * @param task Task to run + */ + public static void asyncExec(final Runnable task) { + display.asyncExec(task); + } + + /** + * Pretty much the same as Callable, but no exceptions are allowed. + * + * @param <T> return value + */ + public static interface GuiCallable<T> { + T run(); + } + + /** + * Generic helper to show a message box to the user, and optionally log the + * message to the log file. + * + * @param parent parent shell this message box belongs to + * @param message Message to display. Can be multiline. + * @param messageType Type of message (warning, information) + * @param logger Logger instance to log to. Can be null. + * @param exception Exception related to this message. Can be null. + * @return true if OK, YES or RETRY was clicked, false for CANCEL or NO + */ + public static boolean showMessageBox(Shell parent, String message, MessageType messageType, + Logger logger, Throwable exception) { + if (logger != null) + logger.log(messageType.logPriority, message, exception); + if (exception != null) + message += "\n\n" + exception.getClass().getSimpleName() + " (Siehe Logdatei)"; + MessageBox box = new MessageBox(parent, messageType.style); + box.setMessage(message); + box.setText(messageType.title); + int ret = box.open(); + return ret == SWT.OK || ret == SWT.RETRY || ret == SWT.YES; + } + + /** + * Run the GUI mainloop as long as a window exists. This method does not + * return. + */ + public static void mainloop() { + do { + Iterator<Shell> it = shells.iterator(); + while (it.hasNext()) { + if (it.next().isDisposed()) + it.remove(); + } + if (!display.readAndDispatch()) + display.sleep(); + } while (!shells.isEmpty()); + display.dispose(); + System.exit(exitCode); + } + + /** + * Exit application - dispose all shells, so mainloop will terminate. + * + * @param code + */ + public static void exit(int code) { + exitCode = code; + asyncExec(new Runnable() { + @Override + public void run() { + for (Shell shell : shells) { + shell.dispose(); + } + } + }); + } + + /** + * Wrapper to get a new shell. This adds the shell to the list of shells, + * which we need to determine whether the app should still be running. + * + * @param style the style of the {@link Shell} + * @return a new {@link Shell} that has no other {@link Shell} as its + * parent. + */ + public static Shell newShell(int style) { + Shell shell = new Shell(display, style); + shells.add(shell); + return shell; + } + + /** + * Wrapper to get a new shell that is a child of another shell. This adds + * the shell to the list of shells, which we need to determine whether the + * app should still be running. + * + * @param parent the parent {@link Shell} + * @param style the style of the {@link Shell} + * @return a new {@link Shell} that has no other {@link Shell} as its + * parent. + */ + public static Shell newShell(Shell parent, int style) { + Shell shell = new Shell(parent, style); + shells.add(shell); + return shell; + } + +} |