blob: d7438b22f03cd433048c6bb94b7fb8af549f0c13 [file] [log] [blame]
/*
* Copyright 2000-2013 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 org.jetbrains.idea.maven.navigator;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunManagerAdapter;
import com.intellij.execution.RunManagerEx;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ex.ToolWindowEx;
import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.ui.treeStructure.SimpleTree;
import com.intellij.util.containers.ContainerUtil;
import icons.MavenIcons;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.maven.execution.MavenRunner;
import org.jetbrains.idea.maven.execution.MavenRunnerSettings;
import org.jetbrains.idea.maven.project.*;
import org.jetbrains.idea.maven.server.NativeMavenProjectHolder;
import org.jetbrains.idea.maven.tasks.MavenShortcutsManager;
import org.jetbrains.idea.maven.tasks.MavenTasksManager;
import org.jetbrains.idea.maven.utils.MavenLog;
import org.jetbrains.idea.maven.utils.MavenSimpleProjectComponent;
import org.jetbrains.idea.maven.utils.MavenUtil;
import javax.swing.*;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.net.URL;
import java.util.Collections;
import java.util.List;
@State(name = "MavenProjectNavigator", storages = {@Storage( file = StoragePathMacros.WORKSPACE_FILE)})
public class MavenProjectsNavigator extends MavenSimpleProjectComponent implements PersistentStateComponent<MavenProjectsNavigatorState> {
public static final String TOOL_WINDOW_ID = "Maven Projects";
private static final URL ADD_ICON_URL = MavenProjectsNavigator.class.getResource("/general/add.png");
private static final URL SYNC_ICON_URL = MavenProjectsNavigator.class.getResource("/actions/refresh.png");
private MavenProjectsNavigatorState myState = new MavenProjectsNavigatorState();
private MavenProjectsManager myProjectsManager;
private MavenTasksManager myTasksManager;
private MavenShortcutsManager myShortcutsManager;
private SimpleTree myTree;
private MavenProjectsStructure myStructure;
private ToolWindowEx myToolWindow;
public static MavenProjectsNavigator getInstance(Project project) {
return project.getComponent(MavenProjectsNavigator.class);
}
public MavenProjectsNavigator(Project project,
MavenProjectsManager projectsManager,
MavenTasksManager tasksManager,
MavenShortcutsManager shortcutsManager) {
super(project);
myProjectsManager = projectsManager;
myTasksManager = tasksManager;
myShortcutsManager = shortcutsManager;
}
public MavenProjectsNavigatorState getState() {
ApplicationManager.getApplication().assertIsDispatchThread();
if (myStructure != null) {
try {
myState.treeState = new Element("root");
TreeState.createOn(myTree).writeExternal(myState.treeState);
}
catch (WriteExternalException e) {
MavenLog.LOG.warn(e);
}
}
return myState;
}
public void loadState(MavenProjectsNavigatorState state) {
myState = state;
scheduleStructureUpdate();
}
public boolean getGroupModules() {
return myState.groupStructurally;
}
public void setGroupModules(boolean value) {
if (myState.groupStructurally != value) {
myState.groupStructurally = value;
scheduleStructureUpdate();
}
}
public boolean getShowIgnored() {
return myState.showIgnored;
}
public void setShowIgnored(boolean value) {
if (myState.showIgnored != value) {
myState.showIgnored = value;
scheduleStructureUpdate();
}
}
public boolean getShowBasicPhasesOnly() {
return myState.showBasicPhasesOnly;
}
public void setShowBasicPhasesOnly(boolean value) {
if (myState.showBasicPhasesOnly != value) {
myState.showBasicPhasesOnly = value;
scheduleStructureUpdate();
}
}
public boolean getAlwaysShowArtifactId() {
return myState.alwaysShowArtifactId;
}
public void setAlwaysShowArtifactId(boolean value) {
if (myState.alwaysShowArtifactId != value) {
myState.alwaysShowArtifactId = value;
scheduleStructureUpdate();
}
}
public boolean getShowVersions() {
return myState.showVersions;
}
public void setShowVersions(boolean value) {
if (myState.showVersions != value) {
myState.showVersions = value;
scheduleStructureUpdate();
}
}
@Override
public void initComponent() {
if (!isNormalProject()) return;
doInit();
}
@TestOnly
public void initForTests() {
doInit();
initTree();
initStructure();
}
private void doInit() {
listenForProjectsChanges();
if (isUnitTestMode()) return;
MavenUtil.runWhenInitialized(myProject, new DumbAwareRunnable() {
public void run() {
if (myProject.isDisposed()) return;
initToolWindow();
}
});
}
@Override
public void disposeComponent() {
myToolWindow = null;
myProjectsManager = null;
}
private void listenForProjectsChanges() {
myProjectsManager.addProjectsTreeListener(new MyProjectsListener());
myShortcutsManager.addListener(new MavenShortcutsManager.Listener() {
public void shortcutsUpdated() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateGoals();
}
});
}
});
myTasksManager.addListener(new MavenTasksManager.Listener() {
public void compileTasksChanged() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateGoals();
}
});
}
});
RunManagerEx.getInstanceEx(myProject).addRunManagerListener(new RunManagerAdapter() {
public void beforeRunTasksChanged() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateGoals();
}
});
}
});
MavenRunner.getInstance(myProject).getSettings().addListener(new MavenRunnerSettings.Listener() {
@Override
public void skipTestsChanged() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateGoals();
}
});
}
});
((RunManagerEx)RunManager.getInstance(myProject)).addRunManagerListener(new RunManagerAdapter() {
private void changed() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateRunConfigurations();
}
});
}
@Override
public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) {
changed();
}
@Override
public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) {
changed();
}
@Override
public void runConfigurationChanged(@NotNull RunnerAndConfigurationSettings settings) {
changed();
}
});
}
private void initToolWindow() {
initTree();
JPanel panel = new MavenProjectsNavigatorPanel(myProject, myTree);
AnAction removeAction = ActionManager.getInstance().getAction("Maven.RemoveRunConfiguration");
removeAction.registerCustomShortcutSet(CommonShortcuts.getDelete(), myTree, myProject);
AnAction editSource = ActionManager.getInstance().getAction("Maven.EditRunConfiguration");
editSource.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myTree, myProject);
final ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(myProject);
myToolWindow = (ToolWindowEx)manager.registerToolWindow(TOOL_WINDOW_ID, panel, ToolWindowAnchor.RIGHT, myProject, true);
myToolWindow.setIcon(MavenIcons.ToolWindowMaven);
final ToolWindowManagerAdapter listener = new ToolWindowManagerAdapter() {
boolean wasVisible = false;
@Override
public void stateChanged() {
if (myToolWindow.isDisposed()) return;
boolean visible = myToolWindow.isVisible();
if (!visible || visible == wasVisible) return;
scheduleStructureUpdate();
wasVisible = visible;
}
};
manager.addToolWindowManagerListener(listener);
ActionManager actionManager = ActionManager.getInstance();
DefaultActionGroup group = new DefaultActionGroup();
group.add(actionManager.getAction("Maven.GroupProjects"));
group.add(actionManager.getAction("Maven.ShowIgnored"));
group.add(actionManager.getAction("Maven.ShowBasicPhasesOnly"));
group.add(actionManager.getAction("Maven.AlwaysShowArtifactId"));
group.add(actionManager.getAction("Maven.ShowVersions"));
myToolWindow.setAdditionalGearActions(group);
Disposer.register(myProject, new Disposable() {
public void dispose() {
manager.removeToolWindowManagerListener(listener);
}
});
}
private void initTree() {
myTree = new SimpleTree() {
private final JLabel myLabel = new JLabel(
ProjectBundle.message("maven.navigator.nothing.to.display", MavenUtil.formatHtmlImage(ADD_ICON_URL),
MavenUtil.formatHtmlImage(SYNC_ICON_URL)));
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (myProjectsManager.hasProjects()) return;
myLabel.setFont(getFont());
myLabel.setBackground(getBackground());
myLabel.setForeground(getForeground());
Rectangle bounds = getBounds();
Dimension size = myLabel.getPreferredSize();
myLabel.setBounds(0, 0, size.width, size.height);
int x = (bounds.width - size.width) / 2;
Graphics g2 = g.create(bounds.x + x, bounds.y + 20, bounds.width, bounds.height);
try {
myLabel.paint(g2);
}
finally {
g2.dispose();
}
}
};
myTree.getEmptyText().clear();
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
}
@TestOnly
public MavenProjectsStructure getStructureForTests() {
return myStructure;
}
public void selectInTree(final MavenProject project) {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.select(project);
}
});
}
private void scheduleStructureRequest(final Runnable r) {
if (isUnitTestMode()) {
r.run();
return;
}
if (myToolWindow == null) return;
MavenUtil.invokeLater(myProject, new Runnable() {
public void run() {
if (!myToolWindow.isVisible()) return;
boolean shouldCreate = myStructure == null;
if (shouldCreate) {
initStructure();
}
r.run();
if (shouldCreate) {
if (myState.treeState != null) {
TreeState treeState = new TreeState();
try {
treeState.readExternal(myState.treeState);
treeState.applyTo(myTree);
}
catch (InvalidDataException e) {
MavenLog.LOG.info(e);
}
}
}
}
});
}
private void initStructure() {
myStructure = new MavenProjectsStructure(myProject, myProjectsManager, myTasksManager, myShortcutsManager, this, myTree);
}
private void scheduleStructureUpdate() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.update();
}
});
}
private class MyProjectsListener extends MavenProjectsTree.ListenerAdapter implements MavenProjectsManager.Listener {
public void activated() {
scheduleStructureUpdate();
}
public void projectsScheduled() {
}
@Override
public void importAndResolveScheduled() {
}
@Override
public void projectsIgnoredStateChanged(final List<MavenProject> ignored, final List<MavenProject> unignored, boolean fromImport) {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateIgnored(ContainerUtil.concat(ignored, unignored));
}
});
}
@Override
public void profilesChanged() {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateProfiles();
}
});
}
@Override
public void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted) {
scheduleUpdateProjects(MavenUtil.collectFirsts(updated), deleted);
}
public void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
NativeMavenProjectHolder nativeMavenProject) {
scheduleUpdateProjects(Collections.singletonList(projectWithChanges.first), Collections.<MavenProject>emptyList());
}
public void pluginsResolved(MavenProject project) {
scheduleUpdateProjects(Collections.singletonList(project), Collections.<MavenProject>emptyList());
}
private void scheduleUpdateProjects(final List<MavenProject> projects, final List<MavenProject> deleted) {
scheduleStructureRequest(new Runnable() {
public void run() {
myStructure.updateProjects(projects, deleted);
}
});
}
}
}