IDEA-127739 Navigation Tab
authorVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Tue, 23 Sep 2014 14:09:52 +0000 (18:09 +0400)
committerVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Tue, 23 Sep 2014 14:10:55 +0000 (18:10 +0400)
Stage 6: new API, "Files" and "Documentation" providers have been implemented

14 files changed:
platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java
platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationPreviewPanelProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java
platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FilePreviewPanelProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/preview/PreviewInfo.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/preview/PreviewManager.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/preview/PreviewPanelProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/preview/PreviewProviderId.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/preview/impl/PreviewManagerImpl.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowInfoImpl.java
platform/platform-resources/src/META-INF/LangExtensionPoints.xml
platform/platform-resources/src/META-INF/LangExtensions.xml

index 319ce54196da4abc41e22fc8f7ce061457403758..c5828145c9916bd2ae84c03c5d3d932709ee7bb4 100644 (file)
@@ -41,6 +41,7 @@ import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.preview.PreviewManager;
 import com.intellij.openapi.project.IndexNotReadyException;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.OrderEntry;
@@ -385,6 +386,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
     myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project);
 
     JBPopup _oldHint = getDocInfoHint();
+    PreviewManager previewManager = PreviewManager.SERVICE.getInstance(myProject);
+    if (previewManager != null &&
+        previewManager.preview(DocumentationPreviewPanelProvider.ID, Couple.of(element, originalElement), requestFocus) != null) {
+      return;
+    }
+
     if (myToolWindow == null && PropertiesComponent.getInstance().isTrueValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW)) {
       createToolWindow(element, originalElement);
     }
@@ -524,7 +531,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
     }
   }
 
