1 /* 2 * Created on Mar 18, 2005 3 */ 4 package org.flexdock.docking.activation; 5 6 import java.awt.AWTEvent; 7 import java.awt.Component; 8 import java.awt.Container; 9 import java.awt.EventQueue; 10 import java.awt.KeyboardFocusManager; 11 import java.awt.Point; 12 import java.awt.Toolkit; 13 import java.awt.event.AWTEventListener; 14 import java.awt.event.MouseEvent; 15 import java.beans.PropertyChangeEvent; 16 import java.beans.PropertyChangeListener; 17 import java.util.HashSet; 18 19 import javax.swing.JTabbedPane; 20 import javax.swing.SwingUtilities; 21 import javax.swing.event.ChangeEvent; 22 import javax.swing.event.ChangeListener; 23 24 import org.flexdock.docking.Dockable; 25 import org.flexdock.docking.DockingConstants; 26 import org.flexdock.docking.DockingManager; 27 import org.flexdock.util.DockingUtility; 28 import org.flexdock.util.SwingUtility; 29 30 /** 31 * @author Christopher Butler 32 */ 33 public class ActiveDockableListener implements DockingConstants, PropertyChangeListener, ChangeListener, AWTEventListener { 34 private static final ActiveDockableListener SINGLETON = new ActiveDockableListener(); 35 private static HashSet PROP_EVENTS = new HashSet(); 36 37 static { primeImpl()38 primeImpl(); 39 } 40 prime()41 public static void prime() { 42 } 43 primeImpl()44 private static void primeImpl() { 45 PROP_EVENTS.add(PERMANENT_FOCUS_OWNER); 46 PROP_EVENTS.add(ACTIVE_WINDOW); 47 48 EventQueue.invokeLater(new Runnable() { 49 public void run() { 50 KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 51 focusManager.addPropertyChangeListener(SINGLETON); 52 } 53 }); 54 55 Toolkit.getDefaultToolkit().addAWTEventListener(SINGLETON, AWTEvent.MOUSE_EVENT_MASK); 56 } 57 getInstance()58 public static ActiveDockableListener getInstance() { 59 return SINGLETON; 60 } 61 ActiveDockableListener()62 private ActiveDockableListener() { 63 } 64 eventDispatched(AWTEvent event)65 public void eventDispatched(AWTEvent event) { 66 //catch all mousePressed events 67 if(event.getID()!=MouseEvent.MOUSE_PRESSED) 68 return; 69 70 MouseEvent evt = (MouseEvent)event; 71 72 if (evt.getSource() instanceof Component) { 73 Component c = (Component) evt.getSource(); 74 75 // check to see if the event was targeted at the deepest component at the current 76 // mouse loaction 77 Container container = c instanceof Container? (Container)c: null; 78 if(container!=null && container.getComponentCount()>1) { 79 // if not, find the deepest component 80 Point p = evt.getPoint(); 81 c = SwingUtilities.getDeepestComponentAt(c, p.x, p.y); 82 } 83 84 // request activation of the dockable that encloses this component 85 ActiveDockableTracker.requestDockableActivation(c); 86 } 87 } 88 propertyChange(PropertyChangeEvent evt)89 public void propertyChange(PropertyChangeEvent evt) { 90 String pName = evt.getPropertyName(); 91 if(!PROP_EVENTS.contains(pName)) 92 return; 93 94 Component oldVal = SwingUtility.toComponent(evt.getOldValue()); 95 Component newVal = SwingUtility.toComponent(evt.getNewValue()); 96 boolean switchTo = newVal!=null; 97 98 if(ACTIVE_WINDOW.equals(pName)) 99 handleWindowChange(evt, oldVal, newVal, switchTo); 100 else 101 handleFocusChange(evt, oldVal, newVal, switchTo); 102 } 103 handleWindowChange(PropertyChangeEvent evt, Component oldVal, Component newVal, boolean activate)104 private void handleWindowChange(PropertyChangeEvent evt, Component oldVal, Component newVal, boolean activate) { 105 // notify the ActiveDockableTracker of the window change 106 ActiveDockableTracker.windowActivated(newVal); 107 108 Component srcComponent = activate? newVal: oldVal; 109 ActiveDockableTracker tracker = ActiveDockableTracker.getTracker(srcComponent); 110 if(tracker!=null) 111 tracker.setActive(activate); 112 } 113 handleFocusChange(PropertyChangeEvent evt, Component oldVal, Component newVal, boolean switchTo)114 private void handleFocusChange(PropertyChangeEvent evt, Component oldVal, Component newVal, boolean switchTo) { 115 if(!switchTo) 116 return; 117 118 if(newVal instanceof JTabbedPane) 119 newVal = ((JTabbedPane)newVal).getSelectedComponent(); 120 activateComponent(newVal); 121 } 122 activateComponent(Component c)123 private void activateComponent(Component c) { 124 Dockable dockable = DockingUtility.getAncestorDockable(c); 125 if(dockable==null) 126 return; 127 128 ActiveDockableTracker tracker = ActiveDockableTracker.getTracker(dockable.getComponent()); 129 if(tracker!=null) { 130 tracker.setActive(dockable); 131 } 132 } 133 134 stateChanged(ChangeEvent e)135 public void stateChanged(ChangeEvent e) { 136 Object obj = e.getSource(); 137 if(obj instanceof JTabbedPane) { 138 JTabbedPane pane = (JTabbedPane)obj; 139 Component c = pane.getSelectedComponent(); 140 Dockable dockable = DockingManager.getDockable(c); 141 if(dockable!=null) { 142 activateComponent(dockable.getComponent()); 143 udpateTabChangeFocus(dockable); 144 } 145 } 146 } 147 udpateTabChangeFocus(final Dockable dockable)148 private void udpateTabChangeFocus(final Dockable dockable) { 149 KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 150 Dockable focusParent = DockingUtility.getAncestorDockable(mgr.getFocusOwner()); 151 if(focusParent==null || focusParent==dockable) 152 return; 153 154 // the current focusParent-dockable is different than the currently active dockable. 155 // we'll need to update the focus component 156 final Component comp = dockable.getComponent(); 157 final Component deep = SwingUtilities.getDeepestComponentAt(comp, comp.getWidth()/2, comp.getHeight()/2); 158 // invokeLater because the new tab may not yet be showing, meaning the enumeration of its 159 // focus-cycle will return empty. the parent dockable in the new tab must be showing. 160 EventQueue.invokeLater(new Runnable() { 161 public void run() { 162 ActiveDockableTracker.focusDockable(deep, dockable, true); 163 } 164 }); 165 166 } 167 168 } 169