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;h=d7ab60c874459297c8ae83000056a039ff75abe0;hp=23cf6673bc2323515c17bc857efa2b7baf5bec08;hb=refs%2Fchanges%2F38%2F238%2F2;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 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 index 23cf6673b..d7ab60c87 100644 --- 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 @@ -1,225 +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) { - } - -} +/******************************************************************************* + * 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) { + } + +}