--- /dev/null
+package org.simantics.utils.ui;\r
+\r
+import java.util.concurrent.Semaphore;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.eclipse.swt.SWTException;\r
+import org.eclipse.swt.custom.CTabFolder;\r
+import org.eclipse.swt.custom.CTabItem;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.TabFolder;\r
+import org.eclipse.swt.widgets.TabItem;\r
+import org.eclipse.swt.widgets.Widget;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+public class SWTUtils {\r
+\r
+ /**\r
+ * When scheduling a runnable to be executed asynchronously in the SWT\r
+ * thread it is not possible to do this in a safe manner based on a\r
+ * {@link Display} instance. When invoking\r
+ * {@link Display#asyncExec(Runnable)} from a non-SWT thread, it may throw\r
+ * {@link SWTException} even if you've first checked\r
+ * {@link Display#isDisposed()}.\r
+ * \r
+ * This method will run {@link Display#asyncExec(Runnable)} using the\r
+ * display returned by the specified widget if the widget is or does not\r
+ * become disposed while trying to get the display from it.\r
+ * \r
+ * @param display the display to asyncExec with\r
+ * @param runnable the runnable to execute with\r
+ * {@link Display#asyncExec(Runnable)}\r
+ * @return <code>true</code> if the executable was scheduled, false\r
+ * otherwise\r
+ */\r
+ public static boolean asyncExec(Display display, Runnable runnable) {\r
+ try {\r
+ if (display.isDisposed())\r
+ return false;\r
+ display.asyncExec(runnable);\r
+ return true;\r
+ } catch (SWTException e) {\r
+ // widget was disposed between isDisposed and getDisplay.\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * When scheduling a runnable to be executed asynchronously in the SWT\r
+ * thread it is not possible to do this in a safe manner based on a\r
+ * {@link Widget} instance. When invoking {@link Widget#getDisplay()} from a\r
+ * non-SWT thread, it may throw {@link SWTException} even if you've first\r
+ * checked {@link Widget#isDisposed()}.\r
+ * \r
+ * This method will run {@link Display#asyncExec(Runnable)} using the\r
+ * display returned by the specified widget if the widget is or does not\r
+ * become disposed while trying to get the display from it.\r
+ * \r
+ * @param widget the widget to get {@link Display} from\r
+ * @param runnable the runnable to execute with {@link Display#asyncExec(Runnable)}\r
+ * @return <code>true</code> if the executable was scheduled, false otherwise\r
+ */\r
+ public static boolean asyncExec(Widget widget, Runnable runnable) {\r
+ try {\r
+ if (widget.isDisposed())\r
+ return false;\r
+ widget.getDisplay().asyncExec(runnable);\r
+ return true;\r
+ } catch (SWTException e) {\r
+ // widget was disposed between isDisposed and getDisplay.\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Invokes a runnable in a specified thread and dispatches SWT events while\r
+ * waiting for the runnable to complete.\r
+ * \r
+ * <p>\r
+ * The runnable must not perform any operations that may cause more SWT\r
+ * events to be dispatched.\r
+ * \r
+ * <p>\r
+ * To be invoked from SWT thread only.\r
+ * \r
+ * @param control\r
+ * @param inThread\r
+ * @param invoke\r
+ * @throws InterruptedException\r
+ */\r
+ public static void invokeAndDispatchEvents(Display display, IThreadWorkQueue inThread, final Runnable invoke) throws InterruptedException {\r
+ if (display.isDisposed())\r
+ throw new IllegalArgumentException("display is disposed");\r
+ final Semaphore sem = new Semaphore(0);\r
+ ThreadUtils.asyncExec(inThread, new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ try {\r
+ invoke.run();\r
+ } finally {\r
+ sem.release();\r
+ }\r
+ }\r
+ });\r
+ boolean done = false;\r
+ while (!done) {\r
+ done = sem.tryAcquire(10, TimeUnit.MILLISECONDS);\r
+ while (!done && display.readAndDispatch()) {\r
+ /*\r
+ * Note: readAndDispatch can cause this to be disposed.\r
+ */\r
+ done = sem.tryAcquire();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Look for an object starting from an SWT (composite) control with a\r
+ * specific filter that can inspect the control freely.\r
+ * \r
+ * @return the first T returned by the control filter or <code>null</code>\r
+ * if the filter returned no results.\r
+ */\r
+ public static <T> T tryGetObject(Control control, ControlFilter<T> filter) {\r
+ if (control == null || control.isDisposed())\r
+ return null;\r
+ T t = filter.accept(control);\r
+ if (t != null)\r
+ return t;\r
+ if (control instanceof Composite) {\r
+ if (control instanceof TabFolder) {\r
+ TabFolder tf = (TabFolder) control;\r
+ for (TabItem item : tf.getSelection()) {\r
+ t = tryGetObject(item.getControl(), filter);\r
+ if (t != null)\r
+ return t;\r
+ }\r
+ } else if (control instanceof CTabFolder) {\r
+ CTabFolder tf = (CTabFolder) control;\r
+ CTabItem item = tf.getSelection();\r
+ if (item != null) {\r
+ t = tryGetObject(item.getControl(), filter);\r
+ if (t != null)\r
+ return t;\r
+ }\r
+ } else {\r
+ Composite c = (Composite) control;\r
+ for (Control child : c.getChildren()) {\r
+ t = tryGetObject(child, filter);\r
+ if (t != null)\r
+ return t;\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @FunctionalInterface\r
+ public interface ControlFilter<T> {\r
+ T accept(Control control);\r
+ }\r
+\r
+}\r