package org.simantics.utils.ui; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Widget; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.ThreadUtils; public class SWTUtils { /** * When scheduling a runnable to be executed asynchronously in the SWT * thread it is not possible to do this in a safe manner based on a * {@link Display} instance. When invoking * {@link Display#asyncExec(Runnable)} from a non-SWT thread, it may throw * {@link SWTException} even if you've first checked * {@link Display#isDisposed()}. * * This method will run {@link Display#asyncExec(Runnable)} using the * display returned by the specified widget if the widget is or does not * become disposed while trying to get the display from it. * * @param display the display to asyncExec with * @param runnable the runnable to execute with * {@link Display#asyncExec(Runnable)} * @return true if the executable was scheduled, false * otherwise */ public static boolean asyncExec(Display display, Runnable runnable) { try { if (display.isDisposed()) return false; display.asyncExec(runnable); return true; } catch (SWTException e) { // widget was disposed between isDisposed and getDisplay. return false; } } /** * When scheduling a runnable to be executed asynchronously in the SWT * thread it is not possible to do this in a safe manner based on a * {@link Widget} instance. When invoking {@link Widget#getDisplay()} from a * non-SWT thread, it may throw {@link SWTException} even if you've first * checked {@link Widget#isDisposed()}. * * This method will run {@link Display#asyncExec(Runnable)} using the * display returned by the specified widget if the widget is or does not * become disposed while trying to get the display from it. * * @param widget the widget to get {@link Display} from * @param runnable the runnable to execute with {@link Display#asyncExec(Runnable)} * @return true if the executable was scheduled, false otherwise */ public static boolean asyncExec(Widget widget, Runnable runnable) { try { if (widget.isDisposed()) return false; widget.getDisplay().asyncExec(runnable); return true; } catch (SWTException e) { // widget was disposed between isDisposed and getDisplay. return false; } } /** * Invokes a runnable in a specified thread and dispatches SWT events while * waiting for the runnable to complete. * *

* The runnable must not perform any operations that may cause more SWT * events to be dispatched. * *

* To be invoked from SWT thread only. * * @param control * @param inThread * @param invoke * @throws InterruptedException */ public static void invokeAndDispatchEvents(Display display, IThreadWorkQueue inThread, final Runnable invoke) throws InterruptedException { if (display.isDisposed()) throw new IllegalArgumentException("display is disposed"); final Semaphore sem = new Semaphore(0); ThreadUtils.asyncExec(inThread, new Runnable() { @Override public void run() { try { invoke.run(); } finally { sem.release(); } } }); boolean done = false; while (!done) { done = sem.tryAcquire(10, TimeUnit.MILLISECONDS); while (!done && display.readAndDispatch()) { /* * Note: readAndDispatch can cause this to be disposed. */ done = sem.tryAcquire(); } } } /** * Look for an object starting from an SWT (composite) control with a * specific filter that can inspect the control freely. * * @return the first T returned by the control filter or null * if the filter returned no results. */ public static T tryGetObject(Control control, ControlFilter filter) { if (control == null || control.isDisposed()) return null; T t = filter.accept(control); if (t != null) return t; if (control instanceof Composite) { if (control instanceof TabFolder) { TabFolder tf = (TabFolder) control; for (TabItem item : tf.getSelection()) { t = tryGetObject(item.getControl(), filter); if (t != null) return t; } } else if (control instanceof CTabFolder) { CTabFolder tf = (CTabFolder) control; CTabItem item = tf.getSelection(); if (item != null) { t = tryGetObject(item.getControl(), filter); if (t != null) return t; } } else { Composite c = (Composite) control; for (Control child : c.getChildren()) { t = tryGetObject(child, filter); if (t != null) return t; } } } return null; } @FunctionalInterface public interface ControlFilter { T accept(Control control); } }