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.openapi.actionSystem.AnAction;
21 import com.intellij.openapi.actionSystem.AnActionEvent;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.fileEditor.FileEditorManager;
24 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.Key;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.wm.*;
30 import com.intellij.openapi.wm.impl.ToolWindowImpl;
31 import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
32 import com.intellij.ui.content.Content;
33 import com.intellij.ui.content.ContentManager;
34 import com.intellij.ui.content.ContentManagerAdapter;
35 import com.intellij.ui.content.ContentManagerEvent;
36 import com.intellij.ui.docking.DockManager;
37 import com.intellij.util.ArrayUtil;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import javax.swing.*;
42 import java.awt.*;
43 import java.util.ArrayList;
44
45 class PreviewPanel extends JPanel {
46   private static final Key<VirtualFile> FILE_KEY = Key.create("v_file");
47   private static final int HISTORY_LIMIT = 10;
48
49   private final Project myProject;
50   private final FileEditorManagerImpl myManager;
51   private final DockManager myDockManager;
52   private EditorWindow myWindow;
53   private EditorsSplitters myEditorsSplitters;
54   private ArrayList<VirtualFile> myHistory = new ArrayList<VirtualFile>();
55   private VirtualFile myModifiedFile = null;
56   private ToolWindowImpl myToolWindow;
57   private VirtualFile myAwaitingForOpen = null;
58   private ContentManager myContentManager;
59   private Content myStubContent;
60
61   static boolean isAvailable() {
62     return UISettings.getInstance().NAVIGATE_TO_PREVIEW;
63   }
64
65   PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) {
66     myProject = project;
67     myManager = manager;
68     myDockManager = dockManager;
69   }
70
71   // package-private API
72
73   EditorWindow getWindow() {
74     initToolWindowIfNeed();
75     return myWindow;
76   }
77
78   boolean hasCurrentFile(@NotNull VirtualFile file) {
79     return file.equals(getCurrentFile());
80   }
81
82   @Nullable
83   VirtualFile getCurrentModifiedFile() {
84     VirtualFile file = getCurrentFile();
85     return file != null && file.equals(myModifiedFile) ? file : null;
86   }
87
88   private void initToolWindowIfNeed() {
89     if (ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW) != null) return;
90
91     myToolWindow = (ToolWindowImpl)ToolWindowManager.getInstance(myProject)
92       .registerToolWindow(ToolWindowId.PREVIEW, this, ToolWindowAnchor.RIGHT, myProject, false);
93     myToolWindow.setIcon(AllIcons.Actions.PreviewDetails);
94     myToolWindow.setContentUiType(ToolWindowContentUiType.COMBO, null);
95     myContentManager = myToolWindow.getContentManager();
96     myStubContent = myContentManager.getContent(0);
97     myContentManager.addContentManagerListener(new ContentManagerAdapter() {
98       @Override
99       public void selectionChanged(ContentManagerEvent event) {
100         final VirtualFile file = event.getContent().getUserData(FILE_KEY);
101         if (event.getOperation() == ContentManagerEvent.ContentOperation.remove && file != null && file.equals(myModifiedFile)) {
102           close(file);
103           return;
104         }
105
106         if (event.getOperation() != ContentManagerEvent.ContentOperation.add) return;
107
108         if (file != null) {
109           event.getContent().setComponent(PreviewPanel.this);//Actually we share the same component between contents
110           if (!file.equals(myWindow.getSelectedFile())) {
111             ApplicationManager.getApplication().invokeLater(new Runnable() {
112               @Override
113               public void run() {
114                 myManager.openFileWithProviders(file, false, myWindow);
115               }
116             });
117           }
118         }
119       }
120     });
121
122     myEditorsSplitters = new MyEditorsSplitters();
123
124     myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER,
125                                                   new FileEditorManagerListener.Before() {
126                                                     @Override
127                                                     public void beforeFileOpened(@NotNull FileEditorManager source,
128                                                                                  @NotNull VirtualFile file) {
129                                                       myAwaitingForOpen = file;
130                                                       VirtualFile currentFile = getCurrentFile();
131                                                       if (currentFile != null &&
132                                                           currentFile.equals(myModifiedFile) &&
133                                                           !currentFile.equals(file)) {
134                                                         close(currentFile);
135                                                       }
136                                                     }
137
138                                                     @Override
139                                                     public void beforeFileClosed(@NotNull FileEditorManager source,
140                                                                                  @NotNull VirtualFile file) {
141                                                       checkStubContent();
142                                                     }
143                                                   });
144     myEditorsSplitters.createCurrentWindow();
145     myWindow = myEditorsSplitters.getCurrentWindow();
146     myWindow.setTabsPlacement(UISettings.TABS_NONE);
147     setLayout(new GridLayout(1, 1));
148     add(myEditorsSplitters);
149     myToolWindow.setTitleActions(new MoveToEditorTabsAction(), new CloseFileAction());
150     myToolWindow.hide(null);
151   }
152
153   @Nullable
154   private VirtualFile getCurrentFile() {
155     VirtualFile[] files = myWindow.getFiles();
156     return files.length == 1 ? files[0] : null;
157   }
158
159   @NotNull
160   private Content addContent(VirtualFile file) {
161     myHistory.add(file);
162     while (myHistory.size() > HISTORY_LIMIT) {
163       myHistory.remove(0);
164     }
165     String title =
166       StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT);
167
168     Content content = myContentManager.getFactory().createContent(this, title, false);
169     content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
170     content.putUserData(FILE_KEY, file);
171     content.setIcon(file.getFileType().getIcon());
172     content.setPopupIcon(file.getFileType().getIcon());
173
174     myContentManager.addContent(content);
175     checkStubContent();
176     return content;
177   }
178
179   private void setSelected(VirtualFile file) {
180     Content content = getContent(file);
181     if (content == null) {
182       content = addContent(file);
183     }
184     myContentManager.setSelectedContent(content);
185   }
186
187   @Nullable
188   private Content getContent(VirtualFile file) {
189     Content[] contents = myContentManager.getContents();
190     for (Content content : contents) {
191       if (file.equals(content.getUserData(FILE_KEY))) {
192         return content;
193       }
194     }
195     return null;
196   }
197
198   private void checkStubContent() {
199     if (myContentManager.getContents().length == 0) {
200       myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "false");
201       myStubContent.setComponent(this);
202       myContentManager.addContent(myStubContent);
203       ApplicationManager.getApplication().invokeLater(new Runnable() {
204         @Override
205         public void run() {
206           if (myContentManager.getIndexOfContent(myStubContent) != -1) {
207             ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).hide(null);
208           }
209         }
210       });
211     }
212     else if (myContentManager.getContents().length > 1) {
213       myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
214       myContentManager.removeContent(myStubContent, false);
215     }
216   }
217
218   private void close(@NotNull VirtualFile file) {
219     myHistory.remove(file);
220     if (ArrayUtil.find(myEditorsSplitters.getOpenFiles(), file) != -1) {
221       myEditorsSplitters.closeFile(file, false);
222     }
223     if (file.equals(myAwaitingForOpen)) {
224       myAwaitingForOpen = null;
225     }
226     if (file.equals(myModifiedFile)) {
227       myModifiedFile = null;
228     }
229     Content content = getContent(file);
230     if (content != null) {
231       myContentManager.removeContent(content, false);
232       checkStubContent();
233     }
234   }
235
236   private class MoveToEditorTabsAction extends AnAction {
237     public MoveToEditorTabsAction() {
238       super(null, "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed);
239     }
240
241     @Override
242     public void actionPerformed(AnActionEvent e) {
243       VirtualFile virtualFile = getCurrentFile();
244       if (virtualFile == null) {
245         return;
246       }
247
248       EditorWindow window = myManager.getCurrentWindow();
249       if (window == null) { //main tab set is still not created, rare situation
250         myManager.getMainSplitters().createCurrentWindow();
251         window = myManager.getCurrentWindow();
252       }
253       myManager.openFileWithProviders(virtualFile, true, window);
254       close(virtualFile);
255       ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).hide(null);
256     }
257   }
258
259
260   private class CloseFileAction extends AnAction {
261     public CloseFileAction() {
262       super(null, "Close", AllIcons.Actions.Close);
263     }
264
265     @Override
266     public void actionPerformed(AnActionEvent e) {
267       for (VirtualFile file : myHistory.toArray(new VirtualFile[myHistory.size()])) {
268         close(file);
269       }
270     }
271   }
272
273   private class MyEditorsSplitters extends EditorsSplitters {
274     public MyEditorsSplitters() {
275       super(myManager, myDockManager, false);
276     }
277
278     @Override
279     protected void afterFileOpen(VirtualFile file) {
280       if (file.equals(myAwaitingForOpen)) {
281         setSelected(file);
282       }
283       myAwaitingForOpen = null;
284     }
285
286     @Override
287     protected void afterFileClosed(VirtualFile file) {
288       close(file);
289     }
290
291     @Override
292     public void updateFileIcon(@NotNull VirtualFile file) {
293       EditorWithProviderComposite composite = myWindow.findFileComposite(file);
294       if (composite != null && composite.isModified()) {
295         myModifiedFile = file;
296       }
297     }
298
299     @Override
300     public void setTabsPlacement(int tabPlacement) {
301       super.setTabsPlacement(UISettings.TABS_NONE);
302     }
303
304     @Override
305     public boolean isPreview() {
306       return true;
307     }
308   }
309 }