package org.openslx.dozmod.gui.helper;
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;
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();
/**
* 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 null;
}
/**
* 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;
}
}