1/*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing;
27
28
29import javax.swing.event.*;
30import java.awt.event.*;
31
32import java.awt.Component;
33import java.awt.Container;
34import java.awt.Window;
35import java.beans.PropertyChangeListener;
36import java.beans.PropertyChangeEvent;
37
38import java.io.Serializable;
39
40
41/**
42 * @author Dave Moore
43 */
44
45@SuppressWarnings("serial")
46class AncestorNotifier implements ComponentListener, PropertyChangeListener, Serializable
47{
48    transient Component firstInvisibleAncestor;
49    EventListenerList listenerList = new EventListenerList();
50    JComponent root;
51
52    AncestorNotifier(JComponent root) {
53        this.root = root;
54        addListeners(root, true);
55    }
56
57    void addAncestorListener(AncestorListener l) {
58        listenerList.add(AncestorListener.class, l);
59    }
60
61    void removeAncestorListener(AncestorListener l) {
62        listenerList.remove(AncestorListener.class, l);
63    }
64
65    AncestorListener[] getAncestorListeners() {
66        return listenerList.getListeners(AncestorListener.class);
67    }
68
69    /**
70     * Notify all listeners that have registered interest for
71     * notification on this event type.  The event instance
72     * is lazily created using the parameters passed into
73     * the fire method.
74     * @see EventListenerList
75     */
76    protected void fireAncestorAdded(JComponent source, int id, Container ancestor, Container ancestorParent) {
77        // Guaranteed to return a non-null array
78        Object[] listeners = listenerList.getListenerList();
79        // Process the listeners last to first, notifying
80        // those that are interested in this event
81        for (int i = listeners.length-2; i>=0; i-=2) {
82            if (listeners[i]==AncestorListener.class) {
83                // Lazily create the event:
84                AncestorEvent ancestorEvent =
85                    new AncestorEvent(source, id, ancestor, ancestorParent);
86                ((AncestorListener)listeners[i+1]).ancestorAdded(ancestorEvent);
87            }
88        }
89    }
90
91    /**
92     * Notify all listeners that have registered interest for
93     * notification on this event type.  The event instance
94     * is lazily created using the parameters passed into
95     * the fire method.
96     * @see EventListenerList
97     */
98    protected void fireAncestorRemoved(JComponent source, int id, Container ancestor, Container ancestorParent) {
99        // Guaranteed to return a non-null array
100        Object[] listeners = listenerList.getListenerList();
101        // Process the listeners last to first, notifying
102        // those that are interested in this event
103        for (int i = listeners.length-2; i>=0; i-=2) {
104            if (listeners[i]==AncestorListener.class) {
105                // Lazily create the event:
106                AncestorEvent ancestorEvent =
107                    new AncestorEvent(source, id, ancestor, ancestorParent);
108                ((AncestorListener)listeners[i+1]).ancestorRemoved(ancestorEvent);
109            }
110        }
111    }
112    /**
113     * Notify all listeners that have registered interest for
114     * notification on this event type.  The event instance
115     * is lazily created using the parameters passed into
116     * the fire method.
117     * @see EventListenerList
118     */
119    protected void fireAncestorMoved(JComponent source, int id, Container ancestor, Container ancestorParent) {
120        // Guaranteed to return a non-null array
121        Object[] listeners = listenerList.getListenerList();
122        // Process the listeners last to first, notifying
123        // those that are interested in this event
124        for (int i = listeners.length-2; i>=0; i-=2) {
125            if (listeners[i]==AncestorListener.class) {
126                // Lazily create the event:
127                AncestorEvent ancestorEvent =
128                    new AncestorEvent(source, id, ancestor, ancestorParent);
129                ((AncestorListener)listeners[i+1]).ancestorMoved(ancestorEvent);
130            }
131        }
132    }
133
134    void removeAllListeners() {
135        removeListeners(root);
136    }
137
138    void addListeners(Component ancestor, boolean addToFirst) {
139        Component a;
140
141        firstInvisibleAncestor = null;
142        for (a = ancestor;
143             firstInvisibleAncestor == null;
144             a = a.getParent()) {
145            if (addToFirst || a != ancestor) {
146                a.addComponentListener(this);
147
148                if (a instanceof JComponent) {
149                    JComponent jAncestor = (JComponent)a;
150
151                    jAncestor.addPropertyChangeListener(this);
152                }
153            }
154            if (!a.isVisible() || a.getParent() == null || a instanceof Window) {
155                firstInvisibleAncestor = a;
156            }
157        }
158        if (firstInvisibleAncestor instanceof Window &&
159            firstInvisibleAncestor.isVisible()) {
160            firstInvisibleAncestor = null;
161        }
162    }
163
164    void removeListeners(Component ancestor) {
165        Component a;
166        for (a = ancestor; a != null; a = a.getParent()) {
167            a.removeComponentListener(this);
168            if (a instanceof JComponent) {
169                JComponent jAncestor = (JComponent)a;
170                jAncestor.removePropertyChangeListener(this);
171            }
172            if (a == firstInvisibleAncestor || a instanceof Window) {
173                break;
174            }
175        }
176    }
177
178    public void componentResized(ComponentEvent e) {}
179
180    public void componentMoved(ComponentEvent e) {
181        Component source = e.getComponent();
182
183        fireAncestorMoved(root, AncestorEvent.ANCESTOR_MOVED,
184                          (Container)source, source.getParent());
185    }
186
187    public void componentShown(ComponentEvent e) {
188        Component ancestor = e.getComponent();
189
190        if (ancestor == firstInvisibleAncestor) {
191            addListeners(ancestor, false);
192            if (firstInvisibleAncestor == null) {
193                fireAncestorAdded(root, AncestorEvent.ANCESTOR_ADDED,
194                                  (Container)ancestor, ancestor.getParent());
195            }
196        }
197    }
198
199    public void componentHidden(ComponentEvent e) {
200        Component ancestor = e.getComponent();
201        boolean needsNotify = firstInvisibleAncestor == null;
202
203        if ( !(ancestor instanceof Window) ) {
204            removeListeners(ancestor.getParent());
205        }
206        firstInvisibleAncestor = ancestor;
207        if (needsNotify) {
208            fireAncestorRemoved(root, AncestorEvent.ANCESTOR_REMOVED,
209                                (Container)ancestor, ancestor.getParent());
210        }
211    }
212
213    public void propertyChange(PropertyChangeEvent evt) {
214        String s = evt.getPropertyName();
215
216        if (s!=null && (s.equals("parent") || s.equals("ancestor"))) {
217            JComponent component = (JComponent)evt.getSource();
218
219            if (evt.getNewValue() != null) {
220                if (component == firstInvisibleAncestor) {
221                    addListeners(component, false);
222                    if (firstInvisibleAncestor == null) {
223                        fireAncestorAdded(root, AncestorEvent.ANCESTOR_ADDED,
224                                          component, component.getParent());
225                    }
226                }
227            } else {
228                boolean needsNotify = firstInvisibleAncestor == null;
229                Container oldParent = (Container)evt.getOldValue();
230
231                removeListeners(oldParent);
232                firstInvisibleAncestor = component;
233                if (needsNotify) {
234                    fireAncestorRemoved(root, AncestorEvent.ANCESTOR_REMOVED,
235                                        component, oldParent);
236                }
237            }
238        }
239    }
240}
241