| /* |
| * Copyright 2000-2009 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.ide.util.scopeChooser; |
| |
| import com.intellij.codeInspection.InspectionsBundle; |
| import com.intellij.execution.ExecutionBundle; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.IdeBundle; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.options.ConfigurationException; |
| import com.intellij.openapi.options.SearchableConfigurable; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectBundle; |
| import com.intellij.openapi.ui.*; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.packageDependencies.DependencyValidationManager; |
| import com.intellij.psi.search.scope.packageSet.*; |
| import com.intellij.ui.TreeSpeedSearch; |
| import com.intellij.util.IconUtil; |
| import com.intellij.util.containers.Convertor; |
| import com.intellij.util.containers.HashSet; |
| import com.intellij.util.ui.tree.TreeUtil; |
| import com.intellij.util.xmlb.annotations.AbstractCollection; |
| import com.intellij.util.xmlb.annotations.Tag; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.DefaultTreeModel; |
| import javax.swing.tree.TreePath; |
| import java.awt.*; |
| import java.awt.event.InputEvent; |
| import java.awt.event.KeyEvent; |
| import java.util.*; |
| import java.util.List; |
| |
| /** |
| * User: anna |
| * Date: 01-Jul-2006 |
| */ |
| public class ScopeChooserConfigurable extends MasterDetailsComponent implements SearchableConfigurable { |
| @NonNls public static final String SCOPE_CHOOSER_CONFIGURABLE_UI_KEY = "ScopeChooserConfigurable.UI"; |
| public static final String PROJECT_SCOPES = "project.scopes"; |
| private final NamedScopesHolder myLocalScopesManager; |
| private final NamedScopesHolder mySharedScopesManager; |
| |
| private final Project myProject; |
| |
| public ScopeChooserConfigurable(final Project project) { |
| super(new ScopeChooserConfigurableState()); |
| myLocalScopesManager = NamedScopeManager.getInstance(project); |
| mySharedScopesManager = DependencyValidationManager.getInstance(project); |
| myProject = project; |
| |
| initTree(); |
| } |
| |
| @Override |
| protected String getComponentStateKey() { |
| return SCOPE_CHOOSER_CONFIGURABLE_UI_KEY; |
| } |
| |
| @Override |
| protected Dimension getPanelPreferredSize() { |
| return new Dimension(400, 200); |
| } |
| |
| @Override |
| protected MasterDetailsStateService getStateService() { |
| return MasterDetailsStateService.getInstance(myProject); |
| } |
| |
| @Override |
| protected ArrayList<AnAction> createActions(final boolean fromPopup) { |
| final ArrayList<AnAction> result = new ArrayList<AnAction>(); |
| result.add(new MyAddAction(fromPopup)); |
| result.add(new MyDeleteAction(forAll(new Condition<Object>() { |
| @Override |
| public boolean value(final Object o) { |
| if (o instanceof MyNode) { |
| final NamedConfigurable namedConfigurable = ((MyNode)o).getConfigurable(); |
| final Object editableObject = namedConfigurable != null ? namedConfigurable.getEditableObject() : null; |
| return editableObject instanceof NamedScope; |
| } |
| return false; |
| } |
| }))); |
| result.add(new MyCopyAction()); |
| result.add(new MySaveAsAction()); |
| result.add(new MyMoveAction(ExecutionBundle.message("move.up.action.name"), IconUtil.getMoveUpIcon(), -1)); |
| result.add(new MyMoveAction(ExecutionBundle.message("move.down.action.name"), IconUtil.getMoveDownIcon(), 1)); |
| return result; |
| } |
| |
| @Override |
| public void reset() { |
| myRoot.removeAllChildren(); |
| loadScopes(mySharedScopesManager); |
| loadScopes(myLocalScopesManager); |
| |
| loadComponentState(); |
| |
| final List<String> order = getScopesState().myOrder; |
| TreeUtil.sort(myRoot, new Comparator<DefaultMutableTreeNode>() { |
| @Override |
| public int compare(final DefaultMutableTreeNode o1, final DefaultMutableTreeNode o2) { |
| final int idx1 = order.indexOf(((MyNode)o1).getDisplayName()); |
| final int idx2 = order.indexOf(((MyNode)o2).getDisplayName()); |
| return idx1 - idx2; |
| } |
| }); |
| |
| super.reset(); |
| } |
| |
| |
| @Override |
| public void apply() throws ConfigurationException { |
| final Set<MyNode> roots = new HashSet<MyNode>(); |
| roots.add(myRoot); |
| checkApply(roots, ProjectBundle.message("rename.message.prefix.scope"), ProjectBundle.message("rename.scope.title")); |
| super.apply(); |
| processScopes(); |
| |
| loadStateOrder(); |
| } |
| |
| @Override |
| protected void checkApply(Set<MyNode> rootNodes, String prefix, String title) throws ConfigurationException { |
| super.checkApply(rootNodes, prefix, title); |
| final Set<String> predefinedScopes = new HashSet<String>(); |
| for (CustomScopesProvider scopesProvider : myProject.getExtensions(CustomScopesProvider.CUSTOM_SCOPES_PROVIDER)) { |
| for (NamedScope namedScope : scopesProvider.getCustomScopes()) { |
| predefinedScopes.add(namedScope.getName()); |
| } |
| } |
| for (MyNode rootNode : rootNodes) { |
| for (int i = 0; i < rootNode.getChildCount(); i++) { |
| final MyNode node = (MyNode)rootNode.getChildAt(i); |
| final NamedConfigurable scopeConfigurable = node.getConfigurable(); |
| final String name = scopeConfigurable.getDisplayName(); |
| if (predefinedScopes.contains(name)) { |
| selectNodeInTree(node); |
| throw new ConfigurationException("Scope name equals to predefined one", ProjectBundle.message("rename.scope.title")); |
| } |
| } |
| } |
| } |
| |
| public ScopeChooserConfigurableState getScopesState() { |
| return (ScopeChooserConfigurableState)myState; |
| } |
| |
| @Override |
| public boolean isModified() { |
| final List<String> order = getScopesState().myOrder; |
| if (myRoot.getChildCount() != order.size()) return true; |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| final MyNode node = (MyNode)myRoot.getChildAt(i); |
| final ScopeConfigurable scopeConfigurable = (ScopeConfigurable)node.getConfigurable(); |
| final NamedScope namedScope = scopeConfigurable.getEditableObject(); |
| if (order.size() <= i) return true; |
| final String name = order.get(i); |
| if (!Comparing.strEqual(name, namedScope.getName())) return true; |
| if (isInitialized(scopeConfigurable)) { |
| final NamedScopesHolder holder = scopeConfigurable.getHolder(); |
| final NamedScope scope = holder.getScope(name); |
| if (scope == null) return true; |
| if (scopeConfigurable.isModified()) return true; |
| } |
| } |
| return false; |
| } |
| |
| private void processScopes() { |
| final List<NamedScope> localScopes = new ArrayList<NamedScope>(); |
| final List<NamedScope> sharedScopes = new ArrayList<NamedScope>(); |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| final MyNode node = (MyNode)myRoot.getChildAt(i); |
| final ScopeConfigurable scopeConfigurable = (ScopeConfigurable)node.getConfigurable(); |
| final NamedScope namedScope = scopeConfigurable.getScope(); |
| if (scopeConfigurable.getHolder() == myLocalScopesManager) { |
| localScopes.add(namedScope); |
| } |
| else { |
| sharedScopes.add(namedScope); |
| } |
| } |
| myLocalScopesManager.setScopes(localScopes.toArray(new NamedScope[localScopes.size()])); |
| mySharedScopesManager.setScopes(sharedScopes.toArray(new NamedScope[sharedScopes.size()])); |
| } |
| |
| private void loadStateOrder() { |
| final List<String> order = getScopesState().myOrder; |
| order.clear(); |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| order.add(((MyNode)myRoot.getChildAt(i)).getDisplayName()); |
| } |
| } |
| |
| private void loadScopes(final NamedScopesHolder holder) { |
| final NamedScope[] scopes = holder.getScopes(); |
| for (NamedScope scope : scopes) { |
| if (isPredefinedScope(scope)) continue; |
| myRoot.add(new MyNode(new ScopeConfigurable(scope, holder == mySharedScopesManager, myProject, TREE_UPDATER))); |
| } |
| } |
| |
| private boolean isPredefinedScope(final NamedScope scope) { |
| return getPredefinedScopes(myProject).contains(scope); |
| } |
| |
| private static Collection<NamedScope> getPredefinedScopes(Project project) { |
| final Collection<NamedScope> result = new ArrayList<NamedScope>(); |
| result.addAll(NamedScopeManager.getInstance(project).getPredefinedScopes()); |
| result.addAll(DependencyValidationManager.getInstance(project).getPredefinedScopes()); |
| return result; |
| } |
| |
| @Override |
| protected void initTree() { |
| myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| final TreePath path = e.getOldLeadSelectionPath(); |
| if (path != null) { |
| final MyNode node = (MyNode)path.getLastPathComponent(); |
| final NamedConfigurable namedConfigurable = node.getConfigurable(); |
| if (namedConfigurable instanceof ScopeConfigurable) { |
| ((ScopeConfigurable)namedConfigurable).cancelCurrentProgress(); |
| } |
| } |
| } |
| }); |
| super.initTree(); |
| myTree.setShowsRootHandles(false); |
| new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() { |
| @Override |
| public String convert(final TreePath treePath) { |
| return ((MyNode)treePath.getLastPathComponent()).getDisplayName(); |
| } |
| }, true); |
| |
| myTree.getEmptyText().setText(IdeBundle.message("scopes.no.scoped")); |
| } |
| |
| @Override |
| protected void processRemovedItems() { |
| //do nothing |
| } |
| |
| @Override |
| protected boolean wasObjectStored(Object editableObject) { |
| if (editableObject instanceof NamedScope) { |
| NamedScope scope = (NamedScope)editableObject; |
| final String scopeName = scope.getName(); |
| return myLocalScopesManager.getScope(scopeName) != null || mySharedScopesManager.getScope(scopeName) != null; |
| } |
| return false; |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return IdeBundle.message("scopes.display.name"); |
| } |
| |
| @Override |
| @NotNull |
| @NonNls |
| public String getHelpTopic() { |
| return PROJECT_SCOPES; //todo help id |
| } |
| |
| @Override |
| protected void updateSelection(@Nullable final NamedConfigurable configurable) { |
| super.updateSelection(configurable); |
| if (configurable instanceof ScopeConfigurable) { |
| ((ScopeConfigurable)configurable).restoreCanceledProgress(); |
| } |
| } |
| |
| @Override |
| protected |
| @Nullable |
| String getEmptySelectionString() { |
| return "Select a scope to view or edit its details here"; |
| } |
| |
| private String createUniqueName() { |
| String str = InspectionsBundle.message("inspection.profile.unnamed"); |
| final HashSet<String> treeScopes = new HashSet<String>(); |
| obtainCurrentScopes(treeScopes); |
| if (!treeScopes.contains(str)) return str; |
| int i = 1; |
| while (true) { |
| if (!treeScopes.contains(str + i)) return str + i; |
| i++; |
| } |
| } |
| |
| private void obtainCurrentScopes(final HashSet<String> scopes) { |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| final MyNode node = (MyNode)myRoot.getChildAt(i); |
| final NamedScope scope = (NamedScope)node.getConfigurable().getEditableObject(); |
| scopes.add(scope.getName()); |
| } |
| } |
| |
| private void addNewScope(final NamedScope scope, final boolean isLocal) { |
| final MyNode nodeToAdd = new MyNode(new ScopeConfigurable(scope, !isLocal, myProject, TREE_UPDATER)); |
| myRoot.add(nodeToAdd); |
| ((DefaultTreeModel)myTree.getModel()).reload(myRoot); |
| selectNodeInTree(nodeToAdd); |
| } |
| |
| private void createScope(final boolean isLocal, String title, final PackageSet set) { |
| final String newName = Messages.showInputDialog(myTree, IdeBundle.message("add.scope.name.label"), title, |
| Messages.getInformationIcon(), createUniqueName(), new InputValidator() { |
| @Override |
| public boolean checkInput(String inputString) { |
| final NamedScopesHolder holder = isLocal ? myLocalScopesManager : mySharedScopesManager; |
| for (NamedScope scope : holder.getPredefinedScopes()) { |
| if (Comparing.strEqual(scope.getName(), inputString.trim())) { |
| return false; |
| } |
| } |
| return inputString.trim().length() > 0; |
| } |
| |
| @Override |
| public boolean canClose(String inputString) { |
| return checkInput(inputString); |
| } |
| }); |
| if (newName != null) { |
| final NamedScope scope = new NamedScope(newName, set); |
| addNewScope(scope, isLocal); |
| } |
| } |
| |
| @Override |
| @NotNull |
| @NonNls |
| public String getId() { |
| return getHelpTopic(); |
| } |
| |
| @Override |
| @Nullable |
| public Runnable enableSearch(final String option) { |
| return null; |
| } |
| |
| private class MyAddAction extends ActionGroup implements ActionGroupWithPreselection { |
| |
| private AnAction[] myChildren; |
| private final boolean myFromPopup; |
| |
| public MyAddAction(boolean fromPopup) { |
| super(IdeBundle.message("add.scope.popup.title"), true); |
| myFromPopup = fromPopup; |
| final Presentation presentation = getTemplatePresentation(); |
| presentation.setIcon(IconUtil.getAddIcon()); |
| setShortcutSet(CommonShortcuts.INSERT); |
| } |
| |
| |
| @Override |
| public void update(AnActionEvent e) { |
| super.update(e); |
| if (myFromPopup) { |
| setPopup(false); |
| } |
| } |
| |
| @Override |
| @NotNull |
| public AnAction[] getChildren(@Nullable AnActionEvent e) { |
| if (myChildren == null) { |
| myChildren = new AnAction[2]; |
| myChildren[0] = new AnAction(IdeBundle.message("add.local.scope.action.text"), IdeBundle.message("add.local.scope.action.text"), |
| myLocalScopesManager.getIcon()) { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| createScope(true, IdeBundle.message("add.scope.dialog.title"), null); |
| } |
| }; |
| myChildren[1] = new AnAction(IdeBundle.message("add.shared.scope.action.text"), IdeBundle.message("add.shared.scope.action.text"), |
| mySharedScopesManager.getIcon()) { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| createScope(false, IdeBundle.message("add.scope.dialog.title"), null); |
| } |
| }; |
| } |
| if (myFromPopup) { |
| final AnAction action = myChildren[getDefaultIndex()]; |
| action.getTemplatePresentation().setIcon(IconUtil.getAddIcon()); |
| return new AnAction[]{action}; |
| } |
| return myChildren; |
| } |
| |
| @Override |
| public ActionGroup getActionGroup() { |
| return this; |
| } |
| |
| @Override |
| public int getDefaultIndex() { |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| final MyNode node = (MyNode)selectionPath.getLastPathComponent(); |
| Object editableObject = node.getConfigurable().getEditableObject(); |
| if (editableObject instanceof NamedScope) { |
| editableObject = ((MyNode)node.getParent()).getConfigurable().getEditableObject(); |
| } |
| if (editableObject instanceof NamedScopeManager) { |
| return 0; |
| } |
| else if (editableObject instanceof DependencyValidationManager) { |
| return 1; |
| } |
| } |
| return 0; |
| } |
| } |
| |
| |
| private class MyMoveAction extends AnAction { |
| private final int myDirection; |
| |
| protected MyMoveAction(String text, Icon icon, int direction) { |
| super(text, text, icon); |
| myDirection = direction; |
| } |
| |
| @Override |
| public void actionPerformed(final AnActionEvent e) { |
| TreeUtil.moveSelectedRow(myTree, myDirection); |
| } |
| |
| @Override |
| public void update(final AnActionEvent e) { |
| final Presentation presentation = e.getPresentation(); |
| presentation.setEnabled(false); |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| if (treeNode.getUserObject() instanceof ScopeConfigurable) { |
| if (myDirection < 0) { |
| presentation.setEnabled(treeNode.getPreviousSibling() != null); |
| } |
| else { |
| presentation.setEnabled(treeNode.getNextSibling() != null); |
| } |
| } |
| } |
| } |
| } |
| |
| private class MyCopyAction extends AnAction { |
| public MyCopyAction() { |
| super(ExecutionBundle.message("copy.configuration.action.name"), ExecutionBundle.message("copy.configuration.action.name"), |
| COPY_ICON); |
| registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK)), myTree); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| NamedScope scope = (NamedScope)getSelectedObject(); |
| if (scope != null) { |
| final NamedScope newScope = scope.createCopy(); |
| final ScopeConfigurable configurable = |
| (ScopeConfigurable)((MyNode)myTree.getSelectionPath().getLastPathComponent()).getConfigurable(); |
| addNewScope(new NamedScope(createUniqueName(), newScope.getValue()), configurable.getHolder() == myLocalScopesManager); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| e.getPresentation().setEnabled(getSelectedObject() instanceof NamedScope); |
| } |
| } |
| |
| private class MySaveAsAction extends AnAction { |
| public MySaveAsAction() { |
| super(ExecutionBundle.message("action.name.save.as.configuration"), ExecutionBundle.message("action.name.save.as.configuration"), |
| AllIcons.Actions.Menu_saveall); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| final MyNode node = (MyNode)selectionPath.getLastPathComponent(); |
| final NamedConfigurable configurable = node.getConfigurable(); |
| if (configurable instanceof ScopeConfigurable) { |
| final ScopeConfigurable scopeConfigurable = (ScopeConfigurable)configurable; |
| PackageSet set = scopeConfigurable.getEditableObject().getValue(); |
| if (set != null) { |
| if (scopeConfigurable.getHolder() == mySharedScopesManager) { |
| createScope(false, IdeBundle.message("scopes.save.dialog.title.shared"), set.createCopy()); |
| } |
| else { |
| createScope(true, IdeBundle.message("scopes.save.dialog.title.local"), set.createCopy()); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| e.getPresentation().setEnabled(getSelectedObject() instanceof NamedScope); |
| } |
| } |
| |
| public static class ScopeChooserConfigurableState extends MasterDetailsState { |
| @Tag("order") |
| @AbstractCollection(surroundWithTag = false, elementTag = "scope", elementValueAttribute = "name") |
| public List<String> myOrder = new ArrayList<String>(); |
| } |
| } |