cleanup
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ui / ChangesViewContentManager.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.openapi.vcs.changes.ui;
18
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.application.ModalityState;
22 import com.intellij.openapi.components.AbstractProjectComponent;
23 import com.intellij.openapi.project.DumbAwareRunnable;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.startup.StartupManager;
26 import com.intellij.openapi.util.Disposer;
27 import com.intellij.openapi.util.IconLoader;
28 import com.intellij.openapi.util.Key;
29 import com.intellij.openapi.vcs.AbstractVcs;
30 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
31 import com.intellij.openapi.vcs.VcsBundle;
32 import com.intellij.openapi.vcs.VcsListener;
33 import com.intellij.openapi.wm.ToolWindow;
34 import com.intellij.openapi.wm.ToolWindowAnchor;
35 import com.intellij.openapi.wm.ToolWindowManager;
36 import com.intellij.ui.content.*;
37 import com.intellij.util.Alarm;
38 import com.intellij.util.NotNullFunction;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42
43 import javax.swing.*;
44 import java.util.*;
45
46 /**
47  * @author yole
48  */
49 public class ChangesViewContentManager extends AbstractProjectComponent {
50   public static final String TOOLWINDOW_ID = VcsBundle.message("changes.toolwindow.name");
51   private static final Key<ChangesViewContentEP> myEPKey = Key.create("ChangesViewContentEP");
52   private MyContentManagerListener myContentManagerListener;
53   private final ProjectLevelVcsManager myVcsManager;
54
55   public static ChangesViewContentManager getInstance(Project project) {
56     return project.getComponent(ChangesViewContentManager.class);
57   }
58
59   private ContentManager myContentManager;
60   private ToolWindow myToolWindow;
61   private final VcsListener myVcsListener = new MyVcsListener();
62   private final Alarm myVcsChangeAlarm;
63   private final List<Content> myAddedContents = new ArrayList<Content>();
64
65   public ChangesViewContentManager(final Project project, final ProjectLevelVcsManager vcsManager) {
66     super(project);
67     myVcsManager = vcsManager;
68     myVcsChangeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project);
69   }
70
71   public void projectOpened() {
72     if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
73     StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
74       public void run() {
75         final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
76         if (toolWindowManager != null) {
77           myToolWindow = toolWindowManager.registerToolWindow(TOOLWINDOW_ID, true, ToolWindowAnchor.BOTTOM, myProject, true);
78           myToolWindow.setIcon(IconLoader.getIcon("/general/toolWindowChanges.png"));
79           updateToolWindowAvailability();
80           final ContentManager contentManager = myToolWindow.getContentManager();
81           myContentManagerListener = new MyContentManagerListener();
82           contentManager.addContentManagerListener(myContentManagerListener);
83
84           myVcsManager.addVcsListener(myVcsListener);
85
86           Disposer.register(myProject, new Disposable(){
87             public void dispose() {
88               contentManager.removeContentManagerListener(myContentManagerListener);
89
90               myVcsManager.removeVcsListener(myVcsListener);
91             }
92           });
93
94           loadExtensionTabs();
95           myContentManager = contentManager;
96           final List<Content> ordered = doPresetOrdering(myAddedContents);
97           for(Content content: ordered) {
98             myContentManager.addContent(content);
99           }
100           myAddedContents.clear();
101           if (contentManager.getContentCount() > 0) {
102             contentManager.setSelectedContent(contentManager.getContent(0));
103           }
104         }
105       }
106     });
107   }
108
109   private void loadExtensionTabs() {
110     final List<Content> contentList = new LinkedList<Content>();
111     final ChangesViewContentEP[] contentEPs = myProject.getExtensions(ChangesViewContentEP.EP_NAME);
112     for(ChangesViewContentEP ep: contentEPs) {
113       final NotNullFunction<Project,Boolean> predicate = ep.newPredicateInstance(myProject);
114       if (predicate == null || predicate.fun(myProject).equals(Boolean.TRUE)) {
115         final Content content = ContentFactory.SERVICE.getInstance().createContent(new ContentStub(ep), ep.getTabName(), false);
116         content.setCloseable(false);
117         content.putUserData(myEPKey, ep);
118         contentList.add(content);
119       }
120     }
121     myAddedContents.addAll(0, contentList);
122   }
123
124   private void addExtensionTab(final ChangesViewContentEP ep) {
125     final Content content = ContentFactory.SERVICE.getInstance().createContent(new ContentStub(ep), ep.getTabName(), false);
126     content.setCloseable(false);
127     content.putUserData(myEPKey, ep);
128     addIntoCorrectPlace(content);
129   }
130
131   private void updateExtensionTabs() {
132     final ChangesViewContentEP[] contentEPs = myProject.getExtensions(ChangesViewContentEP.EP_NAME);
133     for(ChangesViewContentEP ep: contentEPs) {
134       final NotNullFunction<Project,Boolean> predicate = ep.newPredicateInstance(myProject);
135       if (predicate == null) continue;
136       Content epContent = findEPContent(ep);
137       final Boolean predicateResult = predicate.fun(myProject);
138       if (predicateResult.equals(Boolean.TRUE) && epContent == null) {
139         addExtensionTab(ep);
140       }
141       else if (predicateResult.equals(Boolean.FALSE) && epContent != null) {
142         if (!(epContent.getComponent() instanceof ContentStub)) {
143           ep.getInstance(myProject).disposeContent();
144         }
145         myContentManager.removeContent(epContent, true);
146       }
147     }
148   }
149
150   @Nullable
151   private Content findEPContent(final ChangesViewContentEP ep) {
152     final Content[] contents = myContentManager.getContents();
153     for(Content content: contents) {
154       if (content.getUserData(myEPKey) == ep) {
155         return content;
156       }
157     }
158     return null;
159   }
160
161   private void updateToolWindowAvailability() {
162     final AbstractVcs[] abstractVcses = myVcsManager.getAllActiveVcss();
163     myToolWindow.setAvailable(abstractVcses.length > 0, null);
164   }
165
166   public void projectClosed() {
167     myVcsChangeAlarm.cancelAllRequests();
168   }
169
170   @NonNls @NotNull
171   public String getComponentName() {
172     return "ChangesViewContentManager";
173   }
174
175   public void addContent(Content content) {
176     if (myContentManager == null) {
177       myAddedContents.add(content);
178     }
179     else {
180       addIntoCorrectPlace(content);
181     }
182   }
183
184   public void removeContent(final Content content) {
185     if (myContentManager != null) { // for unit tests
186       myContentManager.removeContent(content, true);
187     }
188   }
189
190   public void setSelectedContent(final Content content) {
191     myContentManager.setSelectedContent(content);
192   }
193
194   @Nullable
195   public <T> T getActiveComponent(final Class<T> aClass) {
196     final Content content = myContentManager.getSelectedContent();
197     if (content != null && aClass.isInstance(content.getComponent())) {
198       //noinspection unchecked
199       return (T) content.getComponent();
200     }
201     return null;
202   }
203
204   public void selectContent(final String tabName) {
205     for(Content content: myContentManager.getContents()) {
206       if (content.getDisplayName().equals(tabName)) {
207         myContentManager.setSelectedContent(content);
208         break;
209       }
210     }
211   }
212
213   private class MyVcsListener implements VcsListener {
214     public void directoryMappingChanged() {
215       myVcsChangeAlarm.cancelAllRequests();
216       myVcsChangeAlarm.addRequest(new Runnable() {
217         public void run() {
218           if (myProject.isDisposed()) return;
219           updateToolWindowAvailability();
220           updateExtensionTabs();
221         }
222       }, 100, ModalityState.NON_MODAL);
223     }
224   }
225
226   private static class ContentStub extends JPanel {
227     private final ChangesViewContentEP myEP;
228
229     private ContentStub(final ChangesViewContentEP EP) {
230       myEP = EP;
231     }
232
233     public ChangesViewContentEP getEP() {
234       return myEP;
235     }
236   }
237
238   private class MyContentManagerListener extends ContentManagerAdapter {
239     public void selectionChanged(final ContentManagerEvent event) {
240       Content content = event.getContent();
241       if (content.getComponent() instanceof ContentStub) {
242         ChangesViewContentEP ep = ((ContentStub) content.getComponent()).getEP();
243         ChangesViewContentProvider provider = ep.getInstance(myProject);
244         final JComponent contentComponent = provider.initContent();
245         content.setComponent(contentComponent);
246         if (contentComponent instanceof Disposable) {
247           content.setDisposer((Disposable) contentComponent);
248         }
249       }
250     }
251   }
252
253   private static final String[] ourPresetOrder = {"Local", "Repository", "Incoming", "Shelf"};
254   private static List<Content> doPresetOrdering(final List<Content> contents) {
255     final List<Content> result = new ArrayList<Content>(contents.size());
256     for (final String preset : ourPresetOrder) {
257       for (Iterator<Content> iterator = contents.iterator(); iterator.hasNext();) {
258         final Content current = iterator.next();
259         if (preset.equals(current.getTabName())) {
260           iterator.remove();
261           result.add(current);
262         }
263       }
264     }
265     result.addAll(contents);
266     return result;
267   }
268
269   private void addIntoCorrectPlace(final Content content) {
270     final String name = content.getTabName();
271     final Content[] contents = myContentManager.getContents();
272
273     int idxOfBeingInserted = -1;
274     for (int i = 0; i < ourPresetOrder.length; i++) {
275       final String s = ourPresetOrder[i];
276       if (s.equals(name)) {
277         idxOfBeingInserted = i;
278       }
279     }
280     if (idxOfBeingInserted == -1) {
281       myContentManager.addContent(content);
282       return;
283     }
284
285     final Set<String> existingNames = new HashSet<String>();
286     for (Content existingContent : contents) {
287       existingNames.add(existingContent.getTabName());
288     }
289
290     int place = idxOfBeingInserted;
291     for (int i = 0; i < idxOfBeingInserted; i++) {
292       if (! existingNames.contains(ourPresetOrder[i])) {
293         -- place;
294       }
295
296     }
297     myContentManager.addContent(content, place);
298   }
299 }