IDEA-41229: attach source directly from edit window

This commit is contained in:
Dmitry Avdeev
2010-01-20 12:23:49 +03:00
parent 3a43cf0a09
commit 1edbecfcc2
10 changed files with 214 additions and 33 deletions
@@ -0,0 +1,148 @@
/*
* Copyright 2000-2010 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.daemon.impl;
import com.intellij.ProjectTopics;
import com.intellij.ide.highlighter.JavaClassFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.EditorNotifications;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Dmitry Avdeev
*/
public class AddSourcesNotificationProvider implements EditorNotifications.Provider<EditorNotificationPanel> {
private static final Key<EditorNotificationPanel> KEY = Key.create("add sources to class");
private final Project myProject;
public AddSourcesNotificationProvider(Project project, final EditorNotifications notifications) {
myProject = project;
myProject.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
public void beforeRootsChange(ModuleRootEvent event) {
}
public void rootsChanged(ModuleRootEvent event) {
notifications.updateAllNotifications();
}
});
}
public Key<EditorNotificationPanel> getKey() {
return KEY;
}
public EditorNotificationPanel createNotificationPanel(final VirtualFile file) {
if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
final Library library = findLibrary(file);
if (library == null) return null;
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
if (!(psiFile instanceof PsiJavaFile)) return null;
PsiClass[] classes = ((PsiJavaFile)psiFile).getClasses();
if (classes.length == 0) return null;
final String fqn = classes[0].getQualifiedName();
if (fqn == null) return null;
final EditorNotificationPanel panel = new EditorNotificationPanel();
panel.setText("Class sources not found");
panel.createActionLabel("Attach sources", new Runnable() {
public void run() {
final Library library = findLibrary(file);
if (library == null) {
Messages.showErrorDialog(myProject, "Cannot find library for " + StringUtil.getShortName(fqn), "Error");
return;
}
FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, true, false, true, true);
descriptor.setTitle(ProjectBundle.message("library.attach.sources.action"));
descriptor.setDescription(ProjectBundle.message("library.attach.sources.description"));
VirtualFile[] roots = library.getFiles(OrderRootType.CLASSES);
VirtualFile[] candidates = FileChooser.chooseFiles(myProject, descriptor, roots.length == 0 ? null : roots[0]);
final VirtualFile[] files = PathUIUtils.scandAndSelectDetectedJavaSourceRoots(myProject, candidates);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
Library.ModifiableModel model = library.getModifiableModel();
for (VirtualFile virtualFile : files) {
model.addRoot(virtualFile, OrderRootType.SOURCES);
}
model.commit();
}
});
PsiClass clsClass = JavaPsiFacade.getInstance(myProject).findClass(fqn, GlobalSearchScope.allScope(myProject));
if (!(clsClass instanceof ClsClassImpl)) {
return;
}
PsiClass sourceClass = ((ClsClassImpl)clsClass).getSourceMirrorClass();
if (sourceClass == null) {
return;
}
VirtualFile newFile = sourceClass.getContainingFile().getVirtualFile();
assert newFile != null;
FileEditorManagerEx manager = FileEditorManagerEx.getInstanceEx(myProject);
EditorWindow[] windows = manager.getWindows();
for (EditorWindow window : windows) {
int index = window.findFileIndex(file);
if (index != -1) {
manager.closeFile(file, window);
try {
newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, index);
manager.openFile(newFile, true);
}
finally {
newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, null);
}
}
}
}
});
return panel;
}
@Nullable
private Library findLibrary(VirtualFile file) {
List<OrderEntry> entries = ProjectRootManager.getInstance(myProject).getFileIndex().getOrderEntriesForFile(file);
for (OrderEntry entry : entries) {
if (entry instanceof LibraryOrderEntry) {
return ((LibraryOrderEntry)entry).getLibrary();
}
}
return null;
}
}
@@ -17,6 +17,7 @@ package com.intellij.openapi.roots.ui.configuration;
import com.intellij.ide.util.JavaUtilForVfs;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.DetectedSourceRootsDialog;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -38,11 +39,11 @@ public class PathUIUtils {
* This method takes a candidates for the project root, then scans the candidates and
* if multiple candidates or non root source directories are found whithin some
* directories, it shows a dialog that allows selecting or deselecting them.
* @param component a parent component
* @param parent a parent parent or project
* @param rootCandidates a candidates for roots
* @return a array of source folders or empty array if non was selected or dialog was canceled.
*/
public static VirtualFile[] scandAndSelectDetectedJavaSourceRoots(Component component, final VirtualFile[] rootCandidates) {
public static VirtualFile[] scandAndSelectDetectedJavaSourceRoots(Object parent, final VirtualFile[] rootCandidates) {
final Set<VirtualFile> result = new HashSet<VirtualFile>();
final Map<VirtualFile, List<VirtualFile>> detectedRootsMap = new LinkedHashMap<VirtualFile, List<VirtualFile>>();
// scan for roots
@@ -59,7 +60,9 @@ public class PathUIUtils {
}
}, "Scanning for source roots", true, null);
if(!detectedRootsMap.isEmpty()) {
DetectedSourceRootsDialog dlg = new DetectedSourceRootsDialog(component, detectedRootsMap);
DetectedSourceRootsDialog dlg = parent instanceof Component ?
new DetectedSourceRootsDialog((Component)parent, detectedRootsMap) :
new DetectedSourceRootsDialog((Project)parent, detectedRootsMap);
dlg.show();
if (dlg.isOK()) {
result.addAll(dlg.getChosenRoots());
@@ -24,8 +24,14 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class JavaClassFileType implements FileType {
public static JavaClassFileType INSTANCE = new JavaClassFileType();
private static final Icon ICON = IconLoader.getIcon("/fileTypes/javaClass.png");
private JavaClassFileType() {
}
@NotNull
public String getName() {
return "CLASS";
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class DefaultFileTypeFactory extends FileTypeFactory {
public void createFileTypes(@NotNull final FileTypeConsumer consumer) {
consumer.consume(new JavaClassFileType(), "class");
consumer.consume(JavaClassFileType.INSTANCE, "class");
consumer.consume(JavaFileType.INSTANCE, "java");
@@ -24,7 +24,8 @@ import java.util.EventListener;
import java.util.Iterator;
/**
* @author dsl
* @see com.intellij.openapi.roots.libraries.LibraryTablesRegistrar#getLibraryTable(com.intellij.openapi.project.Project)
* @author dsl
*/
public interface LibraryTable {
@NotNull
@@ -37,8 +37,10 @@ public class EditorNotificationPanel extends JPanel {
public EditorNotificationPanel() {
super(new BorderLayout());
setBackground(LightColors.YELLOW);
setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 4));
setPreferredSize(new Dimension(-1, 24));
add(myLabel, BorderLayout.CENTER);
myLinksPanel = new JPanel(new FlowLayout());
@@ -51,11 +53,19 @@ public class EditorNotificationPanel extends JPanel {
}
public HyperlinkLabel createActionLabel(final String text, @NonNls final String actionId) {
return createActionLabel(text, new Runnable() {
public void run() {
executeAction(actionId);
}
});
}
public HyperlinkLabel createActionLabel(final String text, final Runnable action) {
HyperlinkLabel label = new HyperlinkLabel(text, Color.BLUE, LightColors.YELLOW, Color.BLUE);
label.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(final HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
executeAction(actionId);
action.run();
}
}
});
@@ -41,6 +41,7 @@ public class EditorNotifications extends AbstractProjectComponent {
public interface Provider<T extends JComponent> {
Key<T> getKey();
@Nullable
@@ -88,16 +89,23 @@ public class EditorNotifications extends AbstractProjectComponent {
});
}
public void updateAllNotifications() {
VirtualFile[] files = myFileEditorManager.getOpenFiles();
for (VirtualFile file : files) {
updateNotifications(file);
}
}
private void updateNotification(FileEditor editor, Key<? extends JComponent> key, @Nullable JComponent component) {
JComponent old = editor.getUserData(key);
if (old != null) {
myFileEditorManager.removeTopComponent(editor, old);
}
if (component != null) {
myFileEditorManager.addTopComponent(editor, component);
editor.putUserData((Key<JComponent>)key, component);
}
else {
JComponent old = editor.getUserData(key);
if (old != null) {
myFileEditorManager.removeTopComponent(editor, old);
}
editor.putUserData(key, null);
}
}
@@ -27,6 +27,7 @@ import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.LayeredIcon;
@@ -57,6 +58,7 @@ public class EditorWindow {
private boolean myIsDisposed = false;
private static final Icon PIN_ICON = IconLoader.getIcon("/nodes/tabPin.png");
public static final Key<Integer> INITIAL_INDEX_KEY = Key.create("initial editor index");
protected EditorWindow(final EditorsSplitters owner) {
myOwner = owner;
@@ -404,7 +406,8 @@ public class EditorWindow {
setSelectedEditor(editor, focusEditor);
}
else {
final int indexToInsert = myTabbedPane.getSelectedIndex() + 1;
Integer initialIndex = editor.getFile().getUserData(INITIAL_INDEX_KEY);
final int indexToInsert = initialIndex == null ? myTabbedPane.getSelectedIndex() + 1 : initialIndex;
final VirtualFile file = editor.getFile();
final Icon template = IconLoader.getIcon("/fileTypes/text.png");
myTabbedPane.insertTab(file, new EmptyIcon(template.getIconWidth(), template.getIconHeight()), new TComp(editor), null, indexToInsert);
@@ -661,7 +664,7 @@ public class EditorWindow {
return -1;
}
private int findFileIndex(final VirtualFile fileToFind) {
public int findFileIndex(final VirtualFile fileToFind) {
for (int i = 0; i != getTabCount(); ++i) {
final VirtualFile file = getFileAt(i);
if (file.equals (fileToFind)) {
@@ -25,7 +25,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.InplaceButton;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
@@ -50,13 +49,26 @@ public class ChangelistConflictNotificationPanel extends EditorNotificationPanel
myChangeList = manager.getChangeList(myChange);
assert myChangeList != null;
myLabel.setText("File from non-active changelist is modified");
createActionLabel("Move changes", "move").
setToolTipText("Move changes to active changelist (" + manager.getDefaultChangeList().getName() + ")");
createActionLabel("Switch changelist", "switch").
setToolTipText("Set active changelist to '" + myChangeList.getName() + "'");
createActionLabel("Ignore", "ignore").
setToolTipText("Hide this notification");
setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
createActionLabel("Move changes", new Runnable() {
public void run() {
ChangelistConflictResolution.MOVE.resolveConflict(myTracker.getProject(), myChangeList.getChanges());
}
}).setToolTipText("Move changes to active changelist (" + manager.getDefaultChangeList().getName() + ")");
createActionLabel("Switch changelist", new Runnable() {
public void run() {
List<Change> changes = Collections.singletonList(myTracker.getChangeListManager().getChange(myFile));
ChangelistConflictResolution.SWITCH.resolveConflict(myTracker.getProject(), changes);
}
}).setToolTipText("Set active changelist to '" + myChangeList.getName() + "'");
createActionLabel("Ignore", new Runnable() {
public void run() {
myTracker.ignoreConflict(myFile, true);
}
}).setToolTipText("Hide this notification");
// setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
myLinksPanel.add(new InplaceButton("Show options dialog", IconLoader.getIcon("/general/ideOptions.png"), new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -66,16 +78,4 @@ public class ChangelistConflictNotificationPanel extends EditorNotificationPanel
}
}));
}
@Override
protected void executeAction(String actionId) {
if (actionId.equals("move")) {
ChangelistConflictResolution.MOVE.resolveConflict(myTracker.getProject(), myChangeList.getChanges());
} else if (actionId.equals("switch")) {
List<Change> changes = Collections.singletonList(myTracker.getChangeListManager().getChange(myFile));
ChangelistConflictResolution.SWITCH.resolveConflict(myTracker.getProject(), changes);
} else if (actionId.equals("ignore")) {
myTracker.ignoreConflict(myFile, true);
}
}
}
+2
View File
@@ -668,6 +668,8 @@
<fileType.fileViewProviderFactory filetype="CLASS" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
<editorNotificationProvider implementation="com.intellij.codeInsight.daemon.impl.AddSourcesNotificationProvider"/>
<lang.psiStructureViewFactory language="JAVA" implementationClass="com.intellij.lang.java.JavaStructureViewBuilderFactory"/>
<lang.ast.factory language="JAVA" implementationClass="com.intellij.psi.impl.source.tree.JavaASTFactory"/>