2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.openapi
.wm
.impl
;
18 import com
.intellij
.ide
.DataManager
;
19 import com
.intellij
.openapi
.actionSystem
.DataContext
;
20 import com
.intellij
.openapi
.actionSystem
.PlatformDataKeys
;
21 import com
.intellij
.openapi
.application
.ApplicationManager
;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.wm
.FocusWatcher
;
25 import com
.intellij
.openapi
.wm
.ex
.WindowManagerEx
;
26 import com
.intellij
.util
.containers
.HashMap
;
27 import org
.jetbrains
.annotations
.NonNls
;
28 import org
.jetbrains
.annotations
.NotNull
;
29 import org
.jetbrains
.annotations
.Nullable
;
33 import java
.awt
.event
.ComponentEvent
;
34 import java
.awt
.event
.WindowEvent
;
35 import java
.beans
.PropertyChangeEvent
;
36 import java
.beans
.PropertyChangeListener
;
37 import java
.lang
.ref
.WeakReference
;
38 import java
.util
.HashSet
;
39 import java
.util
.Iterator
;
42 * @author Anton Katilin
43 * @author Vladimir Kondratyev
45 public final class WindowWatcher
implements PropertyChangeListener
{
46 private static final Logger LOG
=Logger
.getInstance("#com.intellij.openapi.wm.impl.WindowWatcher");
47 private final Object myLock
;
48 private final HashMap
<Window
, WindowInfo
> myWindow2Info
;
50 * Currenly focused window (window which has focused component). Can be <code>null</code> if there is no focused
53 private Window myFocusedWindow
;
55 * Contains last focused window for each project.
57 private final HashSet myFocusedWindows
;
58 @NonNls protected static final String FOCUSED_WINDOW_PROPERTY
= "focusedWindow";
62 myWindow2Info
=new HashMap
<Window
, WindowInfo
>();
63 myFocusedWindows
=new HashSet();
67 * This method should get notifications abount changes of focused window.
68 * Only <code>focusedWindow</code> property is acceptable.
69 * @throws IllegalArgumentException if property name isn't <code>focusedWindow</code>.
71 public final void propertyChange(final PropertyChangeEvent e
){
72 if(LOG
.isDebugEnabled()){
73 LOG
.debug("enter: propertyChange("+e
+")");
75 if(!FOCUSED_WINDOW_PROPERTY
.equals(e
.getPropertyName())){
76 throw new IllegalArgumentException("unknown property name: "+e
.getPropertyName());
79 final Window window
=(Window
)e
.getNewValue();
80 if(window
==null || ApplicationManager
.getApplication().isDisposed()){
83 if(!myWindow2Info
.containsKey(window
)){
84 myWindow2Info
.put(window
,new WindowInfo(window
, true));
86 myFocusedWindow
=window
;
87 final Project project
= PlatformDataKeys
.PROJECT
.getData(DataManager
.getInstance().getDataContext(myFocusedWindow
));
88 for (Iterator i
= myFocusedWindows
.iterator(); i
.hasNext();) {
89 final Window w
= (Window
)i
.next();
90 final DataContext dataContext
= DataManager
.getInstance().getDataContext(w
);
91 if (project
== PlatformDataKeys
.PROJECT
.getData(dataContext
)) {
95 myFocusedWindows
.add(myFocusedWindow
);
97 final IdeFrameImpl frame
;
98 if(window
instanceof IdeFrameImpl
){
99 frame
=(IdeFrameImpl
)window
;
101 frame
=(IdeFrameImpl
)SwingUtilities
.getAncestorOfClass(IdeFrameImpl
.class,window
);
104 JOptionPane
.setRootFrame(frame
);
107 if(LOG
.isDebugEnabled()){
108 LOG
.debug("exit: propertyChange()");
112 final void dispatchComponentEvent(final ComponentEvent e
){
113 final int id
=e
.getID();
114 if(WindowEvent
.WINDOW_CLOSED
==id
||(ComponentEvent
.COMPONENT_HIDDEN
==id
&&e
.getSource() instanceof Window
)){
115 dispatchHiddenOrClosed((Window
)e
.getSource());
117 // Clear obsolete reference on root frame
118 if(WindowEvent
.WINDOW_CLOSED
==id
){
119 final Window window
=(Window
)e
.getSource();
120 if(JOptionPane
.getRootFrame()==window
){
121 JOptionPane
.setRootFrame(null);
126 private void dispatchHiddenOrClosed(final Window window
){
127 if(LOG
.isDebugEnabled()){
128 LOG
.debug("enter: dispatchClosed("+window
+")");
130 synchronized(myLock
){
131 final WindowInfo info
=myWindow2Info
.get(window
);
133 final FocusWatcher focusWatcher
=info
.myFocusWatcherRef
.get();
134 if(focusWatcher
!=null){
135 focusWatcher
.deinstall(window
);
137 myWindow2Info
.remove(window
);
140 // Now, we have to recalculate focused window if currently focused
141 // window is being closed.
142 if(myFocusedWindow
==window
){
143 if(LOG
.isDebugEnabled()){
144 LOG
.debug("currently active window should be closed");
146 myFocusedWindow
=myFocusedWindow
.getOwner();
147 if (LOG
.isDebugEnabled()) {
148 LOG
.debug("new active window is "+myFocusedWindow
);
151 for(Iterator i
=myFocusedWindows
.iterator();i
.hasNext();){
152 final Window activeWindow
= (Window
)i
.next();
153 if (activeWindow
== window
) {
154 final Window newActiveWindow
= activeWindow
.getOwner();
156 if (newActiveWindow
!= null) {
157 myFocusedWindows
.add(newActiveWindow
);
162 // Remove invalid infos for garbage collected windows
163 for(Iterator i
=myWindow2Info
.values().iterator();i
.hasNext();){
164 final WindowInfo info
=(WindowInfo
)i
.next();
165 if(info
.myFocusWatcherRef
.get()==null){
166 if (LOG
.isDebugEnabled()) {
167 LOG
.debug("remove collected info");
174 public final Window
getFocusedWindow(){
175 synchronized(myLock
){
176 return myFocusedWindow
;
180 public final Component
getFocusedComponent(final Project project
){
181 synchronized(myLock
){
182 final Window window
=getFocusedWindowForProject(project
);
186 return getFocusedComponent(window
);
191 public final Component
getFocusedComponent(@NotNull final Window window
){
192 synchronized(myLock
){
193 final WindowInfo info
=myWindow2Info
.get(window
);
194 if(info
==null){ // it means that we don't manage this window, so just return standard focus owner
195 // return window.getFocusOwner();
196 // TODO[vova,anton] usage of getMostRecentFocusOwner is experimental. But it seems suitable here.
197 return window
.getMostRecentFocusOwner();
199 final FocusWatcher focusWatcher
=info
.myFocusWatcherRef
.get();
200 if(focusWatcher
!=null){
201 final Component focusedComponent
= focusWatcher
.getFocusedComponent();
202 if(focusedComponent
!= null && focusedComponent
.isShowing()){
203 return focusedComponent
;
209 // info isn't valid, i.e. window was garbage collected, so we need the remove invalid info
211 myWindow2Info
.remove(window
);
218 public FocusWatcher
getFocusWatcherFor(Component c
) {
219 final Window window
= SwingUtilities
.getWindowAncestor(c
);
220 final WindowInfo info
= myWindow2Info
.get(window
);
221 return info
== null ?
null : info
.myFocusWatcherRef
.get();
225 * @param project may be null (for example, if no projects are opened)
227 public final Window
suggestParentWindow(final Project project
){
228 synchronized(myLock
){
229 Window window
=getFocusedWindowForProject(project
);
231 if (project
!= null) {
232 return WindowManagerEx
.getInstanceEx().getFrame(project
);
239 LOG
.assertTrue(window
.isDisplayable());
240 LOG
.assertTrue(window
.isShowing());
243 // skip all windows until found forst dialog or frame
244 if(!(window
instanceof Dialog
)&&!(window
instanceof Frame
)){
245 window
=window
.getOwner();
248 // skip not visible and disposed/not shown windows
249 if(!window
.isDisplayable()||!window
.isShowing()){
250 window
= window
.getOwner();
253 // skip windows that have not associated WindowInfo
254 final WindowInfo info
=myWindow2Info
.get(window
);
256 window
=window
.getOwner();
259 if(info
.mySuggestAsParent
){
262 window
=window
.getOwner();
269 public final void doNotSuggestAsParent(final Window window
){
270 if(LOG
.isDebugEnabled()){
271 LOG
.debug("enter: doNotSuggestAsParent("+window
+")");
273 synchronized(myLock
){
274 final WindowInfo info
=myWindow2Info
.get(window
);
276 myWindow2Info
.put(window
,new WindowInfo(window
, false));
278 info
.mySuggestAsParent
=false;
284 * @return active window for specified <code>project</code>. There is only one window
285 * for project can be at any point of time.
287 private Window
getFocusedWindowForProject(final Project project
){
288 //todo[anton,vova]: it is possible that returned wnd is not contained in myFocusedWindows; investigate
289 outer
: for(Iterator i
=myFocusedWindows
.iterator();i
.hasNext();){
290 Window window
=(Window
)i
.next();
291 while(!window
.isDisplayable()||!window
.isShowing()){ // if window isn't visible then gets its first visible ancestor
292 window
=window
.getOwner();
297 final DataContext dataContext
= DataManager
.getInstance().getDataContext(window
);
298 if (project
== PlatformDataKeys
.PROJECT
.getData(dataContext
)) {
305 private static final class WindowInfo
{
306 public final WeakReference
<FocusWatcher
> myFocusWatcherRef
;
307 public boolean mySuggestAsParent
;
309 public WindowInfo(final Window window
,final boolean suggestAsParent
){
310 final FocusWatcher focusWatcher
=new FocusWatcher();
311 focusWatcher
.install(window
);
312 myFocusWatcherRef
=new WeakReference
<FocusWatcher
>(focusWatcher
);
313 mySuggestAsParent
=suggestAsParent
;