-  private static String getTitle(@NotNull final PsiElement element, final boolean _short) {
+  static String getTitle(@NotNull final PsiElement element, final boolean _short) {
     final String title = SymbolPresentationUtil.getSymbolPresentableText(element);
     return _short ? title != null ? title : element.getText() : CodeInsightBundle.message("javadoc.info.title", title != null ? title : element.getText());
   }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationPreviewPanelProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationPreviewPanelProvider.java
new file mode 100644 (file)
index 0000000..6014968
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInsight.documentation;
+
+import com.intellij.openapi.preview.PreviewPanelProvider;
+import com.intellij.openapi.preview.PreviewProviderId;
+import com.intellij.openapi.util.Couple;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class DocumentationPreviewPanelProvider extends PreviewPanelProvider<Couple<PsiElement>, DocumentationComponent> {
+  public static final PreviewProviderId<Couple<PsiElement>, DocumentationComponent> ID = PreviewProviderId.create("Documentation");
+  private final DocumentationComponent myDocumentationComponent;
+  private final DocumentationManager myDocumentationManager;
+  private boolean myBlocked = false;
+
+  public DocumentationPreviewPanelProvider(DocumentationManager documentationManager) {
+    super(ID);
+    myDocumentationManager = documentationManager;
+    myDocumentationComponent = new DocumentationComponent(documentationManager) {
+      @Override
+      public String toString() {
+        return "Preview DocumentationComponent (" + (isEmpty() ? "empty" : "not empty") + ")";
+      }
+    };
+  }
+
+  @Override
+  public boolean shouldBeEnabledByDefault() {
+    return true;
+  }
+
+  @NotNull
+  @Override
+  protected JComponent getComponent() {
+    return myDocumentationComponent;
+  }
+
+  @NotNull
+  @Override
+  protected String getTitle(@NotNull Couple<PsiElement> content) {
+    return DocumentationManager.getTitle(content.getFirst(), false);
+  }
+
+  @Nullable
+  @Override
+  protected Icon getIcon(@NotNull Couple<PsiElement> content) {
+    return content.getFirst().getIcon(0);
+  }
+
+  @Override
+  public float getMenuOrder() {
+    return 1;
+  }
+
+  @Override
+  public boolean moveContentToStandardView(@NotNull Couple<PsiElement> content) {
+    myBlocked = true;
+    try {
+      myDocumentationManager.showJavaDocInfo(content.getFirst(), content.getSecond());
+    }
+    finally {
+      myBlocked = false;
+    }
+    return true;
+  }
+
+  @Override
+  protected DocumentationComponent initComponent(Couple<PsiElement> content, boolean requestFocus) {
+    if (myBlocked) return null;
+    if (!content.getFirst().getManager().areElementsEquivalent(myDocumentationComponent.getElement(), content.getFirst())) {
+      myDocumentationManager.fetchDocInfo(content.getFirst(), myDocumentationComponent);
+    }
+    return myDocumentationComponent;
+  }
+}
index 101f4a7ddfeda35a5622c476318ac90baa9b50d2..4807812e50477dd7e7823a287155dfed8c7b76be 100644 (file)
@@ -54,12 +54,9 @@ public class EditorEmptyTextPainter {
     UIUtil.TextPainter painter = new UIUtil.TextPainter().withLineSpacing(1.5f);
     painter.withShadow(true, new JBColor(Gray._200.withAlpha(100), Gray._0.withAlpha(255)));
 
-    painter.appendLine("No files are open");
+    painter.appendLine("No files are open").underlined(new JBColor(Gray._150, Gray._180));
 
-    if (!splitters.isPreview()) {
-      painter.underlined(new JBColor(Gray._150, Gray._180));
-      advertiseActions(splitters, painter);
-    }
+    advertiseActions(splitters, painter);
 
     painter.draw(g, new PairFunction<Integer, Integer, Couple<Integer>>() {
       @Override
index da28ee8832e0eebd5f92394e7af741e7067ea0a9..9237310470ef8f1114449c9126408e7b829de000 100644 (file)
@@ -42,6 +42,7 @@ import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
 import com.intellij.openapi.fileTypes.FileTypeEvent;
 import com.intellij.openapi.fileTypes.FileTypeListener;
 import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.preview.PreviewManager;
 import com.intellij.openapi.project.DumbAwareRunnable;
 import com.intellij.openapi.project.DumbService;
 import com.intellij.openapi.project.PossiblyDumbAware;
@@ -108,7 +109,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
   public static final String FILE_EDITOR_MANAGER = "FileEditorManager";
 
   private volatile JPanel myPanels;
-  private PreviewPanel myPreviewPanel;
   private EditorsSplitters mySplitters;
   private final Project myProject;
   private final List<Pair<VirtualFile, EditorWindow>> mySelectionHistory = new ArrayList<Pair<VirtualFile, EditorWindow>>();
@@ -178,10 +178,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
   @NotNull
   public Set<EditorsSplitters> getAllSplitters() {
     Set<EditorsSplitters> all = new LinkedHashSet<EditorsSplitters>();
-    EditorWindow previewWindow = getPreviewWindow();
-    if (previewWindow != null) {
-      all.add(previewWindow.getOwner());
-    }
     all.add(getMainSplitters());
     Set<DockContainer> dockContainers = myDockManager.getContainers();
     for (DockContainer each : dockContainers) {
@@ -255,18 +251,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
         }
       }
     }
-    if (myPreviewPanel == null && PreviewPanel.isAvailable()) {
-      synchronized (myInitLock) {
-        myPreviewPanel = new PreviewPanel(myProject, this, myDockManager);
-      }
-    }
-  }
-
-  @Nullable
-  private EditorWindow getPreviewWindow() {
-    if (!PreviewPanel.isAvailable()) return null;
-    initUI();
-    return myPreviewPanel.getWindow();
   }
 
   private static class MyBorder implements Border {
@@ -651,10 +635,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
     }
 
     if (wndToOpenIn == null || !wndToOpenIn.isFileOpen(file)) {
-      EditorWindow previewWindow = getPreviewWindow();
-      if (previewWindow != null) {
-        wndToOpenIn = previewWindow;
-        focusEditor = true;
+      PreviewManager previewManager = PreviewManager.SERVICE.getInstance(myProject);
+      if (previewManager != null) {
+        Pair<FileEditor[], FileEditorProvider[]> previewResult = previewManager.preview(FilePreviewPanelProvider.ID, file, focusEditor);
+        if (previewResult != null) {
+          return previewResult;
+        }
       }
     }
 
diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FilePreviewPanelProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FilePreviewPanelProvider.java
new file mode 100644 (file)
index 0000000..dcf2638
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.fileEditor.impl;
+
+import com.intellij.ide.ui.UISettings;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.preview.PreviewManager;
+import com.intellij.openapi.preview.PreviewPanelProvider;
+import com.intellij.openapi.preview.PreviewProviderId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.docking.DockManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FilePreviewPanelProvider extends PreviewPanelProvider<VirtualFile, Pair<FileEditor[], FileEditorProvider[]>> {
+  public static final PreviewProviderId<VirtualFile, Pair<FileEditor[], FileEditorProvider[]>> ID = PreviewProviderId.create("Files");
+
+  private final FileEditorManagerImpl myManager;
+  private final Project myProject;
+
+  private EditorWindow myWindow;
+  private EditorsSplitters myEditorsSplitters;
+
+  public FilePreviewPanelProvider(@NotNull Project project, @NotNull FileEditorManagerImpl manager, @NotNull DockManager dockManager) {
+    super(ID);
+    myProject = project;
+    myManager = manager;
+    myEditorsSplitters = new MyEditorsSplitters(manager, dockManager, false);
+    myEditorsSplitters.createCurrentWindow();
+    myWindow = myEditorsSplitters.getCurrentWindow();
+    myWindow.setTabsPlacement(UISettings.TABS_NONE);
+  }
+
+  @NotNull
+  @Override
+  protected JComponent getComponent() {
+    return myEditorsSplitters;
+  }
+
+  @Override
+  public boolean shouldBeEnabledByDefault() {
+    return true;
+  }
+
+  @Override
+  protected Pair<FileEditor[], FileEditorProvider[]> initComponent(VirtualFile file, boolean requestFocus) {
+    return myManager.openFileWithProviders(file, requestFocus, myWindow);
+  }
+
+  @NotNull
+  @Override
+  protected String getTitle(@NotNull VirtualFile file) {
+    return StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT);
+  }
+
+  @Nullable
+  @Override
+  protected Icon getIcon(@NotNull VirtualFile file) {
+    return file.getFileType().getIcon();
+  }
+
+  @Override
+  public float getMenuOrder() {
+    return 0;
+  }
+
+  @Override
+  public boolean moveContentToStandardView(@NotNull VirtualFile file) {
+    EditorWindow window = myManager.getCurrentWindow();
+    if (window == null) { //main tab set is still not created, rare situation
+      myManager.getMainSplitters().createCurrentWindow();
+      window = myManager.getCurrentWindow();
+    }
+    myManager.openFileWithProviders(file, true, window);
+    return true;
+  }
+
+  private class MyEditorsSplitters extends EditorsSplitters {
+    public MyEditorsSplitters(final FileEditorManagerImpl manager, DockManager dockManager, boolean createOwnDockableContainer) {
+      super(manager, dockManager, createOwnDockableContainer);
+    }
+
+    @Override
+    protected void afterFileClosed(VirtualFile file) {
+      PreviewManager previewManager = PreviewManager.SERVICE.getInstance(myProject);
+      if (previewManager != null) {
+        previewManager.close(getId(), file);
+      }
+    }
+
+    @Override
+    protected EditorWindow createEditorWindow() {
+      return new EditorWindow(this) {
+        @Override
+        protected void onBeforeSetEditor(VirtualFile file) {
+          List<VirtualFile> toMove = new ArrayList<VirtualFile>();
+          for (EditorWithProviderComposite composite : getEditorsComposites()) {
+            if (composite.isModified()) {
+              toMove.add(composite.getFile());
+            }
+          }
+          PreviewManager previewManager = PreviewManager.SERVICE.getInstance(myProject);
+          if (previewManager != null) {
+            for (VirtualFile virtualFile : toMove) {
+              previewManager.moveToStandardPlace(getId(), virtualFile);
+            }
+          }
+        }
+      };
+    }
+
+
+    @Override
+    public void setTabsPlacement(int tabPlacement) {
+      super.setTabsPlacement(UISettings.TABS_NONE);
+    }
+
+    @Override
+    public boolean isPreview() {
+      return true;
+    }
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/preview/PreviewInfo.java b/platform/platform-impl/src/com/intellij/openapi/preview/PreviewInfo.java
new file mode 100644 (file)
index 0000000..2d096f9
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.preview;
+
+import com.intellij.util.ui.EmptyIcon;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @param <V> is value type for data we show
+ * @param <C> is "container" type that would be used/reused for displaying data
+ */
+public class PreviewInfo<V, C> {
+  @NotNull private final PreviewPanelProvider<V, C> myProvider;
+  @NotNull private final V myData;
+
+  public static <V, C> PreviewInfo<V, C> create(@NotNull PreviewPanelProvider<V, C> provider, @NotNull V data) {
+    return new PreviewInfo<V, C>(provider, data);
+  }
+
+  public PreviewInfo(@NotNull PreviewPanelProvider<V, C> provider, @NotNull V data) {
+    myProvider = provider;
+    myData = data;
+  }
+
+  public PreviewProviderId<V, C> getId() {
+    return myProvider.getId();
+  }
+
+  @NotNull
+  public JComponent getComponent() {
+    return myProvider.getComponent();
+  }
+
+  @NotNull
+  public String getTitle() {
+    return myProvider.getTitle(myData);
+  }
+
+  @NotNull
+  public Icon getIcon() {
+    Icon icon = myProvider.getIcon(myData);
+    return icon != null ? icon : EmptyIcon.ICON_16;
+  }
+
+  @NotNull
+  public PreviewPanelProvider<V, C> getProvider() {
+    return myProvider;
+  }
+
+  @NotNull
+  public V getData() {
+    return myData;
+  }
+
+  public C initComponent(boolean requestFocus) {
+    return myProvider.initComponent(myData, requestFocus);
+  }
+
+  @Override
+  public int hashCode() {
+    return myProvider.hashCode() * 31 + myData.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return obj == this ||
+           (obj instanceof PreviewInfo && ((PreviewInfo)obj).myProvider.equals(myProvider) && ((PreviewInfo)obj).myData.equals(myData));
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/preview/PreviewManager.java b/platform/platform-impl/src/com/intellij/openapi/preview/PreviewManager.java
new file mode 100644 (file)
index 0000000..810563c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.preview;
+
+import com.intellij.ide.ui.UISettings;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface PreviewManager {
+  class SERVICE {
+
+    private SERVICE() {
+    }
+
+    @Nullable
+    public static PreviewManager getInstance(@NotNull Project project) {
+      if (!UISettings.getInstance().NAVIGATE_TO_PREVIEW) return null;
+      return ServiceManager.getService(project, PreviewManager.class);
+    }
+  }
+
+  /**
+   * @return <code>null</code> if provider is not available / not active or if it forces to use standard view instead of preview at the moment
+   */
+  @Nullable
+  <V, C> C preview(@NotNull PreviewProviderId<V, C> id, V data, boolean requestFocus);
+
+  <V, C> boolean moveToStandardPlace(@NotNull PreviewProviderId<V, C> id, V data);
+
+  <V, C> void close(@NotNull PreviewProviderId<V, C> id, V data);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/preview/PreviewPanelProvider.java b/platform/platform-impl/src/com/intellij/openapi/preview/PreviewPanelProvider.java
new file mode 100644 (file)
index 0000000..b38145c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.preview;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public abstract class PreviewPanelProvider<V, C> {
+  public static final ExtensionPointName<PreviewPanelProvider> EP_NAME =
+    new ExtensionPointName<PreviewPanelProvider>("com.intellij.previewPanelProvider");
+  private final PreviewProviderId<V, C> myId;
+
+  public PreviewPanelProvider(PreviewProviderId<V, C> id) {
+    myId = id;
+  }
+
+  @NotNull
+  public final PreviewProviderId<V, C> getId() {
+    return myId;
+  }
+
+  public abstract boolean shouldBeEnabledByDefault();
+
+  //Let's share single specific component for all provided previews (if possible)
+  @NotNull
+  protected abstract JComponent getComponent();
+
+  @NotNull
+  protected abstract String getTitle(@NotNull V content);
+
+  @Nullable
+  protected abstract Icon getIcon(@NotNull V content);
+
+  public abstract float getMenuOrder();
+
+  public abstract boolean moveContentToStandardView(@NotNull V content);
+
+  protected abstract C initComponent(V content, boolean requestFocus);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/preview/PreviewProviderId.java b/platform/platform-impl/src/com/intellij/openapi/preview/PreviewProviderId.java
new file mode 100644 (file)
index 0000000..925c525
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.preview;
+
+public class PreviewProviderId<V, C> {
+  private final String myVisualName;
+
+  public static <V, C> PreviewProviderId<V, C> create(String visualName) {
+    return new PreviewProviderId<V, C>(visualName);
+  }
+
+  private PreviewProviderId(String visualName) {
+    myVisualName = visualName;
+  }
+
+  public final String getVisualName() {
+    return myVisualName;
+  }
+
+  @Override
+  public final String toString() {
+    return getVisualName();
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/preview/impl/PreviewManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/preview/impl/PreviewManagerImpl.java
new file mode 100644 (file)
index 0000000..a7ba87c
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.preview.impl;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.ui.UISettings;
+import com.intellij.ide.ui.UISettingsListener;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.keymap.Keymap;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.preview.PreviewInfo;
+import com.intellij.openapi.preview.PreviewManager;
+import com.intellij.openapi.preview.PreviewPanelProvider;
+import com.intellij.openapi.preview.PreviewProviderId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Couple;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.wm.*;
+import com.intellij.openapi.wm.impl.ToolWindowImpl;
+import com.intellij.openapi.wm.impl.ToolWindowManagerImpl;
+import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
+import com.intellij.ui.Gray;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentManager;
+import com.intellij.ui.content.ContentManagerAdapter;
+import com.intellij.ui.content.ContentManagerEvent;
+import com.intellij.util.Alarm;
+import com.intellij.util.PairFunction;
+import com.intellij.util.ui.GraphicsUtil;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+
+public class PreviewManagerImpl implements PreviewManager {
+  private static final Key<PreviewInfo> INFO_KEY = Key.create("preview_info");
+  private static final int HISTORY_LIMIT = 10;
+
+  private final Project myProject;
+  private final Alarm myAlarm = new Alarm();
+
+  private ToolWindowImpl myToolWindow;
+
+  private ContentManager myContentManager;
+  private Content myEmptyStateContent;
+  private final JPanel myEmptyStatePanel;
+
+  private ArrayList<PreviewInfo> myHistory = new ArrayList<PreviewInfo>();
+
+
+  private TreeSet<PreviewPanelProvider> myProviders = new TreeSet<PreviewPanelProvider>(new Comparator<PreviewPanelProvider>() {
+    @Override
+    public int compare(PreviewPanelProvider o1, PreviewPanelProvider o2) {
+      return Float.compare(o1.getMenuOrder(), o2.getMenuOrder());
+    }
+  });
+  private Set<PreviewProviderId> myActiveProviderIds = new HashSet<PreviewProviderId>();
+  private boolean myInnerSelectionChange;
+
+  private static boolean isAvailable() {
+    return UISettings.getInstance().NAVIGATE_TO_PREVIEW;
+  }
+
+
+  public PreviewManagerImpl(Project project) {
+    myProject = project;
+    myEmptyStatePanel = new EmptyStatePanel();
+    PreviewPanelProvider[] providers = PreviewPanelProvider.EP_NAME.getExtensions(project);
+    for (PreviewPanelProvider provider : providers) {
+      myProviders.add(provider);
+      if (provider.shouldBeEnabledByDefault()) {
+        myActiveProviderIds.add(provider.getId());
+      }
+      //todo make it all externalizable to store/restore enabled/disabled state
+    }
+
+    UISettings.getInstance().addUISettingsListener(new UISettingsListener() {
+      @Override
+      public void uiSettingsChanged(UISettings source) {
+        checkGlobalState();
+      }
+    }, myProject);
+    checkGlobalState();
+    checkEmptyState();
+  }
+
+  @Nullable
+  public <V, C> PreviewPanelProvider<V, C> findProvider(@NotNull PreviewProviderId<V, C> id) {
+    for (PreviewPanelProvider provider : myProviders) {
+      if (id == provider.getId()) return provider;
+    }
+    return null;
+  }
+
+
+  protected void checkGlobalState() {
+    ToolWindowManagerImpl toolWindowManager = (ToolWindowManagerImpl)ToolWindowManager.getInstance(myProject);
+    if (!isAvailable() && toolWindowManager.getToolWindow(ToolWindowId.PREVIEW) != null) {
+      myHistory.clear();
+      myContentManager.removeAllContents(true);
+      toolWindowManager.unregisterToolWindow(ToolWindowId.PREVIEW);
+      return;
+    }
+    if (isAvailable() && toolWindowManager.getToolWindow(ToolWindowId.PREVIEW) == null) {
+      myToolWindow = (ToolWindowImpl)toolWindowManager
+        .registerToolWindow(ToolWindowId.PREVIEW, myEmptyStatePanel, ToolWindowAnchor.RIGHT, myProject, false);
+      myContentManager = myToolWindow.getContentManager();
+      myToolWindow.setIcon(AllIcons.Actions.PreviewDetails);
+      myToolWindow.setContentUiType(ToolWindowContentUiType.COMBO, null);
+      myToolWindow.setAutoHide(true);
+      myEmptyStateContent = myContentManager.getContent(0);
+      myContentManager.addContentManagerListener(new ContentManagerAdapter() {
+        @Override
+        public void selectionChanged(ContentManagerEvent event) {
+          if (myInnerSelectionChange || event.getOperation() != ContentManagerEvent.ContentOperation.add) return;
+          PreviewInfo previewInfo = event.getContent().getUserData(INFO_KEY);
+          if (previewInfo != null) {
+            preview(previewInfo, true);
+          }
+        }
+      });
+      MoveToStandardViewAction moveToStandardViewAction = new MoveToStandardViewAction();
+
+      moveToStandardViewAction.registerCustomShortcutSet(new ShortcutSet() {
+        @NotNull
+        @Override
+        public Shortcut[] getShortcuts() {
+          Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
+          return keymap.getShortcuts("ShowContent");
+        }
+      }, myToolWindow.getComponent());
+
+      myToolWindow.setTitleActions(moveToStandardViewAction);
+      ArrayList<AnAction> myGearActions = new ArrayList<AnAction>();
+      for (PreviewPanelProvider provider : myProviders) {
+        myGearActions.add(new ContentTypeToggleAction(provider));
+      }
+      myToolWindow.setAdditionalGearActions(new DefaultActionGroup("Preview", myGearActions));
+      //myToolWindow.hide(null);
+    }
+  }
+
+  private void checkEmptyState() {
+    if (myContentManager.getContents().length == 0) {
+      myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "false");
+      myContentManager.addContent(myEmptyStateContent);
+      //ApplicationManager.getApplication().invokeLater(new Runnable() {
+      //  @Override
+      //  public void run() {
+      //    if (myContentManager.getIndexOfContent(myEmptyStateContent) != -1) {
+      //      toggleToolWindow(false);
+      //    }
+      //  }
+      //});
+    }
+    else if (myContentManager.getContents().length > 1) {
+      myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
+      myContentManager.removeContent(myEmptyStateContent, false);
+    }
+  }
+
+  @Nullable
+  private Content getContent(@NotNull PreviewInfo info) {
+    for (Content content : myContentManager.getContents()) {
+      if (info.equals(content.getUserData(INFO_KEY))) {
+        return content;
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  private Content addContent(PreviewInfo info) {
+    myHistory.add(info);
+    while (myHistory.size() > HISTORY_LIMIT) {
+      PreviewInfo previewInfo = myHistory.remove(0);
+      close(previewInfo);
+    }
+
+    Content content = myContentManager.getFactory().createContent(info.getComponent(), info.getTitle(), false);
+    content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
+    content.putUserData(INFO_KEY, info);
+    content.setIcon(info.getIcon());
+    content.setPopupIcon(info.getIcon());
+
+    myContentManager.addContent(content, 0);
+    checkEmptyState();
+    return content;
+  }
+
+  private void close(@NotNull PreviewInfo info) {
+    Content content = getContent(info);
+    if (content != null) {
+      myContentManager.removeContent(content, false);
+      if (myContentManager.getContents().length == 0) {
+        toggleToolWindow(false);
+      }
+      checkEmptyState();
+    }
+  }
+
+  @Override
+  public <V, C> C preview(@NotNull PreviewProviderId<V, C> id, V data, boolean requestFocus) {
+    PreviewPanelProvider<V, C> provider = findProvider(id);
+    if (provider == null || !myActiveProviderIds.contains(id)) return null;
+    return preview(PreviewInfo.create(provider, data), requestFocus);
+  }
+
+  public <V, C> C preview(@NotNull PreviewInfo<V, C> info, boolean requestFocus) {
+    toggleToolWindow(true);
+    Content content = getContent(info);
+    if (myContentManager.getSelectedContent() != content) {
+      myInnerSelectionChange = true;
+      try {
+        if (content == null) {
+          content = addContent(info);
+        }
+        myContentManager.setSelectedContent(content);
+      }
+      finally {
+        myInnerSelectionChange = false;
+      }
+    }
+    if (content != null) {
+      myContentManager.addContent(content, 0);
+    }
+    return info.initComponent(requestFocus);
+  }
+
+  @Override
+  public <V, C> void close(@NotNull PreviewProviderId<V, C> id, V data) {
+    for (Content content : myContentManager.getContents()) {
+      PreviewInfo info = content.getUserData(INFO_KEY);
+      if (info != null && info.getId() == id && info.getData() == data) {
+        close(info);
+        break;
+      }
+    }
+  }
+
+  @Override
+  public <V, C> boolean moveToStandardPlace(@NotNull PreviewProviderId<V, C> id, V data) {
+    PreviewPanelProvider<V, C> provider = findProvider(id);
+    if (provider != null && provider.moveContentToStandardView(data)) {
+      close(id, data);
+      return true;
+    }
+    return false;
+  }
+
+  private void toggleToolWindow(boolean activate) {
+    final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW);
+    if (toolWindow != null && activate != toolWindow.isActive()) {
+      if (activate) {
+        myAlarm.addRequest(new Runnable() {
+          @Override
+          public void run() {
+            toolWindow.activate(null, false);
+          }
+        }, 75, ModalityState.stateForComponent(myEmptyStatePanel));
+      }
+      else {
+        if (!myAlarm.isEmpty()) {
+          toolWindow.hide(null);
+        }
+      }
+    }
+  }
+
+  private class MoveToStandardViewAction extends AnAction {
+
+    public MoveToStandardViewAction() {
+      super("Move to standard view", "Move to standard view", AllIcons.Duplicates.SendToTheLeftGrayed);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      Content selectedContent = myContentManager.getSelectedContent();
+      if (selectedContent == null) return;
+      PreviewInfo previewInfo = selectedContent.getUserData(INFO_KEY);
+      if (previewInfo != null) {
+        moveToStandardPlace(previewInfo.getId(), previewInfo.getData());
+        toggleToolWindow(false);
+      }
+    }
+  }
+
+  private class ContentTypeToggleAction extends ToggleAction {
+    private final PreviewPanelProvider myProvider;
+
+    ContentTypeToggleAction(PreviewPanelProvider provider) {
+      super(provider.getId().getVisualName());
+      myProvider = provider;
+    }
+
+    @Override
+    public boolean isSelected(AnActionEvent e) {
+      return myActiveProviderIds.contains(myProvider.getId());
+    }
+
+    @Override
+    public void setSelected(AnActionEvent e, boolean state) {
+      if (state) {
+        myActiveProviderIds.add(myProvider.getId());
+      }
+      else {
+        myActiveProviderIds.remove(myProvider.getId());
+        for (Iterator<PreviewInfo> iterator = myHistory.iterator(); iterator.hasNext(); ) {
+          PreviewInfo info = iterator.next();
+          if (info.getId().equals(myProvider.getId())) {
+            Content content = getContent(info);
+            if (content != null) {
+              myContentManager.removeContent(content, true);
+            }
+            iterator.remove();
+          }
+        }
+        checkEmptyState();
+      }
+    }
+  }
+
+  private static class EmptyStatePanel extends JPanel {
+    public EmptyStatePanel() {
+      setOpaque(true);
+    }
+
+    @Override
+    public void paint(Graphics g) {
+      boolean isDarkBackground = UIUtil.isUnderDarcula();
+      UIUtil.applyRenderingHints(g);
+      GraphicsUtil.setupAntialiasing(g, true, false);
+      g.setColor(new JBColor(isDarkBackground ? Gray._230 : Gray._80, Gray._160));
+      g.setFont(UIUtil.getLabelFont().deriveFont(isDarkBackground ? 24f : 20f));
+
+      UIUtil.TextPainter painter = new UIUtil.TextPainter().withLineSpacing(1.5f);
+      painter.withShadow(true, new JBColor(Gray._200.withAlpha(100), Gray._0.withAlpha(255)));
+
+      painter.appendLine("No files are open");//.underlined(new JBColor(Gray._150, Gray._180));
+      painter.draw(g, new PairFunction<Integer, Integer, Couple<Integer>>() {
+        @Override
+        public Couple<Integer> fun(Integer width, Integer height) {
+          Dimension s = EmptyStatePanel.this.getSize();
+          return Couple.of((s.width - width) / 2, (s.height - height) / 2);
+        }
+      });
+    }
+  }
+}
index ba5c59a9ac25576c2c03bfb87c3bb7f02fa765ad..dbee99dd8035cfc50de1683d1a5627298c6fcdc7 100644 (file)
@@ -376,8 +376,9 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
 
   public final ActionGroup createPopupGroup() {
     final DefaultActionGroup group = createGearPopupGroup();
-
-    group.add(myToggleContentUiTypeAction);
+    if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) {
+      group.add(myToggleContentUiTypeAction);
+    }
 
     final DefaultActionGroup moveGroup = new DefaultActionGroup(UIBundle.message("tool.window.move.to.action.group.name"), true);
     final ToolWindowAnchor anchor = myInfo.getAnchor();
@@ -432,7 +433,9 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
       group.add(myToggleFloatingModeAction);
     }
     else if (myInfo.isSliding()) {
-      group.add(myToggleDockModeAction);
+      if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) {
+        group.add(myToggleDockModeAction);
+      }
       group.add(myToggleFloatingModeAction);
       group.add(myToggleSideModeAction);
     }
index 5463b43273e60191ad114c00b6d5008ef44b1a3c..8fc9b55d609c8df7e8d4c6c72cc1eeebcbdea9c9 100644 (file)
@@ -17,10 +17,7 @@ package com.intellij.openapi.wm.impl;
 
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.JDOMExternalizable;
-import com.intellij.openapi.wm.ToolWindowAnchor;
-import com.intellij.openapi.wm.ToolWindowContentUiType;
-import com.intellij.openapi.wm.ToolWindowType;
-import com.intellij.openapi.wm.WindowInfo;
+import com.intellij.openapi.wm.*;
 import org.jdom.Element;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
@@ -128,7 +125,7 @@ public final class WindowInfoImpl implements Cloneable,JDOMExternalizable, Windo
     myAutoHide = info.myAutoHide;
     myFloatingBounds = info.myFloatingBounds == null ? null : (Rectangle)info.myFloatingBounds.clone();
     myId = info.myId;
-    myType = info.myType;
+    setTypeAndCheck(info.myType);
     myInternalType = info.myInternalType;
     myVisible = info.myVisible;
     myWeight = info.myWeight;
@@ -302,7 +299,7 @@ public final class WindowInfoImpl implements Cloneable,JDOMExternalizable, Windo
     catch (IllegalArgumentException ignored) {
     }
     try {
-      myType = parseToolWindowType(element.getAttributeValue(TYPE_ATTR));
+      setTypeAndCheck(parseToolWindowType(element.getAttributeValue(TYPE_ATTR)));
     }
     catch (IllegalArgumentException ignored) {
     }
@@ -362,7 +359,11 @@ public final class WindowInfoImpl implements Cloneable,JDOMExternalizable, Windo
     if(ToolWindowType.DOCKED==type||ToolWindowType.SLIDING==type){
       myInternalType=type;
     }
-    myType=type;
+    setTypeAndCheck(type);
+  }
+  //Hardcoded to avoid single-usage-API
+  private void setTypeAndCheck(ToolWindowType type) {
+    myType = ToolWindowId.PREVIEW == myId && type == ToolWindowType.DOCKED ? ToolWindowType.SLIDING : type;
   }
 
   void setVisible(final boolean visible){
index 50ac785eda39aadf7221384af697300481ba69d9..b16ce3f81509b5cdf40a5cd6ae8c5f24f3fb16ce 100644 (file)
 
     <extensionPoint name="goto.nonProjectScopeDisabler" beanClass="com.intellij.ide.actions.NonProjectScopeDisablerEP"/>
     <extensionPoint qualifiedName="com.intellij.equivalenceDescriptorProvider" interface="com.intellij.dupLocator.equivalence.EquivalenceDescriptorProvider"/>
+
+    <extensionPoint name="previewPanelProvider" interface="com.intellij.openapi.preview.PreviewPanelProvider" area="IDEA_PROJECT"/>
   </extensionPoints>
 </idea-plugin>
 
index 92f55b783bb49cb2018d9bcd13b159330b3aa031..70b8007e759b4d6c791d82679044969d95a53367 100644 (file)
 
     <projectService serviceInterface="com.intellij.execution.ExecutionManager" serviceImplementation="com.intellij.execution.impl.ExecutionManagerImpl"/>
     <projectService serviceInterface="com.intellij.execution.ExecutionTargetManager" serviceImplementation="com.intellij.execution.ExecutionTargetManagerImpl"/>
+    <previewPanelProvider implementation="com.intellij.openapi.fileEditor.impl.FilePreviewPanelProvider"/>
+    <previewPanelProvider implementation="com.intellij.codeInsight.documentation.DocumentationPreviewPanelProvider"/>
+    <projectService serviceInterface="com.intellij.openapi.preview.PreviewManager"
+                    serviceImplementation="com.intellij.openapi.preview.impl.PreviewManagerImpl"/>
   </extensions>
 </idea-plugin>