X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FAwtDialogListener.java;fp=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FAwtDialogListener.java;h=23cf6673bc2323515c17bc857efa2b7baf5bec08;hp=0000000000000000000000000000000000000000;hb=ac5f1da15cc639da880fea86a7b828c8fa2e1b7e;hpb=b8392422ec5b5961202d941ec447018526d26dd2 diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtDialogListener.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtDialogListener.java new file mode 100644 index 000000000..23cf6673b --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtDialogListener.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * 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.AWTEvent; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.AWTEventListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.widgets.Display; + +/** + * A listener that insures the proper modal behavior of Swing dialogs when running + * within a SWT environment. When initialized, it blocks and unblocks SWT input + * as modal Swing dialogs are shown and hidden. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +class AwtDialogListener implements AWTEventListener, ComponentListener { + + // modalDialogs should be accessed only from the AWT thread, so no + // synchronization is needed. + private final List modalDialogs = new ArrayList(); + private final Display display; + + /** + * Registers this object as an AWT event listener so that Swing dialogs have the + * proper modal behavior in the containing SWT environment. This is called automatically + * when you construct a {@link EmbeddedSwingComposite}, and it + * need not be called separately in that case. + * @param shell + */ + AwtDialogListener(Display display) { + assert display != null; + + this.display = display; + Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.WINDOW_EVENT_MASK); + } + + private void handleRemovedDialog(Dialog awtDialog, boolean removeListener) { + assert awtDialog != null; + assert modalDialogs != null; + assert display != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // System.out.println("Remove dialog: " + awtDialog); + if (removeListener) { + awtDialog.removeComponentListener(this); + } + // Note: there is no isModal() check here because the dialog might + // have been changed from modal to non-modal after it was opened. In this case + // the currently visible dialog would still act modal and we'd need to unblock + // SWT here when it goes away. + if (modalDialogs.remove(awtDialog)) { + display.asyncExec(new Runnable() { + public void run() { + SwtInputBlocker.unblock(); + } + }); + } + } + + private void handleAddedDialog(final Dialog awtDialog) { + assert awtDialog != null; + assert modalDialogs != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // System.out.println("Add dialog: " + awtDialog); + if (modalDialogs.contains(awtDialog) || !awtDialog.isModal() || !awtDialog.isVisible()) { + return; + } + modalDialogs.add(awtDialog); + awtDialog.addComponentListener(this); + display.asyncExec(new Runnable() { + public void run() { + SwtInputBlocker.block(); + } + }); + } + + void requestFocus() { + // TODO: this does not always bring the dialog to the top + // under some Linux desktops/window managers (e.g. metacity under GNOME). + EventQueue.invokeLater(new Runnable() { + public void run() { + assert modalDialogs != null; + + int size = modalDialogs.size(); + if (size > 0) { + final Dialog awtDialog = (Dialog)modalDialogs.get(size - 1); + + // In one case, a call to requestFocus() alone does not + // bring the AWT dialog to the top. This happens if the + // dialog is given a null parent frame. When opened, the dialog + // can be hidden by the SWT window even when it obtains focus. + // Calling toFront() solves the problem, but... + // + // There are still problems if the Metal look and feel is in use. + // The SWT window will hide the dialog the first time it is + // selected. Once the dialog is brought back to the front by + // the user, there is no further problem. + // + // Why? It looks like SWT is not being notified of lost focus when + // the Metal dialog first opens; subsequently, when focus is regained, the + // focus gain event is not posted to the SwtInputBlocker. + // + // The workaround is to use Windows look and feel, rather than Metal. + // System.out.println("Bringing to front"); + + awtDialog.requestFocus(); + awtDialog.toFront(); + } + } + }); + } + + private void handleOpenedWindow(WindowEvent event) { + assert event != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + Window window = event.getWindow(); + if (window instanceof Dialog) { + handleAddedDialog((Dialog)window); + } + } + + private void handleClosedWindow(WindowEvent event) { + assert event != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // Dispose-based close + Window window = event.getWindow(); + if (window instanceof Dialog) { + // Remove dialog and component listener + handleRemovedDialog((Dialog)window, true); + } + } + + private void handleClosingWindow(WindowEvent event) { + assert event != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // System-based close + Window window = event.getWindow(); + if (window instanceof Dialog) { + final Dialog dialog = (Dialog) window; + // Defer until later. Bad things happen if + // handleRemovedDialog() is called directly from + // this event handler. The Swing dialog does not close + // properly and its modality remains in effect. + EventQueue.invokeLater(new Runnable() { + public void run() { + // Remove dialog and component listener + handleRemovedDialog(dialog, true); + } + }); + } + } + + public void eventDispatched(AWTEvent event) { + assert event != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + switch (event.getID()) { + case WindowEvent.WINDOW_OPENED: + handleOpenedWindow((WindowEvent)event); + break; + + case WindowEvent.WINDOW_CLOSED: + handleClosedWindow((WindowEvent)event); + break; + + case WindowEvent.WINDOW_CLOSING: + handleClosingWindow((WindowEvent)event); + break; + + default: + break; + } + } + + public void componentHidden(ComponentEvent e) { + assert e != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // System.out.println("Component hidden"); + Object obj = e.getSource(); + if (obj instanceof Dialog) { + // Remove dialog but keep listener in place so that we know if/when it is set visible + handleRemovedDialog((Dialog)obj, false); + } + } + + public void componentShown(ComponentEvent e) { + assert e != null; + assert EventQueue.isDispatchThread(); // On AWT event thread + + // System.out.println("Component shown"); + Object obj = e.getSource(); + if (obj instanceof Dialog) { + handleAddedDialog((Dialog)obj); + } + } + + public void componentResized(ComponentEvent e) { + } + + public void componentMoved(ComponentEvent e) { + } + +}