summaryrefslogtreecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java
diff options
context:
space:
mode:
authorSimon Rettberg2015-07-15 11:56:41 +0200
committerSimon Rettberg2015-07-15 11:56:41 +0200
commitf1240b0ddef62b03da3ca9d87812e3be2ff36e15 (patch)
tree3d65208981043fd7e4ca2c61d462d6c602175d95 /dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java
parent[client] Use vmx parser to check validity of VM (work in progress) (diff)
downloadtutor-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.java269
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;
+ }
+
+}