-/*******************************************************************************\r
- * Copyright (c) 2007 SAS Institute.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * SAS Institute - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.utils.ui.internal.awt;\r
-\r
-import java.awt.Component;\r
-import java.awt.Container;\r
-import java.awt.EventQueue;\r
-import java.awt.FocusTraversalPolicy;\r
-import java.awt.Frame;\r
-import java.awt.Window;\r
-import java.awt.event.ContainerEvent;\r
-import java.awt.event.ContainerListener;\r
-import java.awt.event.FocusEvent;\r
-import java.awt.event.FocusListener;\r
-import java.awt.event.WindowEvent;\r
-import java.awt.event.WindowFocusListener;\r
-import java.util.ArrayList;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-\r
-import javax.swing.JPopupMenu;\r
-import javax.swing.text.Caret;\r
-import javax.swing.text.JTextComponent;\r
-\r
-\r
-@SuppressWarnings({"rawtypes", "unchecked"})\r
-public class AwtFocusHandler implements FocusListener, ContainerListener, \r
- WindowFocusListener {\r
-\r
- private Frame frame;\r
- private SwtFocusHandler swtHandler;\r
- private RecursiveContainerListener containerListener;\r
- private boolean awtHasFocus = false;\r
- private Component currentComponent = null;\r
-\r
- public AwtFocusHandler(Frame frame) {\r
- assert frame != null;\r
-\r
- this.frame = frame;\r
- this.containerListener = new RecursiveContainerListener(this);\r
- frame.addContainerListener(this.containerListener);\r
- frame.addWindowFocusListener(this);\r
- }\r
-\r
- public void setSwtHandler(SwtFocusHandler handler) {\r
- assert handler != null;\r
- assert swtHandler == null; // this method is meant to be called once\r
-\r
- swtHandler = handler;\r
- }\r
-\r
- /**\r
- * Invoked from {@link SwtFocusHandler} DisposeListener.\r
- * Only intended to be invoked once.\r
- */\r
- public void dispose() {\r
- assert frame != null;\r
- frame.removeWindowFocusListener(this);\r
- frame.removeContainerListener(containerListener);\r
- frame = null;\r
- currentComponent = null;\r
- }\r
-\r
- void gainFocus() {\r
- // assert frame != null;\r
- // assert !awtHasFocus;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- if (frame == null)\r
- return;\r
-\r
- FocusTraversalPolicy policy = frame.getFocusTraversalPolicy();\r
- Component component;\r
- if (policy instanceof EmbeddedChildFocusTraversalPolicy) {\r
- EmbeddedChildFocusTraversalPolicy embeddedPolicy = (EmbeddedChildFocusTraversalPolicy) policy; \r
- component = embeddedPolicy.getCurrentComponent(frame);\r
- } else {\r
- // TODO: direction based?\r
- component = policy.getDefaultComponent(frame);\r
- }\r
- if (component != null) {\r
- // System.out.println("Requesting focus for component: " + component);\r
- component.requestFocus();\r
- // TODO: else case error? If not, consider moving flag setting below into this if\r
- }\r
- awtHasFocus = true;\r
- }\r
- \r
- /**\r
- * Moves focus back to the next SWT component\r
- */\r
- void transferFocusNext() {\r
- assert swtHandler != null;\r
- assert awtHasFocus;\r
- \r
- awtHasFocus = false;\r
- swtHandler.gainFocusNext();\r
- }\r
- \r
- /**\r
- * Moves focus back to the previous SWT component\r
- */\r
- void transferFocusPrevious() {\r
- assert swtHandler != null;\r
- assert awtHasFocus;\r
- \r
- awtHasFocus = false;\r
- swtHandler.gainFocusPrevious();\r
- }\r
- \r
- boolean awtHasFocus() {\r
- return awtHasFocus;\r
- }\r
-\r
- Component getCurrentComponent() {\r
- return currentComponent;\r
- }\r
- \r
- // ..................... Listener implementations\r
-\r
- public void focusGained(FocusEvent e) {\r
- assert e != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- // System.out.println("gained (awt). component = " + e.getComponent() + ", opposite = " + e.getOppositeComponent());\r
- currentComponent = e.getComponent();\r
- }\r
-\r
- public void focusLost(FocusEvent e) {\r
- // System.out.println("component focus lost (awt). opposite = " + e.getOppositeComponent());\r
- \r
- // Intentionally leaving currentComponent set. When window focus is lost, \r
- // it will be needed. \r
- }\r
-\r
- public void componentAdded(ContainerEvent e) {\r
- assert e != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- e.getChild().addFocusListener(this);\r
- }\r
-\r
- public void componentRemoved(ContainerEvent e) {\r
- assert e != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- e.getChild().removeFocusListener(this);\r
- }\r
- \r
- public void windowGainedFocus(WindowEvent e) {\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- // System.out.println("WindowFocusListener.windowGainedFocus");\r
- awtHasFocus = true;\r
- }\r
-\r
- public void windowLostFocus(WindowEvent e) {\r
- assert e != null;\r
- assert swtHandler != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- // System.out.println("WindowFocusListener.windowLostFocus");\r
- \r
- // Dismiss any popup menus that are\r
- // open when losing focus. This prevents situations where\r
- // multiple popup menus are visible at the same time. In JDK 1.4 and earlier, \r
- // the dismissal is not done automatically. In JDK 1.5, this code is \r
- // unnecessary, but it doesn't seem to hurt anything. \r
- // TODO: verify this is OK on other windowing systems\r
- // TODO: disable in post-1.4 environments\r
- /* boolean popupShown = */hidePopups();\r
- \r
- // If focus is being lost to the parent SWT composite, then\r
- // grab it back for AWT and return. Normally the parent SWT composite will\r
- // do this for us, but it will not see a focus gained event when focus \r
- // is transferred to it from its AWT frame child. \r
- // This happens, for example, if an AWT control has focus and the \r
- // tab of a containing (already active) view is clicked.\r
- //\r
- // However, don't grab back focus if a popup was hidden above. The popup\r
- // area will not be properly redrawn (the popup, or part of it, will \r
- // appear to be still there. \r
- //if (!popupShown && swtHandler.hasFocus()) {\r
- // System.out.println("**** Taking back focus: " + e);\r
- // This seems to have side effects, so it's commented out for now. \r
- // (Sometimes, it forces the workbench window to the foreground when another\r
- // program's window is selected.)\r
- // TODO: find an alternate approach to reassert focus\r
- // gainFocus();\r
- // return;\r
- //}\r
- \r
- // On a normal change of focus, Swing will turn off any selection\r
- // in a text field to help indicate focus is lost. This won't happen\r
- // automatically when transferring to SWT, so turn off the selection\r
- // manually.\r
- if (currentComponent instanceof JTextComponent) {\r
- Caret caret = ((JTextComponent)currentComponent).getCaret();\r
- if (caret != null) {\r
- caret.setSelectionVisible(false);\r
- }\r
- }\r
- awtHasFocus = false;\r
- }\r
-\r
- // Returns true if any popup has been hidden\r
- private boolean hidePopups() {\r
- boolean result = false;\r
- List popups = new ArrayList();\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- Window frame = this.frame;\r
- if (frame == null)\r
- return result;\r
-\r
- // Look for popups inside the frame's component hierarchy. \r
- // Lightweight popups will be found here. \r
- findContainedPopups(frame, popups);\r
- \r
- // Also look for popups in the frame's window hierachy. \r
- // Heavyweight popups will be found here.\r
- findOwnedPopups(frame, popups);\r
- \r
- // System.out.println("Hiding popups, count=" + popups.size());\r
- for (Iterator iter = popups.iterator(); iter.hasNext();) {\r
- Component popup = (Component)iter.next();\r
- if (popup.isVisible()) {\r
- result = true;\r
- popup.setVisible(false);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- private void findOwnedPopups(Window window, List popups) {\r
- assert window != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- Window[] ownedWindows = window.getOwnedWindows();\r
- for (int i = 0; i < ownedWindows.length; i++) {\r
- findContainedPopups(ownedWindows[i], popups);\r
- findOwnedPopups(ownedWindows[i], popups);\r
- }\r
- }\r
-\r
- private void findContainedPopups(Container container, List popups) {\r
- assert container != null;\r
- assert popups != null;\r
- assert EventQueue.isDispatchThread(); // On AWT event thread\r
- \r
- Component[] components = container.getComponents();\r
- for (int i = 0; i < components.length; i++) {\r
- Component c = components[i];\r
- // JPopupMenu is a container, so check for it first\r
- if (c instanceof JPopupMenu) {\r
- popups.add(c);\r
- } else if (c instanceof Container) {\r
- findContainedPopups((Container)c, popups);\r
- }\r
- }\r
- }\r
-\r
- public void postHidePopups() {\r
- EventQueue.invokeLater(new Runnable() {\r
- public void run() {\r
- hidePopups();\r
- }\r
- });\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * 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.Component;
+import java.awt.Container;
+import java.awt.EventQueue;
+import java.awt.FocusTraversalPolicy;
+import java.awt.Frame;
+import java.awt.Window;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JPopupMenu;
+import javax.swing.text.Caret;
+import javax.swing.text.JTextComponent;
+
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class AwtFocusHandler implements FocusListener, ContainerListener,
+ WindowFocusListener {
+
+ private Frame frame;
+ private SwtFocusHandler swtHandler;
+ private RecursiveContainerListener containerListener;
+ private boolean awtHasFocus = false;
+ private Component currentComponent = null;
+
+ public AwtFocusHandler(Frame frame) {
+ assert frame != null;
+
+ this.frame = frame;
+ this.containerListener = new RecursiveContainerListener(this);
+ frame.addContainerListener(this.containerListener);
+ frame.addWindowFocusListener(this);
+ }
+
+ public void setSwtHandler(SwtFocusHandler handler) {
+ assert handler != null;
+ assert swtHandler == null; // this method is meant to be called once
+
+ swtHandler = handler;
+ }
+
+ /**
+ * Invoked from {@link SwtFocusHandler} DisposeListener.
+ * Only intended to be invoked once.
+ */
+ public void dispose() {
+ assert frame != null;
+ frame.removeWindowFocusListener(this);
+ frame.removeContainerListener(containerListener);
+ frame = null;
+ currentComponent = null;
+ }
+
+ void gainFocus() {
+ // assert frame != null;
+ // assert !awtHasFocus;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+ if (frame == null)
+ return;
+
+ FocusTraversalPolicy policy = frame.getFocusTraversalPolicy();
+ Component component;
+ if (policy instanceof EmbeddedChildFocusTraversalPolicy) {
+ EmbeddedChildFocusTraversalPolicy embeddedPolicy = (EmbeddedChildFocusTraversalPolicy) policy;
+ component = embeddedPolicy.getCurrentComponent(frame);
+ } else {
+ // TODO: direction based?
+ component = policy.getDefaultComponent(frame);
+ }
+ if (component != null) {
+ // System.out.println("Requesting focus for component: " + component);
+ component.requestFocus();
+ // TODO: else case error? If not, consider moving flag setting below into this if
+ }
+ awtHasFocus = true;
+ }
+
+ /**
+ * Moves focus back to the next SWT component
+ */
+ void transferFocusNext() {
+ assert swtHandler != null;
+ assert awtHasFocus;
+
+ awtHasFocus = false;
+ swtHandler.gainFocusNext();
+ }
+
+ /**
+ * Moves focus back to the previous SWT component
+ */
+ void transferFocusPrevious() {
+ assert swtHandler != null;
+ assert awtHasFocus;
+
+ awtHasFocus = false;
+ swtHandler.gainFocusPrevious();
+ }
+
+ boolean awtHasFocus() {
+ return awtHasFocus;
+ }
+
+ Component getCurrentComponent() {
+ return currentComponent;
+ }
+
+ // ..................... Listener implementations
+
+ public void focusGained(FocusEvent e) {
+ assert e != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ // System.out.println("gained (awt). component = " + e.getComponent() + ", opposite = " + e.getOppositeComponent());
+ currentComponent = e.getComponent();
+ }
+
+ public void focusLost(FocusEvent e) {
+ // System.out.println("component focus lost (awt). opposite = " + e.getOppositeComponent());
+
+ // Intentionally leaving currentComponent set. When window focus is lost,
+ // it will be needed.
+ }
+
+ public void componentAdded(ContainerEvent e) {
+ assert e != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ e.getChild().addFocusListener(this);
+ }
+
+ public void componentRemoved(ContainerEvent e) {
+ assert e != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ e.getChild().removeFocusListener(this);
+ }
+
+ public void windowGainedFocus(WindowEvent e) {
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+ // System.out.println("WindowFocusListener.windowGainedFocus");
+ awtHasFocus = true;
+ }
+
+ public void windowLostFocus(WindowEvent e) {
+ assert e != null;
+ assert swtHandler != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ // System.out.println("WindowFocusListener.windowLostFocus");
+
+ // Dismiss any popup menus that are
+ // open when losing focus. This prevents situations where
+ // multiple popup menus are visible at the same time. In JDK 1.4 and earlier,
+ // the dismissal is not done automatically. In JDK 1.5, this code is
+ // unnecessary, but it doesn't seem to hurt anything.
+ // TODO: verify this is OK on other windowing systems
+ // TODO: disable in post-1.4 environments
+ /* boolean popupShown = */hidePopups();
+
+ // If focus is being lost to the parent SWT composite, then
+ // grab it back for AWT and return. Normally the parent SWT composite will
+ // do this for us, but it will not see a focus gained event when focus
+ // is transferred to it from its AWT frame child.
+ // This happens, for example, if an AWT control has focus and the
+ // tab of a containing (already active) view is clicked.
+ //
+ // However, don't grab back focus if a popup was hidden above. The popup
+ // area will not be properly redrawn (the popup, or part of it, will
+ // appear to be still there.
+ //if (!popupShown && swtHandler.hasFocus()) {
+ // System.out.println("**** Taking back focus: " + e);
+ // This seems to have side effects, so it's commented out for now.
+ // (Sometimes, it forces the workbench window to the foreground when another
+ // program's window is selected.)
+ // TODO: find an alternate approach to reassert focus
+ // gainFocus();
+ // return;
+ //}
+
+ // On a normal change of focus, Swing will turn off any selection
+ // in a text field to help indicate focus is lost. This won't happen
+ // automatically when transferring to SWT, so turn off the selection
+ // manually.
+ if (currentComponent instanceof JTextComponent) {
+ Caret caret = ((JTextComponent)currentComponent).getCaret();
+ if (caret != null) {
+ caret.setSelectionVisible(false);
+ }
+ }
+ awtHasFocus = false;
+ }
+
+ // Returns true if any popup has been hidden
+ private boolean hidePopups() {
+ boolean result = false;
+ List popups = new ArrayList();
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+ Window frame = this.frame;
+ if (frame == null)
+ return result;
+
+ // Look for popups inside the frame's component hierarchy.
+ // Lightweight popups will be found here.
+ findContainedPopups(frame, popups);
+
+ // Also look for popups in the frame's window hierachy.
+ // Heavyweight popups will be found here.
+ findOwnedPopups(frame, popups);
+
+ // System.out.println("Hiding popups, count=" + popups.size());
+ for (Iterator iter = popups.iterator(); iter.hasNext();) {
+ Component popup = (Component)iter.next();
+ if (popup.isVisible()) {
+ result = true;
+ popup.setVisible(false);
+ }
+ }
+ return result;
+ }
+
+ private void findOwnedPopups(Window window, List popups) {
+ assert window != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ Window[] ownedWindows = window.getOwnedWindows();
+ for (int i = 0; i < ownedWindows.length; i++) {
+ findContainedPopups(ownedWindows[i], popups);
+ findOwnedPopups(ownedWindows[i], popups);
+ }
+ }
+
+ private void findContainedPopups(Container container, List popups) {
+ assert container != null;
+ assert popups != null;
+ assert EventQueue.isDispatchThread(); // On AWT event thread
+
+ Component[] components = container.getComponents();
+ for (int i = 0; i < components.length; i++) {
+ Component c = components[i];
+ // JPopupMenu is a container, so check for it first
+ if (c instanceof JPopupMenu) {
+ popups.add(c);
+ } else if (c instanceof Container) {
+ findContainedPopups((Container)c, popups);
+ }
+ }
+ }
+
+ public void postHidePopups() {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ hidePopups();
+ }
+ });
+ }
+
+}