package com.jetbrains.python.module;
import com.intellij.openapi.Disposable;
-import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
-import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentFolder;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.impl.ContentEntryImpl;
-import com.intellij.openapi.roots.impl.ContentFolderBaseImpl;
import com.intellij.openapi.roots.ui.configuration.*;
import com.intellij.openapi.roots.ui.configuration.actions.ContentEntryEditingAction;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
-import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
-import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
-import com.intellij.ui.JBColor;
import com.intellij.util.EventDispatcher;
import com.intellij.util.containers.MultiMap;
-import com.jetbrains.python.templateLanguages.TemplatesService;
-import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import javax.swing.event.ChangeListener;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.util.ArrayList;
import java.util.List;
public class PyContentEntriesEditor extends CommonContentEntriesEditor {
- private static final Color TEMPLATES_COLOR = JBColor.MAGENTA;
- private final MultiMap<ContentEntry, VirtualFilePointer> myTemplateRoots = new MultiMap<ContentEntry, VirtualFilePointer>();
+ private final PyRootTypeProvider[] myRootTypeProviders;
private final Module myModule;
private Disposable myFilePointersDisposable;
-
- private final VirtualFilePointerListener DUMMY_LISTENER = new VirtualFilePointerListener() {
- @Override
- public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) {
- }
-
- @Override
- public void validityChanged(@NotNull VirtualFilePointer[] pointers) {
- }
- };
+ private MyContentEntryEditor myContentEntryEditor;
public PyContentEntriesEditor(Module module, ModuleConfigurationState moduleConfigurationState,
JpsModuleSourceRootType<?>... rootTypes) {
super(module.getName(), moduleConfigurationState, rootTypes);
+ myRootTypeProviders = Extensions.getExtensions(PyRootTypeProvider.EP_NAME);
myModule = module;
reset();
}
+ public MyContentEntryEditor getContentEntryEditor() {
+ return myContentEntryEditor;
+ }
+
@Override
protected ContentEntryTreeEditor createContentEntryTreeEditor(Project project) {
return new MyContentEntryTreeEditor(project, getEditHandlers());
return entries;
}
+ public ContentEntry[] getContentEntries() {
+ return getModel().getContentEntries();
+ }
+
@Override
public void reset() {
if (myFilePointersDisposable != null) {
Disposer.dispose(myFilePointersDisposable);
}
- myTemplateRoots.clear();
myFilePointersDisposable = Disposer.newDisposable();
- final TemplatesService instance = TemplatesService.getInstance(myModule);
- if (instance != null) {
- final List<VirtualFile> folders = instance.getTemplateFolders();
- for (VirtualFile folder : folders) {
- ContentEntry contentEntry = findContentEntryForFile(folder);
- if (contentEntry != null) {
- myTemplateRoots.putValue(contentEntry, VirtualFilePointerManager.getInstance().create(folder, myFilePointersDisposable,
- DUMMY_LISTENER));
- }
- }
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ provider.reset(myFilePointersDisposable, this, myModule);
}
if (myRootTreeEditor != null) {
}
}
- @Nullable
- private ContentEntry findContentEntryForFile(VirtualFile virtualFile) {
- for (ContentEntry contentEntry : getModel().getContentEntries()) {
- final VirtualFile file = contentEntry.getFile();
- if (file != null && VfsUtilCore.isAncestor(file, virtualFile, false)) {
- return contentEntry;
- }
- }
- return null;
- }
-
@Override
public void disposeUIResources() {
super.disposeUIResources();
@Override
public void apply() throws ConfigurationException {
super.apply();
- List<VirtualFile> templateRoots = getCurrentState();
- final TemplatesService templatesService = TemplatesService.getInstance(myModule);
- if (templatesService != null) {
- templatesService.setTemplateFolders(templateRoots.toArray(new VirtualFile[templateRoots.size()]));
- }
- }
-
- private List<VirtualFile> getCurrentState() {
- List<VirtualFile> result = new ArrayList<VirtualFile>();
- for (ContentEntry entry : myTemplateRoots.keySet()) {
- for (VirtualFilePointer filePointer : myTemplateRoots.get(entry)) {
- result.add(filePointer.getFile());
- }
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ provider.apply(myModule);
}
- return result;
}
@Override
public boolean isModified() {
if (super.isModified()) return true;
- final TemplatesService templatesService = TemplatesService.getInstance(myModule);
- if (templatesService != null) {
- List<VirtualFile> original = templatesService.getTemplateFolders();
- List<VirtualFile> current = getCurrentState();
-
- if (!Comparing.haveEqualElements(original, current)) return true;
-
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ if (provider.isModified(myModule)) {
+ return true;
+ }
}
return false;
}
@Override
protected MyContentEntryEditor createContentEntryEditor(String contentEntryUrl) {
- return new MyContentEntryEditor(contentEntryUrl, getEditHandlers());
+ myContentEntryEditor = new MyContentEntryEditor(contentEntryUrl, getEditHandlers());
+ return myContentEntryEditor;
}
protected class MyContentEntryEditor extends ContentEntryEditor {
@Override
public void deleteContentFolder(ContentEntry contentEntry, ContentFolder folder) {
- if (folder instanceof TemplateRootFolder) {
- removeTemplateRoot(folder.getUrl());
- }
- else {
- super.deleteContentFolder(contentEntry, folder);
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ if (provider.isMine(folder)) {
+ removeRoot(contentEntry, folder.getUrl(), provider);
+ return;
+ }
}
+ super.deleteContentFolder(contentEntry, folder);
}
- public void addTemplateRoot(@NotNull final VirtualFile file) {
- final VirtualFilePointer root = VirtualFilePointerManager.getInstance().create(file, myFilePointersDisposable, DUMMY_LISTENER);
- myTemplateRoots.putValue(getContentEntry(), root);
- myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this));
- update();
- }
-
- public void removeTemplateRoot(@NotNull final String url) {
- final VirtualFilePointer root = getTemplateRoot(url);
+ public void removeRoot(@Nullable ContentEntry contentEntry, String folder, PyRootTypeProvider provider) {
+ if (contentEntry == null) {
+ contentEntry = getContentEntry();
+ }
+ VirtualFilePointer root = getRoot(provider, folder);
if (root != null) {
- myTemplateRoots.remove(getContentEntry(), root);
- myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this));
- update();
+ provider.removeRoot(contentEntry, root);
+ fireUpdate();
}
}
- public boolean hasTemplateRoot(@NotNull final VirtualFile file) {
- return getTemplateRoot(file.getUrl()) != null;
+ public void fireUpdate() {
+ myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this));
+ update();
}
- @Nullable
- public VirtualFilePointer getTemplateRoot(@NotNull final String url) {
- for (VirtualFilePointer filePointer : myTemplateRoots.get(getContentEntry())) {
+ public VirtualFilePointer getRoot(PyRootTypeProvider provider, @NotNull final String url) {
+ for (VirtualFilePointer filePointer : provider.getRoots().get(getContentEntry())) {
if (Comparing.equal(filePointer.getUrl(), url)) {
return filePointer;
}
return null;
}
+ public void addRoot(PyRootTypeProvider provider, @NotNull final VirtualFilePointer root) {
+ provider.getRoots().putValue(getContentEntry(), root);
+ fireUpdate();
+ }
+
protected class MyContentRootPanel extends ContentRootPanel {
public MyContentRootPanel() {
super(MyContentEntryEditor.this, getEditHandlers());
@Override
protected void addFolderGroupComponents() {
super.addFolderGroupComponents();
- if (!myTemplateRoots.get(getContentEntry()).isEmpty()) {
- final List<TemplateRootFolder> folders = new ArrayList<TemplateRootFolder>(myTemplateRoots.size());
- for (VirtualFilePointer root : myTemplateRoots.get(getContentEntry())) {
- folders.add(new TemplateRootFolder(root, getContentEntry()));
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ MultiMap<ContentEntry, VirtualFilePointer> roots = provider.getRoots();
+ if (!roots.get(getContentEntry()).isEmpty()) {
+ final JComponent sourcesComponent = createFolderGroupComponent(provider.getName() + " Folders",
+ provider.createFolders(getContentEntry()),
+ provider.getColor(), null);
+ this.add(sourcesComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0));
}
- final JComponent sourcesComponent = createFolderGroupComponent("Template Folders",
- folders.toArray(new ContentFolder[folders.size()]),
- TEMPLATES_COLOR, null);
- this.add(sourcesComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH,
- GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0));
+
}
}
}
}
- private static class MyContentEntryTreeEditor extends ContentEntryTreeEditor {
+ private class MyContentEntryTreeEditor extends ContentEntryTreeEditor {
private final ChangeListener myListener = new ChangeListener() {
@Override
@Override
protected void createEditingActions() {
super.createEditingActions();
-
- ContentEntryEditingAction a = new ContentEntryEditingAction(myTree) {
- {
- final Presentation templatePresentation = getTemplatePresentation();
- templatePresentation.setText("Templates");
- templatePresentation.setDescription("Template Folders");
- templatePresentation.setIcon(PythonIcons.Python.TemplateRoot);
- }
-
- @Override
- public boolean isSelected(AnActionEvent e) {
- final VirtualFile[] selectedFiles = getSelectedFiles();
- return selectedFiles.length != 0 && getContentEntryEditor().hasTemplateRoot(selectedFiles[0]);
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ ContentEntryEditingAction action = provider.createRootEntryEditingAction(myTree, myFilePointersDisposable, PyContentEntriesEditor.this, getModel());
+ myEditingActionsGroup.add(action);
+ CustomShortcutSet shortcut = provider.getShortcut();
+ if (shortcut != null) {
+ action.registerCustomShortcutSet(shortcut, myTree);
}
-
- @Override
- public void setSelected(AnActionEvent e, boolean isSelected) {
- final VirtualFile[] selectedFiles = getSelectedFiles();
- assert selectedFiles.length != 0;
-
- for (VirtualFile selectedFile : selectedFiles) {
- boolean wasSelected = getContentEntryEditor().hasTemplateRoot(selectedFile);
- if (isSelected) {
- if (!wasSelected) {
- getContentEntryEditor().addTemplateRoot(selectedFile);
- }
- }
- else {
- if (wasSelected) {
- getContentEntryEditor().removeTemplateRoot(selectedFile.getUrl());
- }
- }
- }
- }
- };
- myEditingActionsGroup.add(a);
- a.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_MASK)), myTree);
+ }
}
@Override
return new ContentEntryTreeCellRenderer(this, getEditHandlers()) {
@Override
protected Icon updateIcon(final ContentEntry entry, final VirtualFile file, final Icon originalIcon) {
- if (getContentEntryEditor().hasTemplateRoot(file)) {
- return PythonIcons.Python.TemplateRoot;
+ for (PyRootTypeProvider provider : myRootTypeProviders) {
+ if (provider.hasRoot(file, PyContentEntriesEditor.this)) {
+ return provider.getIcon();
+ }
}
return super.updateIcon(entry, file, originalIcon);
}
};
}
}
- private static class TemplateRootFolder extends ContentFolderBaseImpl {
- protected TemplateRootFolder(@NotNull VirtualFilePointer filePointer, @NotNull ContentEntryImpl contentEntry) {
- super(filePointer, contentEntry);
- }
- }
-
}
--- /dev/null
+/*
+ * Copyright 2000-2015 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.jetbrains.python.module;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ContentFolder;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.actions.ContentEntryEditingAction;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public abstract class PyRootTypeProvider {
+ public static final ExtensionPointName<PyRootTypeProvider> EP_NAME = ExtensionPointName.create("Pythonid.pyRootTypeProvider");
+ protected final VirtualFilePointerListener DUMMY_LISTENER = new VirtualFilePointerListener() {
+ @Override
+ public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) {
+ }
+
+ @Override
+ public void validityChanged(@NotNull VirtualFilePointer[] pointers) {
+ }
+ };
+
+ public abstract void reset(@NotNull final Disposable disposable, PyContentEntriesEditor editor, Module module);
+
+ public abstract void apply(Module module);
+
+ public abstract boolean isModified(Module module);
+
+ public abstract boolean isMine(ContentFolder folder);
+
+ public void removeRoot(ContentEntry contentEntry, @NotNull final VirtualFilePointer root) {
+ getRoots().remove(contentEntry, root);
+ }
+ public abstract MultiMap<ContentEntry, VirtualFilePointer> getRoots();
+
+ public abstract Icon getIcon();
+
+ public abstract String getName();
+
+ public String getNamePlural() {
+ return getName() + "s";
+ }
+
+ public abstract Color getColor();
+
+ @Nullable
+ public CustomShortcutSet getShortcut() {
+ return null;
+ }
+
+
+ protected class RootEntryEditingAction extends ContentEntryEditingAction {
+ private final Disposable myDisposable;
+ private final PyContentEntriesEditor myEditor;
+ private final ModifiableRootModel myModel;
+
+ public RootEntryEditingAction(JTree tree, Disposable disposable, PyContentEntriesEditor editor, ModifiableRootModel model) {
+ super(tree);
+ final Presentation templatePresentation = getTemplatePresentation();
+ templatePresentation.setText(getNamePlural());
+ templatePresentation.setDescription(getName() + " Folders");
+ templatePresentation.setIcon(getIcon());
+ myDisposable = disposable;
+ myEditor = editor;
+ myModel = model;
+ }
+
+ @Override
+ public boolean isSelected(AnActionEvent e) {
+ final VirtualFile[] selectedFiles = getSelectedFiles();
+ return selectedFiles.length != 0 && hasRoot(selectedFiles[0], myEditor);
+ }
+
+ @Override
+ public void setSelected(AnActionEvent e, boolean isSelected) {
+ final VirtualFile[] selectedFiles = getSelectedFiles();
+ assert selectedFiles.length != 0;
+
+ for (VirtualFile selectedFile : selectedFiles) {
+ boolean wasSelected = hasRoot(selectedFile, myEditor);
+ if (isSelected) {
+ if (!wasSelected) {
+ final VirtualFilePointer root = VirtualFilePointerManager.getInstance().create(selectedFile, myDisposable, DUMMY_LISTENER);
+ addRoot(root, myEditor);
+ }
+ }
+ else {
+ if (wasSelected) {
+ removeRoot(selectedFile, myEditor, myModel);
+ }
+ }
+ }
+ }
+ }
+
+ private void addRoot(VirtualFilePointer root, PyContentEntriesEditor editor) {
+ editor.getContentEntryEditor().addRoot(this, root);
+ }
+
+ protected void removeRoot(VirtualFile selectedFile, PyContentEntriesEditor editor, ModifiableRootModel model) {
+ editor.getContentEntryEditor().removeRoot(null, selectedFile.getUrl(), this);
+ }
+
+ protected boolean hasRoot(VirtualFile file, PyContentEntriesEditor editor) {
+ PyContentEntriesEditor.MyContentEntryEditor entryEditor = editor.getContentEntryEditor();
+ return entryEditor.getRoot(this, file.getUrl()) != null;
+ }
+
+ public abstract ContentEntryEditingAction createRootEntryEditingAction(JTree tree,
+ Disposable disposable, PyContentEntriesEditor editor, ModifiableRootModel model);
+
+ public abstract ContentFolder[] createFolders(ContentEntry contentEntry);
+}