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