X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FAwtEnvironment.java;fp=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FAwtEnvironment.java;h=9baf6a6e1b7517b649856b8ff2ca888a247eda03;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=9a9d84ae1bc96f976dc646b03a56212ab1d5cf7b;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtEnvironment.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtEnvironment.java index 9a9d84ae1..9baf6a6e1 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtEnvironment.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtEnvironment.java @@ -1,337 +1,337 @@ -/******************************************************************************* - * Copyright (c) 2007 SAS Institute. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * SAS Institute - initial API and implementation - *******************************************************************************/ -package org.simantics.utils.ui.internal.awt; - -import java.awt.EventQueue; -import java.awt.Frame; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.awt.SWT_AWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - - - -/** - * An environment to enable the proper display of AWT/Swing windows within a SWT or RCP - * application. This class extends the base {@link org.eclipse.swt.awt.SWT_AWT Eclipse SWT/AWT integration} - * support by - * - *

- * This class is most helpful to applications which create new AWT/Swing windows (e.g. dialogs) rather - * than those which embed AWT/Swing components in SWT windows. For support specific to embedding - * AWT/Swing components see {@link EmbeddedSwingComposite}. - *

- * There is at most one instance of this class per SWT - * {@link org.eclipse.swt.widgets.Display Display}. In almost all applications - * this means that there is exactly one instance for the entire application. In fact, the - * current implementation always limits the number of instances to exactly one. - *

- * An instance of this class can be obtained with the static - * {@link #getInstance(Display)} method. -*/ -public final class AwtEnvironment { - // TODO: add pop-up dismissal and font synchronization support to this level? - - private static final String GTK_LOOK_AND_FEEL_NAME = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; //$NON-NLS-1$ - - private static AwtEnvironment instance = null; - private static boolean isLookAndFeelInitialized = false; - - private final Display display; - private final AwtDialogListener dialogListener; - - /** - * Returns the single instance of AwtEnvironment for the given display. On - * the first call to this method, the necessary initialization to allow - * AWT/Swing code to run properly within an Eclipse application is done. - * This initialization includes setting the approprite look and feel and - * registering the necessary listeners to ensure proper behavior of modal - * dialogs. - *

- * The first call to this method must occur before any AWT/Swing APIs are - * called. - *

- * The current implementation limits the number of instances of - * AwtEnvironment to one. If this method is called with a display different - * to one used on a previous call, {@link UnsupportedOperationException} is - * thrown. - * - * @param display - * the non-null SWT display - * @return the AWT environment - * @exception IllegalArgumentException - *

- * @exception UnsupportedOperationException - - * on attempt to use multiple displays. - */ - public static AwtEnvironment getInstance(Display display) { - // For now assume a single display. If necessary, this implementation - // can be changed to create multiple environments for multiple display - // applications. - // TODO: add multiple display support - if (display == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - if ((instance != null) && !display.equals(instance.display)) { - throw new UnsupportedOperationException("Multiple displays not supported"); - } - synchronized (AwtEnvironment.class) { - if (instance == null) { - instance = new AwtEnvironment(display); - } - } - return instance; - } - - // Private constructor - clients use getInstance() to obtain instances - private AwtEnvironment(Display display) { - assert display != null; - - /* - * This property removes a large amount of flicker from embedded swing - * components. Ideally it would not be set until EmbeddedSwingComposite - * is used, but since its value is read once and cached by AWT, it needs - * to be set before any AWT/Swing APIs are called. - */ - // TODO: this is effective only on Windows. - System.setProperty("sun.awt.noerasebackground", "true"); //$NON-NLS-1$//$NON-NLS-2$ - - /* - * RCP apps always want the standard platform look and feel It's - * important to wait for the L&F to be set so that any subsequent calls - * to createFrame() will be return a frame with the proper L&F (note - * that createFrame() happens on the SWT thread). - * - * The call to invokeAndWait is safe because - * the first call AwtEnvironment.getInstance should happen - * before any (potential deadlocking) activity occurs on the - * AWT thread. - */ - try { - EventQueue.invokeAndWait(new Runnable() { - public void run() { - setSystemLookAndFeel(); - } - }); - } catch (InterruptedException e) { - SWT.error(SWT.ERROR_FAILED_EXEC, e); - } catch (InvocationTargetException e) { - SWT.error(SWT.ERROR_FAILED_EXEC, e.getCause()); - } - - this.display = display; - - // Listen for AWT modal dialogs to make them modal application-wide - dialogListener = new AwtDialogListener(display); - } - - /** - * Invokes the given runnable in the AWT event thread while blocking user - * input on the SWT event thread. The SWT event thread will remain blocked - * until the runnable task completes, at which point this method will - * return. - *

- * This method is useful for displayng modal AWT/Swing dialogs from the SWT - * event thread. The modal AWT/Swing dialog will always block input across - * the whole application, but not until it appears. By calling this method, - * it is guaranteed that SWT input is blocked immediately, even before the - * AWT/Swing dialog appears. - *

- * To avoid unnecessary flicker, AWT/Swing dialogs should have their parent - * set to a frame returned by {@link #createDialogParentFrame()}. - *

- * This method must be called from the SWT event thread. - * - * @param runnable - * the code to schedule on the AWT event thread - * @exception IllegalArgumentException - *

- * @exception SWTException - * - */ - public void invokeAndBlockSwt(final Runnable runnable) { - assert display != null; - - /* - * This code snippet is based on the following thread on - * news.eclipse.platform.swt: - * http://dev.eclipse.org/newslists/news.eclipse.platform.swt/msg24234.html - */ - if (runnable == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - if (display != Display.getCurrent()) { - SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); - } - - // Switch to the AWT thread... - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - // do swing work... - runnable.run(); - } finally { - display.asyncExec(new Runnable() { - public void run() { - // Unblock SWT - SwtInputBlocker.unblock(); - } - }); - } - } - }); - - // Prevent user input on SWT components - SwtInputBlocker.block(); - } - - /** - * Creates an AWT frame suitable as a parent for AWT/Swing dialogs. - *

- * This method must be called from the SWT event thread. There must be an active - * shell associated with the environment's display. - *

- * The created frame is a non-visible child of the active shell and will be disposed when that shell - * is disposed. - *

- * See {@link #createDialogParentFrame(Shell)} for more details. - * - * @return a {@link java.awt.Frame} to be used for parenting dialogs - * @exception SWTException - *

- * @exception IllegalStateException - * if the current display has no shells - */ - public Frame createDialogParentFrame() { - if (display != Display.getCurrent()) { - SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); - } - Shell parent = display.getActiveShell(); - if (parent == null) { - throw new IllegalStateException("No Active Shell"); - } - return createDialogParentFrame(parent); - } - - /** - * Creates an AWT frame suitable as a parent for AWT/Swing dialogs. - *

- * This method must be called from the SWT event thread. There must be an active - * shell associated with the environment's display. - *

- * The created frame is a non-visible child of the given shell and will be disposed when that shell - * is disposed. - *

- * This method is useful for creating a frame to parent any AWT/Swing - * dialogs created for use inside a SWT application. A modal AWT/Swing - * dialogs will flicker less if its parent is set to the returned frame - * rather than to null or to an independently created {@link java.awt.Frame}. - * - * @return a {@link java.awt.Frame} to be used for parenting dialogs - * @exception SWTException - *

- * @exception IllegalStateException - * if the current display has no shells - */ - public Frame createDialogParentFrame(Shell parent) { - if (parent == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - if (display != Display.getCurrent()) { - SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); - } - Shell shell = new Shell(parent); - shell.setVisible(false); - Composite composite = new Composite(shell, SWT.EMBEDDED); - return SWT_AWT.new_Frame(composite); - } - - // Find a shell to use, giving preference to the active shell. - Shell getShell() { - Shell shell = display.getActiveShell(); - if (shell == null) { - Shell[] allShells = display.getShells(); - if (allShells.length > 0) { - shell = allShells[0]; - } - } - return shell; - } - - void requestAwtDialogFocus() { - assert dialogListener != null; - - dialogListener.requestFocus(); - } - - private void setSystemLookAndFeel() { - assert EventQueue.isDispatchThread(); // On AWT event thread - - if (!isLookAndFeelInitialized) { - isLookAndFeelInitialized = true; - try { - String systemLaf = UIManager.getSystemLookAndFeelClassName(); - String xplatLaf = UIManager.getCrossPlatformLookAndFeelClassName(); - - // Java makes metal the system look and feel if running under a - // non-gnome Linux desktop. Fix that here, if the RCP itself is - // running - // with the GTK windowing system set. - if (xplatLaf.equals(systemLaf) && Platform.isGtk()) { - systemLaf = GTK_LOOK_AND_FEEL_NAME; - } - UIManager.setLookAndFeel(systemLaf); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (UnsupportedLookAndFeelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - // This method is called by unit tests - static void reset() { - instance = null; - } - -} +/******************************************************************************* + * Copyright (c) 2007 SAS Institute. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAS Institute - initial API and implementation + *******************************************************************************/ +package org.simantics.utils.ui.internal.awt; + +import java.awt.EventQueue; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.awt.SWT_AWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + + + +/** + * An environment to enable the proper display of AWT/Swing windows within a SWT or RCP + * application. This class extends the base {@link org.eclipse.swt.awt.SWT_AWT Eclipse SWT/AWT integration} + * support by + * + *

+ * This class is most helpful to applications which create new AWT/Swing windows (e.g. dialogs) rather + * than those which embed AWT/Swing components in SWT windows. For support specific to embedding + * AWT/Swing components see {@link EmbeddedSwingComposite}. + *

+ * There is at most one instance of this class per SWT + * {@link org.eclipse.swt.widgets.Display Display}. In almost all applications + * this means that there is exactly one instance for the entire application. In fact, the + * current implementation always limits the number of instances to exactly one. + *

+ * An instance of this class can be obtained with the static + * {@link #getInstance(Display)} method. +*/ +public final class AwtEnvironment { + // TODO: add pop-up dismissal and font synchronization support to this level? + + private static final String GTK_LOOK_AND_FEEL_NAME = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; //$NON-NLS-1$ + + private static AwtEnvironment instance = null; + private static boolean isLookAndFeelInitialized = false; + + private final Display display; + private final AwtDialogListener dialogListener; + + /** + * Returns the single instance of AwtEnvironment for the given display. On + * the first call to this method, the necessary initialization to allow + * AWT/Swing code to run properly within an Eclipse application is done. + * This initialization includes setting the approprite look and feel and + * registering the necessary listeners to ensure proper behavior of modal + * dialogs. + *

+ * The first call to this method must occur before any AWT/Swing APIs are + * called. + *

+ * The current implementation limits the number of instances of + * AwtEnvironment to one. If this method is called with a display different + * to one used on a previous call, {@link UnsupportedOperationException} is + * thrown. + * + * @param display + * the non-null SWT display + * @return the AWT environment + * @exception IllegalArgumentException + *

+ * @exception UnsupportedOperationException - + * on attempt to use multiple displays. + */ + public static AwtEnvironment getInstance(Display display) { + // For now assume a single display. If necessary, this implementation + // can be changed to create multiple environments for multiple display + // applications. + // TODO: add multiple display support + if (display == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + if ((instance != null) && !display.equals(instance.display)) { + throw new UnsupportedOperationException("Multiple displays not supported"); + } + synchronized (AwtEnvironment.class) { + if (instance == null) { + instance = new AwtEnvironment(display); + } + } + return instance; + } + + // Private constructor - clients use getInstance() to obtain instances + private AwtEnvironment(Display display) { + assert display != null; + + /* + * This property removes a large amount of flicker from embedded swing + * components. Ideally it would not be set until EmbeddedSwingComposite + * is used, but since its value is read once and cached by AWT, it needs + * to be set before any AWT/Swing APIs are called. + */ + // TODO: this is effective only on Windows. + System.setProperty("sun.awt.noerasebackground", "true"); //$NON-NLS-1$//$NON-NLS-2$ + + /* + * RCP apps always want the standard platform look and feel It's + * important to wait for the L&F to be set so that any subsequent calls + * to createFrame() will be return a frame with the proper L&F (note + * that createFrame() happens on the SWT thread). + * + * The call to invokeAndWait is safe because + * the first call AwtEnvironment.getInstance should happen + * before any (potential deadlocking) activity occurs on the + * AWT thread. + */ + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + setSystemLookAndFeel(); + } + }); + } catch (InterruptedException e) { + SWT.error(SWT.ERROR_FAILED_EXEC, e); + } catch (InvocationTargetException e) { + SWT.error(SWT.ERROR_FAILED_EXEC, e.getCause()); + } + + this.display = display; + + // Listen for AWT modal dialogs to make them modal application-wide + dialogListener = new AwtDialogListener(display); + } + + /** + * Invokes the given runnable in the AWT event thread while blocking user + * input on the SWT event thread. The SWT event thread will remain blocked + * until the runnable task completes, at which point this method will + * return. + *

+ * This method is useful for displayng modal AWT/Swing dialogs from the SWT + * event thread. The modal AWT/Swing dialog will always block input across + * the whole application, but not until it appears. By calling this method, + * it is guaranteed that SWT input is blocked immediately, even before the + * AWT/Swing dialog appears. + *

+ * To avoid unnecessary flicker, AWT/Swing dialogs should have their parent + * set to a frame returned by {@link #createDialogParentFrame()}. + *

+ * This method must be called from the SWT event thread. + * + * @param runnable + * the code to schedule on the AWT event thread + * @exception IllegalArgumentException + *

+ * @exception SWTException + * + */ + public void invokeAndBlockSwt(final Runnable runnable) { + assert display != null; + + /* + * This code snippet is based on the following thread on + * news.eclipse.platform.swt: + * http://dev.eclipse.org/newslists/news.eclipse.platform.swt/msg24234.html + */ + if (runnable == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + if (display != Display.getCurrent()) { + SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); + } + + // Switch to the AWT thread... + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + // do swing work... + runnable.run(); + } finally { + display.asyncExec(new Runnable() { + public void run() { + // Unblock SWT + SwtInputBlocker.unblock(); + } + }); + } + } + }); + + // Prevent user input on SWT components + SwtInputBlocker.block(); + } + + /** + * Creates an AWT frame suitable as a parent for AWT/Swing dialogs. + *

+ * This method must be called from the SWT event thread. There must be an active + * shell associated with the environment's display. + *

+ * The created frame is a non-visible child of the active shell and will be disposed when that shell + * is disposed. + *

+ * See {@link #createDialogParentFrame(Shell)} for more details. + * + * @return a {@link java.awt.Frame} to be used for parenting dialogs + * @exception SWTException + *

+ * @exception IllegalStateException + * if the current display has no shells + */ + public Frame createDialogParentFrame() { + if (display != Display.getCurrent()) { + SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); + } + Shell parent = display.getActiveShell(); + if (parent == null) { + throw new IllegalStateException("No Active Shell"); + } + return createDialogParentFrame(parent); + } + + /** + * Creates an AWT frame suitable as a parent for AWT/Swing dialogs. + *

+ * This method must be called from the SWT event thread. There must be an active + * shell associated with the environment's display. + *

+ * The created frame is a non-visible child of the given shell and will be disposed when that shell + * is disposed. + *

+ * This method is useful for creating a frame to parent any AWT/Swing + * dialogs created for use inside a SWT application. A modal AWT/Swing + * dialogs will flicker less if its parent is set to the returned frame + * rather than to null or to an independently created {@link java.awt.Frame}. + * + * @return a {@link java.awt.Frame} to be used for parenting dialogs + * @exception SWTException + *

+ * @exception IllegalStateException + * if the current display has no shells + */ + public Frame createDialogParentFrame(Shell parent) { + if (parent == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + if (display != Display.getCurrent()) { + SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); + } + Shell shell = new Shell(parent); + shell.setVisible(false); + Composite composite = new Composite(shell, SWT.EMBEDDED); + return SWT_AWT.new_Frame(composite); + } + + // Find a shell to use, giving preference to the active shell. + Shell getShell() { + Shell shell = display.getActiveShell(); + if (shell == null) { + Shell[] allShells = display.getShells(); + if (allShells.length > 0) { + shell = allShells[0]; + } + } + return shell; + } + + void requestAwtDialogFocus() { + assert dialogListener != null; + + dialogListener.requestFocus(); + } + + private void setSystemLookAndFeel() { + assert EventQueue.isDispatchThread(); // On AWT event thread + + if (!isLookAndFeelInitialized) { + isLookAndFeelInitialized = true; + try { + String systemLaf = UIManager.getSystemLookAndFeelClassName(); + String xplatLaf = UIManager.getCrossPlatformLookAndFeelClassName(); + + // Java makes metal the system look and feel if running under a + // non-gnome Linux desktop. Fix that here, if the RCP itself is + // running + // with the GTK windowing system set. + if (xplatLaf.equals(systemLaf) && Platform.isGtk()) { + systemLaf = GTK_LOOK_AND_FEEL_NAME; + } + UIManager.setLookAndFeel(systemLaf); + } catch (ClassNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (UnsupportedLookAndFeelException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + // This method is called by unit tests + static void reset() { + instance = null; + } + +}