IDEA-127739 Navigation Tab
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / fileEditor / impl / PreviewPanel.java
1 /*
2  * Copyright 2000-2014 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 package com.intellij.openapi.fileEditor.impl;
17
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.ui.UISettings;
20 import com.intellij.ide.ui.UISettingsListener;
21 import com.intellij.openapi.actionSystem.AnAction;
22 import com.intellij.openapi.actionSystem.AnActionEvent;
23 import com.intellij.openapi.actionSystem.DefaultActionGroup;
24 import com.intellij.openapi.actionSystem.ToggleAction;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.fileEditor.FileEditorManager;
27 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Key;
30 import com.intellij.openapi.util.text.StringUtil;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.openapi.wm.*;
33 import com.intellij.openapi.wm.impl.ToolWindowImpl;
34 import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
35 import com.intellij.ui.content.Content;
36 import com.intellij.ui.content.ContentManager;
37 import com.intellij.ui.content.ContentManagerAdapter;
38 import com.intellij.ui.content.ContentManagerEvent;
39 import com.intellij.ui.docking.DockManager;
40 import com.intellij.util.ArrayUtil;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import javax.swing.*;
45 import java.awt.*;
46 import java.util.ArrayList;
47 import java.util.EnumSet;
48
49 class PreviewPanel extends JPanel {
50
51   private CardLayout myLayout;
52
53   enum ContentType {Files, Usages, Diagrams, Documentation}
54
55   private static final Key<VirtualFile> FILE_KEY = Key.create("v_file");
56   private static final int HISTORY_LIMIT = 10;
57
58   private final Project myProject;
59   private final FileEditorManagerImpl myManager;
60   private final DockManager myDockManager;
61   private EditorWindow myWindow;
62   private EditorsSplitters myEditorsSplitters;
63   private ArrayList<VirtualFile> myHistory = new ArrayList<VirtualFile>();
64   private VirtualFile myModifiedFile = null;
65   private ToolWindowImpl myToolWindow;
66   private VirtualFile myAwaitingForOpen = null;
67   private ContentManager myContentManager;
68   private Content myStubContent;
69   private boolean myBlocked = false;
70
71   private EnumSet<ContentType> myTypes = EnumSet.noneOf(ContentType.class);
72
73   static boolean isAvailable() {
74     return UISettings.getInstance().NAVIGATE_TO_PREVIEW;
75   }
76
77   PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) {
78     myProject = project;
79     myManager = manager;
80     myDockManager = dockManager;
81   }
82
83   /*
84   * @return null if preview is not avalable
85    */
86   @Nullable
87   EditorWindow getWindow() {
88     if (!isAvailable() || isBlocked() || myProject.isDisposed()) return null;
89     initToolWindowIfNeed();
90     return myWindow;
91   }
92
93   boolean isBlocked() {
94     return myBlocked;
95   }
96
97   private void initToolWindowIfNeed() {
98     if (!isAvailable() || ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW) != null) return;
99
100     myToolWindow = (ToolWindowImpl)ToolWindowManager.getInstance(myProject)
101       .registerToolWindow(ToolWindowId.PREVIEW, this, ToolWindowAnchor.RIGHT, myProject, false);
102     UISettings.getInstance().addUISettingsListener(new UISettingsListener() {
103       @Override
104       public void uiSettingsChanged(UISettings source) {
105         if (!isAvailable()) {
106           VirtualFile[] files = myWindow.getFiles();
107           for (VirtualFile file : files) {
108             close(file);
109           }
110           ToolWindowManager.getInstance(myProject).unregisterToolWindow(ToolWindowId.PREVIEW);
111         }
112       }
113     }, myProject);
114     myToolWindow.setIcon(AllIcons.Actions.PreviewDetails);
115     myToolWindow.setContentUiType(ToolWindowContentUiType.COMBO, null);
116     myContentManager = myToolWindow.getContentManager();
117     myStubContent = myContentManager.getContent(0);
118     myContentManager.addContentManagerListener(new ContentManagerAdapter() {
119       @Override
120       public void selectionChanged(ContentManagerEvent event) {
121         final VirtualFile file = event.getContent().getUserData(FILE_KEY);
122         if (event.getOperation() == ContentManagerEvent.ContentOperation.remove && file != null && file.equals(myModifiedFile)) {
123           close(file);
124           return;
125         }
126
127         if (event.getOperation() != ContentManagerEvent.ContentOperation.add) return;
128
129         if (file != null) {
130           event.getContent().setComponent(PreviewPanel.this);//Actually we share the same component between contents
131           if (!file.equals(myWindow.getSelectedFile())) {
132             ApplicationManager.getApplication().invokeLater(new Runnable() {
133               @Override
134               public void run() {
135                 myManager.openFileWithProviders(file, false, myWindow);
136               }
137             });
138           }
139         }
140       }
141     });
142
143     myEditorsSplitters = new MyEditorsSplitters();
144
145     myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER,
146                                                   new FileEditorManagerListener.Before() {
147                                                     @Override
148                                                     public void beforeFileOpened(@NotNull FileEditorManager source,
149                                                                                  @NotNull VirtualFile file) {
150                                                       myAwaitingForOpen = file;
151                                                       VirtualFile currentFile = getCurrentFile();
152                                                       if (currentFile != null &&
153                                                           currentFile.equals(myModifiedFile) &&
154                                                           !currentFile.equals(file)) {
155                                                         close(currentFile);
156                                                       }
157                                                     }
158
159                                                     @Override
160                                                     public void beforeFileClosed(@NotNull FileEditorManager source,
161                                                                                  @NotNull VirtualFile file) {
162                                                       checkStubContent();
163                                                     }
164                                                   });
165     myEditorsSplitters.createCurrentWindow();
166     myWindow = myEditorsSplitters.getCurrentWindow();
167     myWindow.setTabsPlacement(UISettings.TABS_NONE);
168     myLayout = new CardLayout();
169     setLayout(myLayout);
170     add(ContentType.Files.toString(), myEditorsSplitters);
171     //add(ContentType.Usages.toString(), myUsagesPreview);??? tree or editor ???
172     //add(ContentType.Diagrams.toString(), myDiagramPanel);
173     //add(ContentType.Documentation.toString(), myDocumentationPanel);//todo
174     myToolWindow.setTitleActions(new MoveToEditorTabsAction());
175     ArrayList<AnAction> myGearActions = new ArrayList<AnAction>();
176     for (ContentType contentType : ContentType.values()) {
177       myGearActions.add(new ContentTypeToggleAction(contentType));
178     }
179     myToolWindow.setAdditionalGearActions(new DefaultActionGroup("Preview", myGearActions));
180     myToolWindow.hide(null);
181   }
182
183   @Nullable
184   private VirtualFile getCurrentFile() {
185     VirtualFile[] files = myWindow.getFiles();
186     return files.length == 1 ? files[0] : null;
187   }
188
189   @NotNull
190   private Content addContent(VirtualFile file) {
191     myHistory.add(file);
192     while (myHistory.size() > HISTORY_LIMIT) {
193       myHistory.remove(0);
194     }
195     String title =
196       StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT);
197
198     Content content = myContentManager.getFactory().createContent(this, title, false);
199     content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
200     content.putUserData(FILE_KEY, file);
201     content.setIcon(file.getFileType().getIcon());
202     content.setPopupIcon(file.getFileType().getIcon());
203
204     myContentManager.addContent(content, 0);
205     checkStubContent();
206     return content;
207   }
208
209   private void setSelected(VirtualFile file) {
210     Content content = getContent(file);
211     if (content == null) {
212       content = addContent(file);
213     }
214     myContentManager.setSelectedContent(content);
215     myContentManager.addContent(content, 0);
216   }
217
218   @Nullable
219   private Content getContent(VirtualFile file) {
220     Content[] contents = myContentManager.getContents();
221     for (Content content : contents) {
222       if (file.equals(content.getUserData(FILE_KEY))) {
223         return content;
224       }
225     }
226     return null;
227   }
228
229   private void checkStubContent() {
230     if (myContentManager.getContents().length == 0) {
231       myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "false");
232       myStubContent.setComponent(this);
233       myContentManager.addContent(myStubContent);
234       ApplicationManager.getApplication().invokeLater(new Runnable() {
235         @Override
236         public void run() {
237           if (myContentManager.getIndexOfContent(myStubContent) != -1) {
238             toggleToolWindow(false);
239           }
240         }
241       });
242     }
243     else if (myContentManager.getContents().length > 1) {
244       myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
245       myContentManager.removeContent(myStubContent, false);
246     }
247   }
248
249   private void close(@NotNull VirtualFile file) {
250     myHistory.remove(file);
251     if (ArrayUtil.find(myEditorsSplitters.getOpenFiles(), file) != -1) {
252       myEditorsSplitters.closeFile(file, false);
253     }
254     if (file.equals(myAwaitingForOpen)) {
255       myAwaitingForOpen = null;
256     }
257     if (file.equals(myModifiedFile)) {
258       myBlocked = true;
259       try {
260         myManager.openFileWithProviders(myModifiedFile, false, true);
261       }
262       finally {
263         myBlocked = false;
264       }
265       myModifiedFile = null;
266     }
267     Content content = getContent(file);
268     if (content != null) {
269       myContentManager.removeContent(content, false);
270       checkStubContent();
271     }
272   }
273
274   private void toggleToolWindow(boolean activate) {
275     ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW);
276     if (toolWindow != null) {
277       if (activate) {
278         toolWindow.activate(null, false);
279       }
280       else {
281         toolWindow.hide(null);
282       }
283     }
284   }
285
286   private class MoveToEditorTabsAction extends AnAction {
287
288     public MoveToEditorTabsAction() {
289       super("Move to main tabs", "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed);
290     }
291
292     @Override
293     public void actionPerformed(AnActionEvent e) {
294       VirtualFile virtualFile = getCurrentFile();
295       if (virtualFile == null) {
296         return;
297       }
298
299       EditorWindow window = myManager.getCurrentWindow();
300       if (window == null) { //main tab set is still not created, rare situation
301         myManager.getMainSplitters().createCurrentWindow();
302         window = myManager.getCurrentWindow();
303       }
304       myManager.openFileWithProviders(virtualFile, true, window);
305       close(virtualFile);
306       toggleToolWindow(false);
307     }
308   }
309
310   private class ContentTypeToggleAction extends ToggleAction {
311     private final ContentType myContentType;
312
313     ContentTypeToggleAction(ContentType contentType) {
314       super(contentType.toString());
315       myContentType = contentType;
316     }
317
318     @Override
319     public boolean isSelected(AnActionEvent e) {
320       return myTypes.contains(myContentType);
321     }
322
323     @Override
324     public void setSelected(AnActionEvent e, boolean state) {
325       if (state) {
326         myTypes.add(myContentType);
327       } else {
328         myTypes.remove(myContentType);
329       }
330     }
331   }
332
333   private class MyEditorsSplitters extends EditorsSplitters {
334     public MyEditorsSplitters() {
335       super(myManager, myDockManager, false);
336     }
337
338     @Override
339     protected void afterFileOpen(VirtualFile file) {
340       if (file.equals(myAwaitingForOpen)) {
341         setSelected(file);
342       }
343       myAwaitingForOpen = null;
344     }
345
346     @Override
347     protected void afterFileClosed(VirtualFile file) {
348       close(file);
349     }
350
351     @Override
352     public void updateFileIcon(@NotNull VirtualFile file) {
353       EditorWithProviderComposite composite = myWindow.findFileComposite(file);
354       if (composite != null && composite.isModified()) {
355         myModifiedFile = file;
356       }
357     }
358
359     @Override
360     protected EditorWindow createEditorWindow() {
361       return new EditorWindow(this) {
362         @Override
363         protected void onBeforeSetEditor(VirtualFile file) {
364           VirtualFile currentFile = getCurrentFile();
365           if (currentFile != null && currentFile.equals(myModifiedFile)) {
366             myBlocked = true;
367             try {
368               myManager.openFileWithProviders(myModifiedFile, false, true);
369             }
370             finally {
371               myBlocked = false;
372             }
373           }
374           else {
375             toggleToolWindow(true);
376           }
377         }
378       };
379     }
380
381     @Override
382     public void setTabsPlacement(int tabPlacement) {
383       super.setTabsPlacement(UISettings.TABS_NONE);
384     }
385
386     @Override
387     public boolean isPreview() {
388       return true;
389     }
390   }
391 }