45af7f8e8b459ced882bfb37c429b401f94ea77c
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / lookup / impl / LookupManagerImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.intellij.codeInsight.lookup.impl;
18
19 import com.intellij.codeInsight.CodeInsightSettings;
20 import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
21 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
22 import com.intellij.codeInsight.documentation.DocumentationManager;
23 import com.intellij.codeInsight.hint.EditorHintListener;
24 import com.intellij.codeInsight.hint.HintManager;
25 import com.intellij.codeInsight.lookup.*;
26 import com.intellij.openapi.Disposable;
27 import com.intellij.openapi.components.ProjectComponent;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.editor.EditorFactory;
30 import com.intellij.openapi.editor.event.EditorFactoryAdapter;
31 import com.intellij.openapi.editor.event.EditorFactoryEvent;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.Disposer;
34 import com.intellij.psi.PsiDocumentManager;
35 import com.intellij.psi.PsiFile;
36 import com.intellij.ui.LightweightHint;
37 import com.intellij.util.Alarm;
38 import com.intellij.util.messages.MessageBus;
39 import org.jetbrains.annotations.NotNull;
40
41 import java.beans.PropertyChangeListener;
42 import java.beans.PropertyChangeSupport;
43
44 public class LookupManagerImpl extends LookupManager implements ProjectComponent {
45   private final Project myProject;
46
47   protected LookupImpl myActiveLookup = null;
48   protected Editor myActiveLookupEditor = null;
49   private final PropertyChangeSupport myPropertyChangeSupport = new PropertyChangeSupport(this);
50
51   private boolean myIsDisposed;
52
53   public LookupManagerImpl(Project project, MessageBus bus) {
54     myProject = project;
55
56     bus.connect().subscribe(EditorHintListener.TOPIC, new EditorHintListener() {
57       public void hintShown(final Project project, final LightweightHint hint, final int flags) {
58         if (project == myProject) {
59           Lookup lookup = getActiveLookup();
60           if (lookup != null && (flags & HintManager.HIDE_BY_LOOKUP_ITEM_CHANGE) != 0) {
61             lookup.addLookupListener(new LookupAdapter() {
62               public void currentItemChanged(LookupEvent event) {
63                 hint.hide();
64               }
65
66               public void itemSelected(LookupEvent event) {
67                 hint.hide();
68               }
69
70               public void lookupCanceled(LookupEvent event) {
71                 hint.hide();
72               }
73             });
74           }
75         }
76       }
77     });
78
79   }
80
81   @NotNull
82   public String getComponentName() {
83     return "LookupManager";
84   }
85
86   public void initComponent() {
87   }
88
89   public void disposeComponent() {
90   }
91
92   public void projectOpened() {
93
94     final EditorFactoryAdapter myEditorFactoryListener = new EditorFactoryAdapter() {
95       public void editorReleased(EditorFactoryEvent event) {
96         if (event.getEditor() == myActiveLookupEditor) {
97           hideActiveLookup();
98         }
99       }
100     };
101     EditorFactory.getInstance().addEditorFactoryListener(myEditorFactoryListener);
102     Disposer.register(myProject, new Disposable() {
103       public void dispose() {
104         EditorFactory.getInstance().removeEditorFactoryListener(myEditorFactoryListener);
105       }
106     });
107   }
108
109   public void projectClosed() {
110     myIsDisposed = true;
111   }
112
113   public Lookup showLookup(final Editor editor,
114                            @NotNull LookupElement[] items,
115                            final String prefix,
116                            @NotNull final LookupArranger arranger) {
117     final LookupImpl lookup = createLookup(editor, items, prefix, arranger);
118     lookup.show();
119     return lookup;
120   }
121
122   public LookupImpl createLookup(final Editor editor,
123                                  @NotNull LookupElement[] items,
124                                  final String prefix,
125                                  @NotNull final LookupArranger arranger) {
126     hideActiveLookup();
127
128     final CodeInsightSettings settings = CodeInsightSettings.getInstance();
129
130     final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
131
132     final Alarm alarm = new Alarm();
133     final Runnable request = new Runnable() {
134       public void run() {
135         DocumentationManager.getInstance(myProject).showJavaDocInfo(editor, psiFile, false);
136       }
137     };
138     if (settings.AUTO_POPUP_JAVADOC_INFO) {
139       alarm.addRequest(request, settings.JAVADOC_INFO_DELAY);
140     }
141
142     final DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
143     if (daemonCodeAnalyzer != null) {
144       daemonCodeAnalyzer.setUpdateByTimerEnabled(false);
145     }
146     myActiveLookup = new LookupImpl(myProject, editor, arranger);
147     myActiveLookupEditor = editor;
148     myActiveLookup.addLookupListener(new LookupAdapter() {
149       public void itemSelected(LookupEvent event) {
150         dispose();
151       }
152
153       public void lookupCanceled(LookupEvent event) {
154         dispose();
155       }
156
157       public void currentItemChanged(LookupEvent event) {
158         alarm.cancelAllRequests();
159         if (settings.AUTO_POPUP_JAVADOC_INFO) {
160           alarm.addRequest(request, settings.JAVADOC_INFO_DELAY);
161         }
162       }
163
164       private void dispose() {
165         alarm.cancelAllRequests();
166         if (daemonCodeAnalyzer != null) {
167           daemonCodeAnalyzer.setUpdateByTimerEnabled(true);
168         }
169         if (myActiveLookup == null) return;
170         myActiveLookup.removeLookupListener(this);
171         Lookup lookup = myActiveLookup;
172         myActiveLookup = null;
173         myActiveLookupEditor = null;
174         myPropertyChangeSupport.firePropertyChange(PROP_ACTIVE_LOOKUP, lookup, null);
175       }
176     });
177
178     if (items.length > 0) {
179       for (final LookupElement item : items) {
180         if (prefix != null) {
181           item.setPrefixMatcher(new CamelHumpMatcher(prefix));
182         }
183         myActiveLookup.addItem(item);
184       }
185       myActiveLookup.refreshUi();
186     }
187
188     myPropertyChangeSupport.firePropertyChange(PROP_ACTIVE_LOOKUP, null, myActiveLookup);
189     return myActiveLookup;
190   }
191
192   public void hideActiveLookup() {
193     if (myActiveLookup != null) {
194       myActiveLookup.hide();
195     }
196   }
197
198   public Lookup getActiveLookup() {
199     return myActiveLookup;
200   }
201
202   public void addPropertyChangeListener(PropertyChangeListener listener) {
203     myPropertyChangeSupport.addPropertyChangeListener(listener);
204   }
205
206   public void removePropertyChangeListener(PropertyChangeListener listener) {
207     myPropertyChangeSupport.removePropertyChangeListener(listener);
208   }
209
210   public boolean isDisposed() {
211     return myIsDisposed;
212   }
213
214 }