| /* |
| * 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.execution.impl; |
| |
| import com.intellij.execution.*; |
| import com.intellij.execution.configuration.ConfigurationFactoryEx; |
| import com.intellij.execution.configurations.*; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.options.*; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.ui.Splitter; |
| import com.intellij.openapi.ui.popup.JBPopupFactory; |
| import com.intellij.openapi.ui.popup.ListPopup; |
| import com.intellij.openapi.ui.popup.ListPopupStep; |
| import com.intellij.openapi.ui.popup.PopupStep; |
| import com.intellij.openapi.ui.popup.util.BaseListPopupStep; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.Trinity; |
| import com.intellij.openapi.wm.IdeFocusManager; |
| import com.intellij.ui.*; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.ui.components.labels.ActionLink; |
| import com.intellij.ui.treeStructure.Tree; |
| import com.intellij.util.ArrayUtilRt; |
| import com.intellij.util.IconUtil; |
| import com.intellij.util.PlatformIcons; |
| import com.intellij.util.config.StorageAccessors; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.Convertor; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.ui.EditableModel; |
| import com.intellij.util.ui.EmptyIcon; |
| import com.intellij.util.ui.GridBag; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.util.ui.tree.TreeUtil; |
| import gnu.trove.THashSet; |
| import net.miginfocom.swing.MigLayout; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.border.EmptyBorder; |
| import javax.swing.event.*; |
| import javax.swing.tree.*; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.KeyEvent; |
| import java.util.*; |
| import java.util.List; |
| |
| import static com.intellij.execution.impl.RunConfigurable.NodeKind.*; |
| import static com.intellij.ui.RowsDnDSupport.RefinedDropSupport.Position.*; |
| |
| class RunConfigurable extends BaseConfigurable { |
| |
| private static final Icon ADD_ICON = IconUtil.getAddIcon(); |
| private static final Icon REMOVE_ICON = IconUtil.getRemoveIcon(); |
| private static final Icon SHARED_ICON = AllIcons.Nodes.Shared; |
| private static final Icon NON_SHARED_ICON = EmptyIcon.ICON_16; |
| @NonNls private static final String DIVIDER_PROPORTION = "dividerProportion"; |
| @NonNls private static final Object DEFAULTS = new Object() { |
| @Override |
| public String toString() { |
| return "Defaults"; |
| } |
| }; |
| |
| private volatile boolean isDisposed = false; |
| |
| private final Project myProject; |
| private RunDialogBase myRunDialog; |
| @NonNls final DefaultMutableTreeNode myRoot = new DefaultMutableTreeNode("Root"); |
| final MyTreeModel myTreeModel = new MyTreeModel(myRoot); |
| final Tree myTree = new Tree(myTreeModel); |
| private final JPanel myRightPanel = new JPanel(new BorderLayout()); |
| private final Splitter mySplitter = new Splitter(false); |
| private JPanel myWholePanel; |
| private final StorageAccessors myProperties = StorageAccessors.createGlobal("RunConfigurable"); |
| private Configurable mySelectedConfigurable = null; |
| private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.RunConfigurable"); |
| private final JTextField myRecentsLimit = new JTextField("5", 2); |
| private final JCheckBox myConfirmation = new JCheckBox(ExecutionBundle.message("rerun.confirmation.checkbox"), true); |
| private final List<Pair<UnnamedConfigurable, JComponent>> myAdditionalSettings = new ArrayList<Pair<UnnamedConfigurable, JComponent>>(); |
| private Map<ConfigurationFactory, Configurable> myStoredComponents = new HashMap<ConfigurationFactory, Configurable>(); |
| private ToolbarDecorator myToolbarDecorator; |
| private boolean isFolderCreating; |
| private RunConfigurable.MyToolbarAddAction myAddAction = new MyToolbarAddAction(); |
| |
| public RunConfigurable(final Project project) { |
| this(project, null); |
| } |
| |
| public RunConfigurable(final Project project, @Nullable final RunDialogBase runDialog) { |
| myProject = project; |
| myRunDialog = runDialog; |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return ExecutionBundle.message("run.configurable.display.name"); |
| } |
| |
| private void initTree() { |
| myTree.setRootVisible(false); |
| myTree.setShowsRootHandles(true); |
| UIUtil.setLineStyleAngled(myTree); |
| TreeUtil.installActions(myTree); |
| new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() { |
| @Override |
| public String convert(TreePath o) { |
| DefaultMutableTreeNode node = (DefaultMutableTreeNode)o.getLastPathComponent(); |
| final Object userObject = node.getUserObject(); |
| if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| return ((RunnerAndConfigurationSettingsImpl)userObject).getName(); |
| } |
| else if (userObject instanceof SingleConfigurationConfigurable) { |
| return ((SingleConfigurationConfigurable)userObject).getNameText(); |
| } |
| else { |
| if (userObject instanceof ConfigurationType) { |
| return ((ConfigurationType)userObject).getDisplayName(); |
| } |
| else if (userObject instanceof String) { |
| return (String)userObject; |
| } |
| } |
| return o.toString(); |
| } |
| }); |
| myTree.setCellRenderer(new ColoredTreeCellRenderer() { |
| @Override |
| public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, |
| boolean hasFocus) { |
| if (value instanceof DefaultMutableTreeNode) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; |
| final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent(); |
| final Object userObject = node.getUserObject(); |
| Boolean shared = null; |
| final String name = RunConfigurable.getName(userObject); |
| if (userObject instanceof ConfigurationType) { |
| final ConfigurationType configurationType = (ConfigurationType)userObject; |
| append(name, parent.isRoot() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| setIcon(configurationType.getIcon()); |
| } |
| else if (userObject == DEFAULTS) { |
| append(name, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); |
| setIcon(AllIcons.General.Settings); |
| } |
| else if (userObject instanceof String) {//Folders |
| append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| setIcon(AllIcons.Nodes.Folder); |
| } |
| else if (userObject instanceof ConfigurationFactory) { |
| append(name); |
| setIcon(((ConfigurationFactory)userObject).getIcon()); |
| } |
| else { |
| final RunManagerImpl runManager = getRunManager(); |
| RunnerAndConfigurationSettings configuration = null; |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| final SingleConfigurationConfigurable<?> settings = (SingleConfigurationConfigurable)userObject; |
| RunnerAndConfigurationSettings configurationSettings; |
| configurationSettings = settings.getSettings(); |
| configuration = configurationSettings; |
| shared = settings.isStoreProjectConfiguration(); |
| setIcon(ProgramRunnerUtil.getConfigurationIcon(configurationSettings, !settings.isValid())); |
| } |
| else if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| RunnerAndConfigurationSettings settings = (RunnerAndConfigurationSettings)userObject; |
| shared = runManager.isConfigurationShared(settings); |
| setIcon(RunManagerEx.getInstanceEx(myProject).getConfigurationIcon(settings)); |
| configuration = settings; |
| } |
| if (configuration != null) { |
| append(name, configuration.isTemporary() |
| ? SimpleTextAttributes.GRAY_ATTRIBUTES |
| : SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| } |
| } |
| if (shared != null) { |
| Icon icon = getIcon(); |
| LayeredIcon layeredIcon = new LayeredIcon(icon, shared ? SHARED_ICON : NON_SHARED_ICON); |
| setIcon(layeredIcon); |
| setIconTextGap(0); |
| } else { |
| setIconTextGap(2); |
| } |
| } |
| } |
| }); |
| final RunManagerEx manager = getRunManager(); |
| final ConfigurationType[] factories = manager.getConfigurationFactories(); |
| for (ConfigurationType type : factories) { |
| final List<RunnerAndConfigurationSettings> configurations = manager.getConfigurationSettingsList(type); |
| if (!configurations.isEmpty()) { |
| final DefaultMutableTreeNode typeNode = new DefaultMutableTreeNode(type); |
| myRoot.add(typeNode); |
| Map<String, DefaultMutableTreeNode> folderMapping = new HashMap<String, DefaultMutableTreeNode>(); |
| int folderCounter = 0; |
| for (RunnerAndConfigurationSettings configuration : configurations) { |
| String folder = configuration.getFolderName(); |
| if (folder != null) { |
| DefaultMutableTreeNode node = folderMapping.get(folder); |
| if (node == null) { |
| node = new DefaultMutableTreeNode(folder); |
| typeNode.insert(node, folderCounter); |
| folderCounter++; |
| folderMapping.put(folder, node); |
| } |
| node.add(new DefaultMutableTreeNode(configuration)); |
| } else { |
| typeNode.add(new DefaultMutableTreeNode(configuration)); |
| } |
| } |
| } |
| } |
| |
| // add defaults |
| final DefaultMutableTreeNode defaults = new DefaultMutableTreeNode(DEFAULTS); |
| final ConfigurationType[] configurationTypes = RunManagerImpl.getInstanceImpl(myProject).getConfigurationFactories(); |
| for (final ConfigurationType type : configurationTypes) { |
| if (!(type instanceof UnknownConfigurationType)) { |
| ConfigurationFactory[] configurationFactories = type.getConfigurationFactories(); |
| DefaultMutableTreeNode typeNode = new DefaultMutableTreeNode(type); |
| defaults.add(typeNode); |
| if (configurationFactories.length != 1) { |
| for (ConfigurationFactory factory : configurationFactories) { |
| typeNode.add(new DefaultMutableTreeNode(factory)); |
| } |
| } |
| } |
| } |
| if (defaults.getChildCount() > 0) myRoot.add(defaults); |
| |
| myTree.addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| DefaultMutableTreeNode node = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| final Object userObject = getSafeUserObject(node); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| updateRightPanel((SingleConfigurationConfigurable<RunConfiguration>)userObject); |
| } |
| else if (userObject instanceof String) { |
| showFolderField(getSelectedConfigurationType(), node, (String)userObject); |
| } |
| else { |
| if (userObject instanceof ConfigurationType || userObject == DEFAULTS) { |
| final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent(); |
| if (parent.isRoot()) { |
| drawPressAddButtonMessage(userObject == DEFAULTS ? null : (ConfigurationType)userObject); |
| } |
| else { |
| final ConfigurationType type = (ConfigurationType)userObject; |
| ConfigurationFactory[] factories = type.getConfigurationFactories(); |
| if (factories.length == 1) { |
| final ConfigurationFactory factory = factories[0]; |
| showTemplateConfigurable(factory); |
| } |
| else { |
| drawPressAddButtonMessage((ConfigurationType)userObject); |
| } |
| } |
| } |
| else if (userObject instanceof ConfigurationFactory) { |
| showTemplateConfigurable((ConfigurationFactory)userObject); |
| } |
| } |
| } |
| updateDialog(); |
| } |
| }); |
| myTree.registerKeyboardAction(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| clickDefaultButton(); |
| } |
| }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED); |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| if (isDisposed) return; |
| |
| myTree.requestFocusInWindow(); |
| final RunnerAndConfigurationSettings settings = manager.getSelectedConfiguration(); |
| if (settings != null) { |
| final Enumeration enumeration = myRoot.breadthFirstEnumeration(); |
| while (enumeration.hasMoreElements()) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)enumeration.nextElement(); |
| final Object userObject = node.getUserObject(); |
| if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| final RunnerAndConfigurationSettings runnerAndConfigurationSettings = (RunnerAndConfigurationSettings)userObject; |
| final ConfigurationType configurationType = settings.getType(); |
| if (configurationType != null && |
| Comparing.strEqual(runnerAndConfigurationSettings.getConfiguration().getType().getId(), configurationType.getId()) && |
| Comparing.strEqual(runnerAndConfigurationSettings.getConfiguration().getName(), settings.getName())) { |
| TreeUtil.selectInTree(node, true, myTree); |
| return; |
| } |
| } |
| } |
| } |
| else { |
| mySelectedConfigurable = null; |
| } |
| //TreeUtil.selectInTree(defaults, true, myTree); |
| drawPressAddButtonMessage(null); |
| } |
| }); |
| sortTopLevelBranches(); |
| ((DefaultTreeModel)myTree.getModel()).reload(); |
| } |
| |
| private void showTemplateConfigurable(ConfigurationFactory factory) { |
| Configurable configurable = myStoredComponents.get(factory); |
| if (configurable == null){ |
| configurable = new TemplateConfigurable(RunManagerImpl.getInstanceImpl(myProject).getConfigurationTemplate(factory)); |
| myStoredComponents.put(factory, configurable); |
| configurable.reset(); |
| } |
| updateRightPanel(configurable); |
| } |
| |
| private void showFolderField(final ConfigurationType type, final DefaultMutableTreeNode node, final String folderName) { |
| myRightPanel.removeAll(); |
| JPanel p = new JPanel(new MigLayout("ins " + myToolbarDecorator.getActionsPanel().getHeight() + " 5 0 0, flowx")); |
| final JTextField textField = new JTextField(folderName); |
| textField.getDocument().addDocumentListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| node.setUserObject(textField.getText()); |
| myTreeModel.reload(node); |
| } |
| }); |
| p.add(new JLabel("Folder name:"), "gapright 5"); |
| p.add(textField, "pushx, growx, wrap"); |
| p.add(new JLabel(ExecutionBundle.message("run.configuration.rename.folder.disclaimer")), "gaptop 5, spanx 2"); |
| |
| myRightPanel.add(p); |
| myRightPanel.revalidate(); |
| myRightPanel.repaint(); |
| if (isFolderCreating) { |
| textField.selectAll(); |
| textField.requestFocus(); |
| } |
| } |
| |
| private Object getSafeUserObject(DefaultMutableTreeNode node) { |
| Object userObject = node.getUserObject(); |
| if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| final SingleConfigurationConfigurable<RunConfiguration> configurationConfigurable = |
| SingleConfigurationConfigurable.editSettings((RunnerAndConfigurationSettings)userObject, null); |
| installUpdateListeners(configurationConfigurable); |
| node.setUserObject(configurationConfigurable); |
| return configurationConfigurable; |
| } |
| return userObject; |
| } |
| |
| public void setRunDialog(final RunDialogBase runDialog) { |
| myRunDialog = runDialog; |
| } |
| |
| private void updateRightPanel(final Configurable configurable) { |
| myRightPanel.removeAll(); |
| mySelectedConfigurable = configurable; |
| |
| final JBScrollPane scrollPane = new JBScrollPane(configurable.createComponent()); |
| scrollPane.setBorder(null); |
| myRightPanel.add(scrollPane, BorderLayout.CENTER); |
| if (configurable instanceof SingleConfigurationConfigurable) { |
| myRightPanel.add(((SingleConfigurationConfigurable)configurable).getValidationComponent(), BorderLayout.SOUTH); |
| } |
| |
| setupDialogBounds(); |
| } |
| |
| private void sortTopLevelBranches() { |
| List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(myTree); |
| TreeUtil.sort(myRoot, new Comparator() { |
| @Override |
| public int compare(final Object o1, final Object o2) { |
| final Object userObject1 = ((DefaultMutableTreeNode)o1).getUserObject(); |
| final Object userObject2 = ((DefaultMutableTreeNode)o2).getUserObject(); |
| if (userObject1 instanceof ConfigurationType && userObject2 instanceof ConfigurationType) { |
| return ((ConfigurationType)userObject1).getDisplayName().compareTo(((ConfigurationType)userObject2).getDisplayName()); |
| } |
| else if (userObject1 == DEFAULTS && userObject2 instanceof ConfigurationType) { |
| return 1; |
| } |
| else if (userObject2 == DEFAULTS && userObject1 instanceof ConfigurationType) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| }); |
| TreeUtil.restoreExpandedPaths(myTree, expandedPaths); |
| } |
| |
| private void update() { |
| updateDialog(); |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| myTreeModel.reload(node); |
| } |
| } |
| |
| private void installUpdateListeners(final SingleConfigurationConfigurable<RunConfiguration> info) { |
| final boolean[] changed = new boolean[]{false}; |
| info.getEditor().addSettingsEditorListener(new SettingsEditorListener<RunnerAndConfigurationSettings>() { |
| @Override |
| public void stateChanged(final SettingsEditor<RunnerAndConfigurationSettings> editor) { |
| update(); |
| final RunConfiguration configuration = info.getConfiguration(); |
| if (configuration instanceof LocatableConfiguration) { |
| final LocatableConfiguration runtimeConfiguration = (LocatableConfiguration)configuration; |
| if (runtimeConfiguration.isGeneratedName() && !changed[0]) { |
| try { |
| final LocatableConfiguration snapshot = (LocatableConfiguration)editor.getSnapshot().getConfiguration(); |
| final String generatedName = snapshot.suggestedName(); |
| if (generatedName != null && generatedName.length() > 0) { |
| info.setNameText(generatedName); |
| changed[0] = false; |
| } |
| } |
| catch (ConfigurationException ignore) { |
| } |
| } |
| } |
| setupDialogBounds(); |
| } |
| }); |
| |
| info.addNameListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| changed[0] = true; |
| update(); |
| } |
| }); |
| |
| info.addSharedListener(new ChangeListener() { |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| changed[0] = true; |
| update(); |
| } |
| }); |
| } |
| |
| private void drawPressAddButtonMessage(final ConfigurationType configurationType) { |
| JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); |
| panel.setBorder(new EmptyBorder(30, 0, 0, 0)); |
| panel.add(new JLabel("Press the")); |
| |
| ActionLink addIcon = new ActionLink("", ADD_ICON, myAddAction); |
| addIcon.setBorder(new EmptyBorder(0, 0, 0, 5)); |
| panel.add(addIcon); |
| |
| final String configurationTypeDescription = configurationType != null |
| ? configurationType.getConfigurationTypeDescription() |
| : ExecutionBundle.message("run.configuration.default.type.description"); |
| panel.add(new JLabel(ExecutionBundle.message("empty.run.configuration.panel.text.label3", configurationTypeDescription))); |
| JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(panel, true); |
| |
| myRightPanel.removeAll(); |
| myRightPanel.add(scrollPane, BorderLayout.CENTER); |
| if (configurationType == null) { |
| JPanel settingsPanel = new JPanel(new GridBagLayout()); |
| GridBag grid = new GridBag().setDefaultAnchor(GridBagConstraints.NORTHWEST); |
| |
| for (Pair<UnnamedConfigurable, JComponent> each : myAdditionalSettings) { |
| settingsPanel.add(each.second, grid.nextLine().next()); |
| } |
| settingsPanel.add(createSettingsPanel(), grid.nextLine().next()); |
| |
| JPanel wrapper = new JPanel(new BorderLayout()); |
| wrapper.add(settingsPanel, BorderLayout.WEST); |
| wrapper.add(Box.createGlue(), BorderLayout.CENTER); |
| |
| myRightPanel.add(wrapper, BorderLayout.SOUTH); |
| } |
| myRightPanel.revalidate(); |
| myRightPanel.repaint(); |
| } |
| |
| private static String getName(Object userObject) { |
| if (userObject instanceof ConfigurationType) { |
| return ((ConfigurationType)userObject).getDisplayName(); |
| } |
| if (userObject == DEFAULTS) { |
| return "Defaults"; |
| } |
| if (userObject instanceof ConfigurationFactory) { |
| return ((ConfigurationFactory)userObject).getName(); |
| } |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| return ((SingleConfigurationConfigurable)userObject).getNameText(); |
| } |
| if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| return ((RunnerAndConfigurationSettingsImpl)userObject).getName(); |
| } |
| return String.valueOf(userObject);//Folder objects are strings |
| } |
| |
| private JPanel createLeftPanel() { |
| initTree(); |
| MyRemoveAction removeAction = new MyRemoveAction(); |
| MyMoveAction moveUpAction = new MyMoveAction(ExecutionBundle.message("move.up.action.name"), null, IconUtil.getMoveUpIcon(), -1); |
| MyMoveAction moveDownAction = new MyMoveAction(ExecutionBundle.message("move.down.action.name"), null, IconUtil.getMoveDownIcon(), 1); |
| myToolbarDecorator = ToolbarDecorator.createDecorator(myTree).setAsUsualTopToolbar() |
| .setAddAction(myAddAction).setAddActionName(ExecutionBundle.message("add.new.run.configuration.acrtion.name")) |
| .setRemoveAction(removeAction).setRemoveActionUpdater(removeAction) |
| .setRemoveActionName(ExecutionBundle.message("remove.run.configuration.action.name")) |
| .setMoveUpAction(moveUpAction).setMoveUpActionName(ExecutionBundle.message("move.up.action.name")).setMoveUpActionUpdater( |
| moveUpAction) |
| .setMoveDownAction(moveDownAction).setMoveDownActionName(ExecutionBundle.message("move.down.action.name")).setMoveDownActionUpdater( |
| moveDownAction) |
| .addExtraAction(AnActionButton.fromAction(new MyCopyAction())) |
| .addExtraAction(AnActionButton.fromAction(new MySaveAction())) |
| .addExtraAction(AnActionButton.fromAction(new MyEditDefaultsAction())) |
| .addExtraAction(AnActionButton.fromAction(new MyCreateFolderAction())) |
| .addExtraAction(AnActionButton.fromAction(new MySortFolderAction())) |
| .setButtonComparator(ExecutionBundle.message("add.new.run.configuration.acrtion.name"), |
| ExecutionBundle.message("remove.run.configuration.action.name"), |
| ExecutionBundle.message("copy.configuration.action.name"), |
| ExecutionBundle.message("action.name.save.configuration"), |
| ExecutionBundle.message("run.configuration.edit.default.configuration.settings.text"), |
| ExecutionBundle.message("move.up.action.name"), |
| ExecutionBundle.message("move.down.action.name"), |
| ExecutionBundle.message("run.configuration.create.folder.text") |
| ).setForcedDnD(); |
| return myToolbarDecorator.createPanel(); |
| } |
| |
| private JPanel createSettingsPanel() { |
| JPanel bottomPanel = new JPanel(new GridBagLayout()); |
| GridBag g = new GridBag(); |
| |
| bottomPanel.add(myConfirmation, g.nextLine().coverLine()); |
| bottomPanel.add(new JLabel("Temporary configurations limit:"), g.nextLine().next()); |
| bottomPanel.add(myRecentsLimit, g.next().anchor(GridBagConstraints.WEST)); |
| |
| myRecentsLimit.getDocument().addDocumentListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| setModified(true); |
| } |
| }); |
| myConfirmation.addChangeListener(new ChangeListener() { |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| setModified(true); |
| } |
| }); |
| return bottomPanel; |
| } |
| |
| @Nullable |
| private ConfigurationType getSelectedConfigurationType() { |
| final DefaultMutableTreeNode configurationTypeNode = getSelectedConfigurationTypeNode(); |
| return configurationTypeNode != null ? (ConfigurationType)configurationTypeNode.getUserObject() : null; |
| } |
| |
| @Override |
| public JComponent createComponent() { |
| for (RunConfigurationsSettings each : Extensions.getExtensions(RunConfigurationsSettings.EXTENSION_POINT, myProject)) { |
| UnnamedConfigurable configurable = each.createConfigurable(); |
| myAdditionalSettings.add(Pair.create(configurable, configurable.createComponent())); |
| } |
| |
| myWholePanel = new JPanel(new BorderLayout()); |
| mySplitter.setFirstComponent(createLeftPanel()); |
| mySplitter.setSecondComponent(myRightPanel); |
| myWholePanel.add(mySplitter, BorderLayout.CENTER); |
| |
| updateDialog(); |
| |
| Dimension d = myWholePanel.getPreferredSize(); |
| d.width = Math.max(d.width, 800); |
| d.height = Math.max(d.height, 600); |
| myWholePanel.setPreferredSize(d); |
| |
| mySplitter.setProportion(myProperties.getFloat(DIVIDER_PROPORTION, 0.3f)); |
| |
| return myWholePanel; |
| } |
| |
| @Override |
| public void reset() { |
| final RunManagerEx manager = getRunManager(); |
| final RunManagerConfig config = manager.getConfig(); |
| myRecentsLimit.setText(Integer.toString(config.getRecentsLimit())); |
| myConfirmation.setSelected(config.isRestartRequiresConfirmation()); |
| |
| for (Pair<UnnamedConfigurable, JComponent> each : myAdditionalSettings) { |
| each.first.reset(); |
| } |
| |
| setModified(false); |
| } |
| |
| public Configurable getSelectedConfigurable() { |
| return mySelectedConfigurable; |
| } |
| |
| @Override |
| public void apply() throws ConfigurationException { |
| updateActiveConfigurationFromSelected(); |
| |
| final RunManagerImpl manager = getRunManager(); |
| final ConfigurationType[] types = manager.getConfigurationFactories(); |
| List<ConfigurationType> configurationTypes = new ArrayList<ConfigurationType>(); |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)myRoot.getChildAt(i); |
| Object userObject = node.getUserObject(); |
| if (userObject instanceof ConfigurationType) { |
| configurationTypes.add((ConfigurationType)userObject); |
| } |
| } |
| for (ConfigurationType type : types) { |
| if (!configurationTypes.contains(type)) |
| configurationTypes.add(type); |
| } |
| |
| for (ConfigurationType configurationType : configurationTypes) { |
| applyByType(configurationType); |
| } |
| |
| try { |
| int i = Math.max(RunManagerConfig.MIN_RECENT_LIMIT, Integer.parseInt(myRecentsLimit.getText())); |
| int oldLimit = manager.getConfig().getRecentsLimit(); |
| if (oldLimit != i) { |
| manager.getConfig().setRecentsLimit(i); |
| manager.checkRecentsLimit(); |
| } |
| } |
| catch (NumberFormatException e) { |
| // ignore |
| } |
| manager.getConfig().setRestartRequiresConfirmation(myConfirmation.isSelected()); |
| |
| for (Configurable configurable : myStoredComponents.values()) { |
| if (configurable.isModified()){ |
| configurable.apply(); |
| } |
| } |
| |
| for (Pair<UnnamedConfigurable, JComponent> each : myAdditionalSettings) { |
| each.first.apply(); |
| } |
| |
| manager.saveOrder(); |
| setModified(false); |
| myTree.repaint(); |
| } |
| |
| protected void updateActiveConfigurationFromSelected() { |
| if (mySelectedConfigurable != null && mySelectedConfigurable instanceof SingleConfigurationConfigurable) { |
| RunnerAndConfigurationSettings settings = |
| (RunnerAndConfigurationSettings)((SingleConfigurationConfigurable)mySelectedConfigurable).getSettings(); |
| |
| getRunManager().setSelectedConfiguration(settings); |
| } |
| } |
| |
| private void applyByType(@NotNull ConfigurationType type) throws ConfigurationException { |
| RunnerAndConfigurationSettings selectedSettings = getSelectedSettings(); |
| int indexToMove = -1; |
| |
| DefaultMutableTreeNode typeNode = getConfigurationTypeNode(type); |
| final RunManagerImpl manager = getRunManager(); |
| final ArrayList<RunConfigurationBean> stableConfigurations = new ArrayList<RunConfigurationBean>(); |
| if (typeNode != null) { |
| final Set<String> names = new HashSet<String>(); |
| List<DefaultMutableTreeNode> configurationNodes = new ArrayList<DefaultMutableTreeNode>(); |
| collectNodesRecursively(typeNode, configurationNodes, CONFIGURATION, TEMPORARY_CONFIGURATION); |
| for (DefaultMutableTreeNode node : configurationNodes) { |
| final Object userObject = node.getUserObject(); |
| RunConfigurationBean configurationBean = null; |
| RunnerAndConfigurationSettings settings = null; |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| final SingleConfigurationConfigurable configurable = (SingleConfigurationConfigurable)userObject; |
| settings = (RunnerAndConfigurationSettings)configurable.getSettings(); |
| if (settings.isTemporary()) { |
| applyConfiguration(typeNode, configurable); |
| } |
| configurationBean = new RunConfigurationBean(configurable); |
| } |
| else if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| settings = (RunnerAndConfigurationSettings)userObject; |
| configurationBean = new RunConfigurationBean(settings, |
| manager.isConfigurationShared(settings), |
| manager.getBeforeRunTasks(settings.getConfiguration())); |
| |
| } |
| if (configurationBean != null) { |
| final SingleConfigurationConfigurable configurable = configurationBean.getConfigurable(); |
| final String nameText = configurable != null ? configurable.getNameText() : configurationBean.getSettings().getName(); |
| if (!names.add(nameText)) { |
| TreeUtil.selectNode(myTree, node); |
| throw new ConfigurationException(type.getDisplayName() + " with name \'" + nameText + "\' already exists"); |
| } |
| stableConfigurations.add(configurationBean); |
| if (settings == selectedSettings) { |
| indexToMove = stableConfigurations.size()-1; |
| } |
| } |
| } |
| List<DefaultMutableTreeNode> folderNodes = new ArrayList<DefaultMutableTreeNode>(); |
| collectNodesRecursively(typeNode, folderNodes, FOLDER); |
| names.clear(); |
| for (DefaultMutableTreeNode node : folderNodes) { |
| String folderName = (String)node.getUserObject(); |
| if (folderName.isEmpty()) { |
| TreeUtil.selectNode(myTree, node); |
| throw new ConfigurationException("Folder name shouldn't be empty"); |
| } |
| if (!names.add(folderName)) { |
| TreeUtil.selectNode(myTree, node); |
| throw new ConfigurationException("Folders name \'" + folderName + "\' is duplicated"); |
| } |
| } |
| } |
| // try to apply all |
| for (RunConfigurationBean bean : stableConfigurations) { |
| final SingleConfigurationConfigurable configurable = bean.getConfigurable(); |
| if (configurable != null) { |
| applyConfiguration(typeNode, configurable); |
| } |
| } |
| |
| // if apply succeeded, update the list of configurations in RunManager |
| Set<RunnerAndConfigurationSettings> toDeleteSettings = new THashSet<RunnerAndConfigurationSettings>(); |
| for (RunConfiguration each : manager.getConfigurationsList(type)) { |
| ContainerUtil.addIfNotNull(toDeleteSettings, manager.getSettings(each)); |
| } |
| |
| //Just saved as 'stable' configuration shouldn't stay between temporary ones (here we order model to save) |
| int shift = 0; |
| if (selectedSettings != null && selectedSettings.getType() == type) { |
| shift = adjustOrder(); |
| } |
| if (shift != 0 && indexToMove != -1) { |
| stableConfigurations.add(indexToMove-shift, stableConfigurations.remove(indexToMove)); |
| } |
| for (RunConfigurationBean each : stableConfigurations) { |
| toDeleteSettings.remove(each.getSettings()); |
| manager.addConfiguration(each.getSettings(), each.isShared(), each.getStepsBeforeLaunch(), false); |
| } |
| |
| for (RunnerAndConfigurationSettings each : toDeleteSettings) { |
| manager.removeConfiguration(each); |
| } |
| } |
| |
| static void collectNodesRecursively(DefaultMutableTreeNode parentNode, List<DefaultMutableTreeNode> nodes, NodeKind... allowed) { |
| for (int i = 0; i < parentNode.getChildCount(); i++) { |
| DefaultMutableTreeNode child = (DefaultMutableTreeNode)parentNode.getChildAt(i); |
| if (ArrayUtilRt.find(allowed, getKind(child)) != -1) { |
| nodes.add(child); |
| } |
| collectNodesRecursively(child, nodes, allowed); |
| } |
| } |
| |
| @Nullable |
| private DefaultMutableTreeNode getConfigurationTypeNode(@NotNull final ConfigurationType type) { |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)myRoot.getChildAt(i); |
| if (node.getUserObject() == type) return node; |
| } |
| return null; |
| } |
| |
| private void applyConfiguration(DefaultMutableTreeNode typeNode, SingleConfigurationConfigurable<?> configurable) throws ConfigurationException { |
| try { |
| if (configurable != null) { |
| configurable.apply(); |
| RunManagerImpl.getInstanceImpl(myProject).fireRunConfigurationChanged(configurable.getSettings()); |
| } |
| } |
| catch (ConfigurationException e) { |
| for (int i = 0; i < typeNode.getChildCount(); i++) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)typeNode.getChildAt(i); |
| if (Comparing.equal(configurable, node.getUserObject())) { |
| TreeUtil.selectNode(myTree, node); |
| break; |
| } |
| } |
| throw e; |
| } |
| } |
| |
| @Override |
| public boolean isModified() { |
| if (super.isModified()) return true; |
| final RunManagerImpl runManager = getRunManager(); |
| final List<RunConfiguration> allConfigurations = runManager.getAllConfigurationsList(); |
| final List<RunConfiguration> currentConfigurations = new ArrayList<RunConfiguration>(); |
| for (int i = 0; i < myRoot.getChildCount(); i++) { |
| DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode)myRoot.getChildAt(i); |
| final Object object = typeNode.getUserObject(); |
| if (object instanceof ConfigurationType) { |
| final List<RunnerAndConfigurationSettings> configurationSettings = runManager.getConfigurationSettingsList( |
| (ConfigurationType)object); |
| List<DefaultMutableTreeNode> configurationNodes = new ArrayList<DefaultMutableTreeNode>(); |
| collectNodesRecursively(typeNode, configurationNodes, CONFIGURATION, TEMPORARY_CONFIGURATION); |
| if (configurationSettings.size() != configurationNodes.size()) return true; |
| for (int j = 0; j < configurationNodes.size(); j++) { |
| DefaultMutableTreeNode configurationNode = configurationNodes.get(j); |
| final Object userObject = configurationNode.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| SingleConfigurationConfigurable configurable = (SingleConfigurationConfigurable)userObject; |
| if (!Comparing.strEqual(configurationSettings.get(j).getConfiguration().getName(), configurable.getConfiguration().getName())) { |
| return true; |
| } |
| if (configurable.isModified()) return true; |
| currentConfigurations.add(configurable.getConfiguration()); |
| } |
| else if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| currentConfigurations.add(((RunnerAndConfigurationSettings)userObject).getConfiguration()); |
| } |
| } |
| } |
| } |
| if (allConfigurations.size() != currentConfigurations.size() || !allConfigurations.containsAll(currentConfigurations)) return true; |
| |
| for (Configurable configurable : myStoredComponents.values()) { |
| if (configurable.isModified()) return true; |
| } |
| |
| for (Pair<UnnamedConfigurable, JComponent> each : myAdditionalSettings) { |
| if (each.first.isModified()) return true; |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public void disposeUIResources() { |
| isDisposed = true; |
| for (Configurable configurable : myStoredComponents.values()) { |
| configurable.disposeUIResources(); |
| } |
| myStoredComponents.clear(); |
| |
| for (Pair<UnnamedConfigurable, JComponent> each : myAdditionalSettings) { |
| each.first.disposeUIResources(); |
| } |
| |
| TreeUtil.traverseDepth(myRoot, new TreeUtil.Traverse() { |
| @Override |
| public boolean accept(Object node) { |
| if (node instanceof DefaultMutableTreeNode) { |
| final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node; |
| final Object userObject = treeNode.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| ((SingleConfigurationConfigurable)userObject).disposeUIResources(); |
| } |
| } |
| return true; |
| } |
| }); |
| myRightPanel.removeAll(); |
| myProperties.setFloat(DIVIDER_PROPORTION, mySplitter.getProportion()); |
| mySplitter.dispose(); |
| } |
| |
| private void updateDialog() { |
| final Executor executor = myRunDialog != null ? myRunDialog.getExecutor() : null; |
| if (executor == null) return; |
| final StringBuilder buffer = new StringBuilder(); |
| buffer.append(executor.getId()); |
| final SingleConfigurationConfigurable<RunConfiguration> configuration = getSelectedConfiguration(); |
| if (configuration != null) { |
| buffer.append(" - "); |
| buffer.append(configuration.getNameText()); |
| } |
| myRunDialog.setOKActionEnabled(canRunConfiguration(configuration, executor)); |
| myRunDialog.setTitle(buffer.toString()); |
| } |
| |
| private void setupDialogBounds() { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| UIUtil.setupEnclosingDialogBounds(myWholePanel); |
| } |
| }); |
| } |
| |
| @Nullable |
| private SingleConfigurationConfigurable<RunConfiguration> getSelectedConfiguration() { |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| final Object userObject = treeNode.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| return (SingleConfigurationConfigurable<RunConfiguration>)userObject; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean canRunConfiguration(@Nullable SingleConfigurationConfigurable<RunConfiguration> configuration, final @NotNull Executor executor) { |
| try { |
| return configuration != null && RunManagerImpl.canRunConfiguration(configuration.getSnapshot(), executor); |
| } |
| catch (ConfigurationException e) { |
| return false; |
| } |
| } |
| |
| RunManagerImpl getRunManager() { |
| return RunManagerImpl.getInstanceImpl(myProject); |
| } |
| |
| @Override |
| public String getHelpTopic() { |
| final ConfigurationType type = getSelectedConfigurationType(); |
| if (type != null) { |
| return "reference.dialogs.rundebug." + type.getId(); |
| } |
| return "reference.dialogs.rundebug"; |
| } |
| |
| private void clickDefaultButton() { |
| if (myRunDialog != null) myRunDialog.clickDefaultButton(); |
| } |
| |
| @Nullable |
| private DefaultMutableTreeNode getSelectedConfigurationTypeNode() { |
| TreePath selectionPath = myTree.getSelectionPath(); |
| DefaultMutableTreeNode node = selectionPath != null ? (DefaultMutableTreeNode)selectionPath.getLastPathComponent() : null; |
| while(node != null) { |
| Object userObject = node.getUserObject(); |
| if (userObject instanceof ConfigurationType) { |
| return node; |
| } |
| node = (DefaultMutableTreeNode)node.getParent(); |
| } |
| return null; |
| } |
| |
| @NotNull |
| private DefaultMutableTreeNode getNode(int row) { |
| return (DefaultMutableTreeNode)myTree.getPathForRow(row).getLastPathComponent(); |
| } |
| |
| @Nullable |
| Trinity<Integer, Integer, RowsDnDSupport.RefinedDropSupport.Position> getAvailableDropPosition(int direction) { |
| int[] rows = myTree.getSelectionRows(); |
| if (rows == null || rows.length != 1) { |
| return null; |
| } |
| int oldIndex = rows[0]; |
| int newIndex = oldIndex + direction; |
| |
| if (!getKind((DefaultMutableTreeNode)myTree.getPathForRow(oldIndex).getLastPathComponent()).supportsDnD()) |
| return null; |
| |
| while (newIndex > 0 && newIndex < myTree.getRowCount()) { |
| TreePath targetPath = myTree.getPathForRow(newIndex); |
| boolean allowInto = getKind((DefaultMutableTreeNode)targetPath.getLastPathComponent()) == FOLDER && !myTree.isExpanded(targetPath); |
| RowsDnDSupport.RefinedDropSupport.Position position = allowInto && myTreeModel.isDropInto(myTree, oldIndex, newIndex) ? |
| INTO : |
| direction > 0 ? BELOW : ABOVE; |
| DefaultMutableTreeNode oldNode = getNode(oldIndex); |
| DefaultMutableTreeNode newNode = getNode(newIndex); |
| if (oldNode.getParent() != newNode.getParent() && getKind(newNode) != FOLDER) { |
| RowsDnDSupport.RefinedDropSupport.Position copy = position; |
| if (position == BELOW) { |
| copy = ABOVE; |
| } |
| else if (position == ABOVE) { |
| copy = BELOW; |
| } |
| if (myTreeModel.canDrop(oldIndex, newIndex, copy)) { |
| return Trinity.create(oldIndex, newIndex, copy); |
| } |
| } |
| if (myTreeModel.canDrop(oldIndex, newIndex, position)) { |
| return Trinity.create(oldIndex, newIndex, position); |
| } |
| |
| if (position == BELOW && newIndex < myTree.getRowCount() - 1 && myTreeModel.canDrop(oldIndex, newIndex + 1, ABOVE)) { |
| return Trinity.create(oldIndex, newIndex + 1, ABOVE); |
| } |
| if (position == ABOVE && newIndex > 1 && myTreeModel.canDrop(oldIndex, newIndex - 1, BELOW)) { |
| return Trinity.create(oldIndex, newIndex - 1, BELOW); |
| } |
| if (position == BELOW && myTreeModel.canDrop(oldIndex, newIndex, ABOVE)) { |
| return Trinity.create(oldIndex, newIndex, ABOVE); |
| } |
| if (position == ABOVE && myTreeModel.canDrop(oldIndex, newIndex, BELOW)) { |
| return Trinity.create(oldIndex, newIndex, BELOW); |
| } |
| newIndex += direction; |
| } |
| return null; |
| } |
| |
| |
| @NotNull |
| private static String createUniqueName(DefaultMutableTreeNode typeNode, @Nullable String baseName, NodeKind...kinds) { |
| String str = (baseName == null) ? ExecutionBundle.message("run.configuration.unnamed.name.prefix") : baseName; |
| List<DefaultMutableTreeNode> configurationNodes = new ArrayList<DefaultMutableTreeNode>(); |
| collectNodesRecursively(typeNode, configurationNodes, kinds); |
| final ArrayList<String> currentNames = new ArrayList<String>(); |
| for (DefaultMutableTreeNode node : configurationNodes) { |
| final Object userObject = node.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| currentNames.add(((SingleConfigurationConfigurable)userObject).getNameText()); |
| } |
| else if (userObject instanceof RunnerAndConfigurationSettingsImpl) { |
| currentNames.add(((RunnerAndConfigurationSettings)userObject).getName()); |
| } |
| else if (userObject instanceof String) { |
| currentNames.add((String)userObject); |
| } |
| } |
| return RunManager.suggestUniqueName(str, currentNames); |
| } |
| |
| private SingleConfigurationConfigurable<RunConfiguration> createNewConfiguration(final RunnerAndConfigurationSettings settings, |
| final DefaultMutableTreeNode node, |
| DefaultMutableTreeNode selectedNode) { |
| final SingleConfigurationConfigurable<RunConfiguration> configurationConfigurable = |
| SingleConfigurationConfigurable.editSettings(settings, null); |
| installUpdateListeners(configurationConfigurable); |
| DefaultMutableTreeNode nodeToAdd = new DefaultMutableTreeNode(configurationConfigurable); |
| myTreeModel.insertNodeInto(nodeToAdd, node, selectedNode != null ? node.getIndex(selectedNode) + 1 :node.getChildCount()); |
| TreeUtil.selectNode(myTree, nodeToAdd); |
| return configurationConfigurable; |
| } |
| |
| private void createNewConfiguration(final ConfigurationFactory factory) { |
| DefaultMutableTreeNode node = null; |
| DefaultMutableTreeNode selectedNode = null; |
| TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath != null) { |
| selectedNode = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| } |
| DefaultMutableTreeNode typeNode = getConfigurationTypeNode(factory.getType()); |
| if (typeNode == null) { |
| typeNode = new DefaultMutableTreeNode(factory.getType()); |
| myRoot.add(typeNode); |
| sortTopLevelBranches(); |
| ((DefaultTreeModel)myTree.getModel()).reload(); |
| } |
| node = typeNode; |
| if (selectedNode != null && typeNode.isNodeDescendant(selectedNode)) { |
| node = selectedNode; |
| if (getKind(node).isConfiguration()) { |
| node = (DefaultMutableTreeNode)node.getParent(); |
| } |
| } |
| final RunnerAndConfigurationSettings settings = getRunManager().createConfiguration(createUniqueName(typeNode, null, CONFIGURATION, TEMPORARY_CONFIGURATION), factory); |
| if (factory instanceof ConfigurationFactoryEx) { |
| ((ConfigurationFactoryEx)factory).onNewConfigurationCreated(settings.getConfiguration()); |
| } |
| createNewConfiguration(settings, node, selectedNode); |
| } |
| |
| private class MyToolbarAddAction extends AnAction implements AnActionButtonRunnable { |
| public MyToolbarAddAction() { |
| super(ExecutionBundle.message("add.new.run.configuration.acrtion.name"), |
| ExecutionBundle.message("add.new.run.configuration.acrtion.name"), ADD_ICON); |
| registerCustomShortcutSet(CommonShortcuts.INSERT, myTree); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| showAddPopup(true); |
| } |
| |
| @Override |
| public void run(AnActionButton button) { |
| showAddPopup(true); |
| } |
| |
| private void showAddPopup(final boolean showApplicableTypesOnly) { |
| ConfigurationType[] allTypes = getRunManager().getConfigurationFactories(false); |
| final List<ConfigurationType> configurationTypes = getTypesToShow(showApplicableTypesOnly, allTypes); |
| Collections.sort(configurationTypes, new Comparator<ConfigurationType>() { |
| @Override |
| public int compare(final ConfigurationType type1, final ConfigurationType type2) { |
| return type1.getDisplayName().compareToIgnoreCase(type2.getDisplayName()); |
| } |
| }); |
| final int hiddenCount = allTypes.length - configurationTypes.size(); |
| if (hiddenCount > 0) { |
| configurationTypes.add(null); |
| } |
| |
| final ListPopup popup = JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<ConfigurationType>( |
| ExecutionBundle.message("add.new.run.configuration.acrtion.name"), configurationTypes) { |
| |
| @Override |
| @NotNull |
| public String getTextFor(final ConfigurationType type) { |
| return type != null ? type.getDisplayName() : hiddenCount + " items more (irrelevant)..."; |
| } |
| |
| @Override |
| public boolean isSpeedSearchEnabled() { |
| return true; |
| } |
| |
| @Override |
| public boolean canBeHidden(ConfigurationType value) { |
| return true; |
| } |
| |
| @Override |
| public Icon getIconFor(final ConfigurationType type) { |
| return type != null ? type.getIcon() : EmptyIcon.ICON_16; |
| } |
| |
| @Override |
| public PopupStep onChosen(final ConfigurationType type, final boolean finalChoice) { |
| if (hasSubstep(type)) { |
| return getSupStep(type); |
| } |
| if (type == null) { |
| return doFinalStep(new Runnable() { |
| @Override |
| public void run() { |
| showAddPopup(false); |
| } |
| }); |
| } |
| |
| final ConfigurationFactory[] factories = type.getConfigurationFactories(); |
| if (factories.length > 0) { |
| createNewConfiguration(factories[0]); |
| } |
| return FINAL_CHOICE; |
| } |
| |
| @Override |
| public int getDefaultOptionIndex() { |
| ConfigurationType type = getSelectedConfigurationType(); |
| return type != null ? configurationTypes.indexOf(type) : super.getDefaultOptionIndex(); |
| } |
| |
| private ListPopupStep getSupStep(final ConfigurationType type) { |
| final ConfigurationFactory[] factories = type.getConfigurationFactories(); |
| Arrays.sort(factories, new Comparator<ConfigurationFactory>() { |
| @Override |
| public int compare(final ConfigurationFactory factory1, final ConfigurationFactory factory2) { |
| return factory1.getName().compareToIgnoreCase(factory2.getName()); |
| } |
| }); |
| return new BaseListPopupStep<ConfigurationFactory>( |
| ExecutionBundle.message("add.new.run.configuration.action.name", type.getDisplayName()), factories) { |
| |
| @Override |
| @NotNull |
| public String getTextFor(final ConfigurationFactory value) { |
| return value.getName(); |
| } |
| |
| @Override |
| public Icon getIconFor(final ConfigurationFactory factory) { |
| return factory.getIcon(); |
| } |
| |
| @Override |
| public PopupStep onChosen(final ConfigurationFactory factory, final boolean finalChoice) { |
| createNewConfiguration(factory); |
| return FINAL_CHOICE; |
| } |
| }; |
| } |
| |
| @Override |
| public boolean hasSubstep(final ConfigurationType type) { |
| return type != null && type.getConfigurationFactories().length > 1; |
| } |
| }); |
| //new TreeSpeedSearch(myTree); |
| popup.showUnderneathOf(myToolbarDecorator.getActionsPanel()); |
| } |
| |
| private List<ConfigurationType> getTypesToShow(boolean showApplicableTypesOnly, ConfigurationType[] allTypes) { |
| if (showApplicableTypesOnly) { |
| List<ConfigurationType> applicableTypes = new ArrayList<ConfigurationType>(); |
| for (ConfigurationType type : allTypes) { |
| if (isApplicable(type)) { |
| applicableTypes.add(type); |
| } |
| } |
| if (applicableTypes.size() < allTypes.length - 3) { |
| return applicableTypes; |
| } |
| } |
| return new ArrayList<ConfigurationType>(Arrays.asList(allTypes)); |
| } |
| |
| private boolean isApplicable(ConfigurationType type) { |
| for (ConfigurationFactory factory : type.getConfigurationFactories()) { |
| if (factory.isApplicable(myProject)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| |
| private class MyRemoveAction extends AnAction implements AnActionButtonRunnable, AnActionButtonUpdater{ |
| |
| public MyRemoveAction() { |
| super(ExecutionBundle.message("remove.run.configuration.action.name"), |
| ExecutionBundle.message("remove.run.configuration.action.name"), REMOVE_ICON); |
| registerCustomShortcutSet(CommonShortcuts.getDelete(), myTree); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| doRemove(); |
| } |
| |
| @Override |
| public void run(AnActionButton button) { |
| doRemove(); |
| } |
| |
| private void doRemove() { |
| TreePath[] selections = myTree.getSelectionPaths(); |
| myTree.clearSelection(); |
| |
| int nodeIndexToSelect = -1; |
| DefaultMutableTreeNode parentToSelect = null; |
| |
| Set<DefaultMutableTreeNode> changedParents = new HashSet<DefaultMutableTreeNode>(); |
| boolean wasRootChanged = false; |
| |
| for (TreePath each : selections) { |
| DefaultMutableTreeNode node = (DefaultMutableTreeNode)each.getLastPathComponent(); |
| DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent(); |
| NodeKind kind = getKind(node); |
| if (!kind.isConfiguration() && kind != FOLDER) |
| continue; |
| |
| if (node.getUserObject() instanceof SingleConfigurationConfigurable) { |
| ((SingleConfigurationConfigurable)node.getUserObject()).disposeUIResources(); |
| } |
| |
| nodeIndexToSelect = parent.getIndex(node); |
| parentToSelect = parent; |
| myTreeModel.removeNodeFromParent(node); |
| changedParents.add(parent); |
| |
| if (kind == FOLDER) { |
| List<DefaultMutableTreeNode> children = new ArrayList<DefaultMutableTreeNode>(); |
| for (int i = 0; i < node.getChildCount(); i++) { |
| DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i); |
| Object userObject = getSafeUserObject(child); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| ((SingleConfigurationConfigurable)userObject).setFolderName(null); |
| } |
| children.add(0, child); |
| } |
| int confIndex = 0; |
| for (int i = 0; i < parent.getChildCount(); i++) { |
| if (getKind((DefaultMutableTreeNode)parent.getChildAt(i)).isConfiguration()) { |
| confIndex = i; |
| break; |
| } |
| } |
| for (DefaultMutableTreeNode child : children) { |
| if (getKind(child) == CONFIGURATION) |
| myTreeModel.insertNodeInto(child, parent, confIndex); |
| } |
| confIndex = parent.getChildCount(); |
| for (int i = 0; i < parent.getChildCount(); i++) { |
| if (getKind((DefaultMutableTreeNode)parent.getChildAt(i)) == TEMPORARY_CONFIGURATION) { |
| confIndex = i; |
| break; |
| } |
| } |
| for (DefaultMutableTreeNode child : children) { |
| if (getKind(child) == TEMPORARY_CONFIGURATION) |
| myTreeModel.insertNodeInto(child, parent, confIndex); |
| } |
| } |
| |
| if (parent.getChildCount() == 0 && parent.getUserObject() instanceof ConfigurationType) { |
| changedParents.remove(parent); |
| wasRootChanged = true; |
| |
| nodeIndexToSelect = myRoot.getIndex(parent); |
| nodeIndexToSelect = Math.max(0, nodeIndexToSelect - 1); |
| parentToSelect = myRoot; |
| parent.removeFromParent(); |
| } |
| } |
| |
| if (wasRootChanged) { |
| ((DefaultTreeModel)myTree.getModel()).reload(); |
| } else { |
| for (DefaultMutableTreeNode each : changedParents) { |
| myTreeModel.reload(each); |
| myTree.expandPath(new TreePath(each)); |
| } |
| } |
| |
| mySelectedConfigurable = null; |
| if (myRoot.getChildCount() == 0) { |
| drawPressAddButtonMessage(null); |
| } |
| else { |
| if (parentToSelect.getChildCount() > 0) { |
| TreeNode nodeToSelect = nodeIndexToSelect < parentToSelect.getChildCount() |
| ? parentToSelect.getChildAt(nodeIndexToSelect) |
| : parentToSelect.getChildAt(nodeIndexToSelect - 1); |
| TreeUtil.selectInTree((DefaultMutableTreeNode)nodeToSelect, true, myTree); |
| } |
| } |
| } |
| |
| |
| @Override |
| public void update(AnActionEvent e) { |
| boolean enabled = isEnabled(e); |
| e.getPresentation().setEnabled(enabled); |
| } |
| |
| @Override |
| public boolean isEnabled(AnActionEvent e) { |
| boolean enabled = false; |
| TreePath[] selections = myTree.getSelectionPaths(); |
| if (selections != null) { |
| for (TreePath each : selections) { |
| NodeKind kind = getKind((DefaultMutableTreeNode)each.getLastPathComponent()); |
| if (kind.isConfiguration() || kind == FOLDER) { |
| enabled = true; |
| break; |
| } |
| } |
| } |
| return enabled; |
| } |
| } |
| |
| private class MyCopyAction extends AnAction { |
| public MyCopyAction() { |
| super(ExecutionBundle.message("copy.configuration.action.name"), |
| ExecutionBundle.message("copy.configuration.action.name"), |
| PlatformIcons.COPY_ICON); |
| |
| final AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_EDITOR_DUPLICATE); |
| registerCustomShortcutSet(action.getShortcutSet(), myTree); |
| } |
| |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| final SingleConfigurationConfigurable<RunConfiguration> configuration = getSelectedConfiguration(); |
| LOG.assertTrue(configuration != null); |
| try { |
| final DefaultMutableTreeNode typeNode = getSelectedConfigurationTypeNode(); |
| final RunnerAndConfigurationSettings settings = configuration.getSnapshot(); |
| final String copyName = createUniqueName(typeNode, configuration.getNameText(), CONFIGURATION, TEMPORARY_CONFIGURATION); |
| settings.setName(copyName); |
| final ConfigurationFactory factory = settings.getFactory(); |
| if (factory instanceof ConfigurationFactoryEx) { |
| ((ConfigurationFactoryEx)factory).onConfigurationCopied(settings.getConfiguration()); |
| } |
| final SingleConfigurationConfigurable<RunConfiguration> configurable = createNewConfiguration(settings, typeNode, getSelectedNode()); |
| IdeFocusManager.getInstance(myProject).requestFocus(configurable.getNameTextField(), true); |
| configurable.getNameTextField().setSelectionStart(0); |
| configurable.getNameTextField().setSelectionEnd(copyName.length()); |
| } |
| catch (ConfigurationException e1) { |
| Messages.showErrorDialog(myToolbarDecorator.getActionsPanel(), e1.getMessage(), e1.getTitle()); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| final SingleConfigurationConfigurable<RunConfiguration> configuration = getSelectedConfiguration(); |
| e.getPresentation().setEnabled(configuration != null && !(configuration.getConfiguration() instanceof UnknownRunConfiguration)); |
| } |
| } |
| |
| private class MySaveAction extends AnAction { |
| |
| public MySaveAction() { |
| super(ExecutionBundle.message("action.name.save.configuration"), null, AllIcons.Actions.Menu_saveall); |
| } |
| |
| @Override |
| public void actionPerformed(final AnActionEvent e) { |
| final SingleConfigurationConfigurable<RunConfiguration> configurationConfigurable = getSelectedConfiguration(); |
| LOG.assertTrue(configurationConfigurable != null); |
| try { |
| configurationConfigurable.apply(); |
| } |
| catch (ConfigurationException e1) { |
| //do nothing |
| } |
| final RunnerAndConfigurationSettings originalConfiguration = configurationConfigurable.getSettings(); |
| if (originalConfiguration.isTemporary()) { |
| getRunManager().makeStable(originalConfiguration); |
| adjustOrder(); |
| } |
| myTree.repaint(); |
| } |
| |
| @Override |
| public void update(final AnActionEvent e) { |
| final SingleConfigurationConfigurable<RunConfiguration> configuration = getSelectedConfiguration(); |
| final Presentation presentation = e.getPresentation(); |
| final boolean enabled; |
| if (configuration == null) { |
| enabled = false; |
| } else { |
| RunnerAndConfigurationSettings settings = configuration.getSettings(); |
| enabled = settings != null && settings.isTemporary(); |
| } |
| presentation.setEnabled(enabled); |
| presentation.setVisible(enabled); |
| } |
| } |
| |
| /** |
| * Just saved as 'stable' configuration shouldn't stay between temporary ones (here we order nodes in JTree only) |
| * @return shift (positive) for move configuration "up" to other stable configurations. Zero means "there is nothing to change" |
| */ |
| |
| |
| private int adjustOrder() { |
| TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath == null) |
| return 0; |
| final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| RunnerAndConfigurationSettings selectedSettings = getSettings(treeNode); |
| if (selectedSettings == null || selectedSettings.isTemporary()) |
| return 0; |
| MutableTreeNode parent = (MutableTreeNode)treeNode.getParent(); |
| int initialPosition = parent.getIndex(treeNode); |
| int position = initialPosition; |
| DefaultMutableTreeNode node = treeNode.getPreviousSibling(); |
| while (node != null) { |
| RunnerAndConfigurationSettings settings = getSettings(node); |
| if (settings != null && settings.isTemporary()) { |
| position--; |
| } else { |
| break; |
| } |
| node = node.getPreviousSibling(); |
| } |
| for (int i = 0; i < initialPosition - position; i++) { |
| TreeUtil.moveSelectedRow(myTree, -1); |
| } |
| return initialPosition - position; |
| } |
| |
| private class MyMoveAction extends AnAction implements AnActionButtonRunnable, AnActionButtonUpdater { |
| private final int myDirection; |
| |
| protected MyMoveAction(String text, String description, Icon icon, int direction) { |
| super(text, description, icon); |
| myDirection = direction; |
| } |
| |
| @Override |
| public void actionPerformed(final AnActionEvent e) { |
| doMove(); |
| } |
| |
| private void doMove() { |
| Trinity<Integer, Integer, RowsDnDSupport.RefinedDropSupport.Position> dropPosition = getAvailableDropPosition(myDirection); |
| if (dropPosition != null) { |
| myTreeModel.drop(dropPosition.first, dropPosition.second, dropPosition.third); |
| } |
| } |
| |
| @Override |
| public void run(AnActionButton button) { |
| doMove(); |
| } |
| |
| @Override |
| public void update(final AnActionEvent e) { |
| e.getPresentation().setEnabled(isEnabled(e)); |
| } |
| |
| @Override |
| public boolean isEnabled(AnActionEvent e) { |
| return getAvailableDropPosition(myDirection) != null; |
| } |
| } |
| |
| private class MyEditDefaultsAction extends AnAction { |
| public MyEditDefaultsAction() { |
| super(ExecutionBundle.message("run.configuration.edit.default.configuration.settings.text"), |
| ExecutionBundle.message("run.configuration.edit.default.configuration.settings.description"), AllIcons.General.Settings); |
| } |
| |
| @Override |
| public void actionPerformed(final AnActionEvent e) { |
| TreeNode defaults = TreeUtil.findNodeWithObject(DEFAULTS, myTree.getModel(), myRoot); |
| if (defaults != null) { |
| final ConfigurationType configurationType = getSelectedConfigurationType(); |
| if (configurationType != null) { |
| defaults = TreeUtil.findNodeWithObject(configurationType, myTree.getModel(), defaults); |
| } |
| final DefaultMutableTreeNode defaultsNode = (DefaultMutableTreeNode)defaults; |
| if (defaultsNode == null) { |
| return; |
| } |
| final TreePath path = TreeUtil.getPath(myRoot, defaultsNode); |
| myTree.expandPath(path); |
| TreeUtil.selectInTree(defaultsNode, true, myTree); |
| myTree.scrollPathToVisible(path); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| boolean isEnabled = TreeUtil.findNodeWithObject(DEFAULTS, myTree.getModel(), myRoot) != null; |
| TreePath path = myTree.getSelectionPath(); |
| if (path != null) { |
| Object o = path.getLastPathComponent(); |
| if (o instanceof DefaultMutableTreeNode && ((DefaultMutableTreeNode)o).getUserObject().equals(DEFAULTS)) { |
| isEnabled = false; |
| } |
| o = path.getParentPath().getLastPathComponent(); |
| if (o instanceof DefaultMutableTreeNode && ((DefaultMutableTreeNode)o).getUserObject().equals(DEFAULTS)) { |
| isEnabled = false; |
| } |
| } |
| e.getPresentation().setEnabled(isEnabled); |
| } |
| } |
| |
| private class MyCreateFolderAction extends AnAction { |
| private MyCreateFolderAction() { |
| super(ExecutionBundle.message("run.configuration.create.folder.text"), |
| ExecutionBundle.message("run.configuration.create.folder.description"), AllIcons.Nodes.Folder); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| final ConfigurationType type = getSelectedConfigurationType(); |
| if (type == null) { |
| return; |
| } |
| final DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(); |
| DefaultMutableTreeNode typeNode = getConfigurationTypeNode(type); |
| if (typeNode == null) { |
| return; |
| } |
| String folderName = createUniqueName(typeNode, "New Folder", FOLDER); |
| List<DefaultMutableTreeNode> folders = new ArrayList<DefaultMutableTreeNode>(); |
| collectNodesRecursively(getConfigurationTypeNode(type), folders, FOLDER); |
| final DefaultMutableTreeNode folderNode = new DefaultMutableTreeNode(folderName); |
| myTreeModel.insertNodeInto(folderNode, typeNode, folders.size()); |
| isFolderCreating = true; |
| try { |
| for (DefaultMutableTreeNode node : selectedNodes) { |
| int folderRow = myTree.getRowForPath(new TreePath(folderNode.getPath())); |
| int rowForPath = myTree.getRowForPath(new TreePath(node.getPath())); |
| if (getKind(node).isConfiguration() && myTreeModel.canDrop(rowForPath, folderRow, INTO)) { |
| myTreeModel.drop(rowForPath, folderRow, INTO); |
| } |
| } |
| myTree.setSelectionPath(new TreePath(folderNode.getPath())); |
| } |
| finally { |
| isFolderCreating = false; |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| boolean isEnabled = false; |
| boolean toMove = false; |
| DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(); |
| ConfigurationType selectedType = null; |
| for (DefaultMutableTreeNode node : selectedNodes) { |
| ConfigurationType type = getType(node); |
| if (selectedType == null) { |
| selectedType = type; |
| } else { |
| if (!Comparing.equal(type, selectedType)) { |
| isEnabled = false; |
| break; |
| } |
| } |
| NodeKind kind = getKind(node); |
| if (kind.isConfiguration() || (kind == CONFIGURATION_TYPE && node.getParent() == myRoot) || kind == FOLDER) { |
| isEnabled = true; |
| } |
| if (kind.isConfiguration()) { |
| toMove = true; |
| } |
| } |
| e.getPresentation().setText(ExecutionBundle.message("run.configuration.create.folder.description" + (toMove ? ".move" : ""))); |
| e.getPresentation().setEnabled(isEnabled); |
| } |
| } |
| |
| private class MySortFolderAction extends AnAction implements Comparator<DefaultMutableTreeNode>{ |
| private MySortFolderAction() { |
| super(ExecutionBundle.message("run.configuration.sort.folder.text"), |
| ExecutionBundle.message("run.configuration.sort.folder.description"), AllIcons.Icons.Inspector.SortByName); |
| } |
| |
| @Override |
| public int compare(DefaultMutableTreeNode node1, DefaultMutableTreeNode node2) { |
| NodeKind kind1 = getKind(node1); |
| NodeKind kind2 = getKind(node2); |
| if (kind1 == FOLDER) { |
| return (kind2 == FOLDER) ? node1.getParent().getIndex(node1) - node2.getParent().getIndex(node2) : -1; |
| } |
| if (kind2 == FOLDER) { |
| return 1; |
| } |
| String name1 = getName(node1.getUserObject()); |
| String name2 = getName(node2.getUserObject()); |
| if (kind1 == TEMPORARY_CONFIGURATION) { |
| return (kind2 == TEMPORARY_CONFIGURATION) ? name1.compareTo(name2) : 1; |
| } |
| if (kind2 == TEMPORARY_CONFIGURATION) { |
| return -1; |
| } |
| return name1.compareTo(name2); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| final DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(); |
| List<DefaultMutableTreeNode> foldersToSort = new ArrayList<DefaultMutableTreeNode>(); |
| for (DefaultMutableTreeNode node : selectedNodes) { |
| NodeKind kind = getKind(node); |
| if (kind == CONFIGURATION_TYPE || kind == FOLDER) { |
| foldersToSort.add(node); |
| } |
| } |
| for (DefaultMutableTreeNode folderNode : foldersToSort) { |
| List<DefaultMutableTreeNode> children = new ArrayList<DefaultMutableTreeNode>(); |
| for (int i = 0; i < folderNode.getChildCount(); i++) { |
| DefaultMutableTreeNode child = (DefaultMutableTreeNode)folderNode.getChildAt(i); |
| children.add(child); |
| } |
| Collections.sort(children, this); |
| for (DefaultMutableTreeNode child : children) { |
| folderNode.add(child); |
| } |
| myTreeModel.nodeStructureChanged(folderNode); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| final DefaultMutableTreeNode[] selectedNodes = getSelectedNodes(); |
| for (DefaultMutableTreeNode node : selectedNodes) { |
| NodeKind kind = getKind(node); |
| if (kind == CONFIGURATION_TYPE || kind == FOLDER) { |
| e.getPresentation().setEnabled(true); |
| return; |
| } |
| } |
| e.getPresentation().setEnabled(false); |
| } |
| } |
| |
| @Nullable |
| private static ConfigurationType getType(DefaultMutableTreeNode node) { |
| while (node != null) { |
| if (node.getUserObject() instanceof ConfigurationType) { |
| return (ConfigurationType)node.getUserObject(); |
| } |
| node = (DefaultMutableTreeNode)node.getParent(); |
| } |
| return null; |
| } |
| |
| @NotNull |
| private DefaultMutableTreeNode[] getSelectedNodes() { |
| return myTree.getSelectedNodes(DefaultMutableTreeNode.class, null); |
| } |
| |
| @Nullable |
| private DefaultMutableTreeNode getSelectedNode() { |
| DefaultMutableTreeNode[] nodes = myTree.getSelectedNodes(DefaultMutableTreeNode.class, null); |
| return nodes.length >= 1 ? nodes[0] : null; |
| } |
| |
| @Nullable |
| private RunnerAndConfigurationSettings getSelectedSettings() { |
| TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath == null) |
| return null; |
| return getSettings((DefaultMutableTreeNode)selectionPath.getLastPathComponent()); |
| } |
| |
| @Nullable |
| private static RunnerAndConfigurationSettings getSettings(DefaultMutableTreeNode treeNode) { |
| if (treeNode == null) |
| return null; |
| RunnerAndConfigurationSettings settings = null; |
| if (treeNode.getUserObject() instanceof SingleConfigurationConfigurable) { |
| settings = (RunnerAndConfigurationSettings)((SingleConfigurationConfigurable)treeNode.getUserObject()).getSettings(); |
| } |
| if (treeNode.getUserObject() instanceof RunnerAndConfigurationSettings) { |
| settings = (RunnerAndConfigurationSettings)treeNode.getUserObject(); |
| } |
| return settings; |
| } |
| |
| private static class RunConfigurationBean { |
| private final RunnerAndConfigurationSettings mySettings; |
| private final boolean myShared; |
| private final List<BeforeRunTask> myStepsBeforeLaunch; |
| private final SingleConfigurationConfigurable myConfigurable; |
| |
| public RunConfigurationBean(final RunnerAndConfigurationSettings settings, |
| final boolean shared, |
| final List<BeforeRunTask> stepsBeforeLaunch) { |
| mySettings = settings; |
| myShared = shared; |
| myStepsBeforeLaunch = Collections.unmodifiableList(stepsBeforeLaunch); |
| myConfigurable = null; |
| } |
| |
| public RunConfigurationBean(final SingleConfigurationConfigurable configurable) { |
| myConfigurable = configurable; |
| mySettings = (RunnerAndConfigurationSettings)myConfigurable.getSettings(); |
| final ConfigurationSettingsEditorWrapper editorWrapper = (ConfigurationSettingsEditorWrapper)myConfigurable.getEditor(); |
| myShared = configurable.isStoreProjectConfiguration(); |
| myStepsBeforeLaunch = editorWrapper.getStepsBeforeLaunch(); |
| } |
| |
| public RunnerAndConfigurationSettings getSettings() { |
| return mySettings; |
| } |
| |
| public boolean isShared() { |
| return myShared; |
| } |
| |
| public List<BeforeRunTask> getStepsBeforeLaunch() { |
| return myStepsBeforeLaunch; |
| } |
| |
| public SingleConfigurationConfigurable getConfigurable() { |
| return myConfigurable; |
| } |
| |
| @Override |
| public String toString() { |
| return String.valueOf(mySettings); |
| } |
| } |
| |
| public interface RunDialogBase { |
| void setOKActionEnabled(boolean isEnabled); |
| |
| @Nullable |
| Executor getExecutor(); |
| |
| void setTitle(String title); |
| |
| void clickDefaultButton(); |
| } |
| |
| enum NodeKind { |
| CONFIGURATION_TYPE, FOLDER, CONFIGURATION, TEMPORARY_CONFIGURATION, UNKNOWN; |
| |
| boolean supportsDnD() { |
| return this == FOLDER || this == CONFIGURATION || this == TEMPORARY_CONFIGURATION; |
| } |
| |
| boolean isConfiguration() { |
| return this == CONFIGURATION | this == TEMPORARY_CONFIGURATION; |
| } |
| } |
| |
| @NotNull |
| static NodeKind getKind(@Nullable DefaultMutableTreeNode node) { |
| if (node == null) |
| return UNKNOWN; |
| Object userObject = node.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable || userObject instanceof RunnerAndConfigurationSettings) { |
| RunnerAndConfigurationSettings settings = getSettings(node); |
| if (settings == null) { |
| return UNKNOWN; |
| } |
| return settings.isTemporary() ? TEMPORARY_CONFIGURATION : CONFIGURATION; |
| } |
| if (userObject instanceof String) { |
| return FOLDER; |
| } |
| if (userObject instanceof ConfigurationType) { |
| return CONFIGURATION_TYPE; |
| } |
| return UNKNOWN; |
| } |
| |
| class MyTreeModel extends DefaultTreeModel implements EditableModel, RowsDnDSupport.RefinedDropSupport { |
| private MyTreeModel(TreeNode root) { |
| super(root); |
| } |
| |
| @Override |
| public void addRow() { |
| } |
| |
| @Override |
| public void removeRow(int index) { |
| } |
| |
| @Override |
| public void exchangeRows(int oldIndex, int newIndex) { |
| //Do nothing, use drop() instead |
| } |
| |
| @Override |
| public boolean canExchangeRows(int oldIndex, int newIndex) { |
| return false;//Legacy, use canDrop() instead |
| } |
| |
| @Override |
| public boolean canDrop(int oldIndex, int newIndex, @NotNull Position position) { |
| if (myTree.getRowCount() <= oldIndex || myTree.getRowCount() <= newIndex || oldIndex < 0 || newIndex < 0) { |
| return false; |
| } |
| DefaultMutableTreeNode oldNode = (DefaultMutableTreeNode)myTree.getPathForRow(oldIndex).getLastPathComponent(); |
| DefaultMutableTreeNode newNode = (DefaultMutableTreeNode)myTree.getPathForRow(newIndex).getLastPathComponent(); |
| DefaultMutableTreeNode oldParent = (DefaultMutableTreeNode)oldNode.getParent(); |
| DefaultMutableTreeNode newParent = (DefaultMutableTreeNode)newNode.getParent(); |
| NodeKind oldKind = getKind(oldNode); |
| NodeKind newKind = getKind(newNode); |
| ConfigurationType oldType = getType(oldNode); |
| ConfigurationType newType = getType(newNode); |
| if (oldParent == newParent) { |
| if (oldNode.getPreviousSibling() == newNode && position == BELOW) { |
| return false; |
| } |
| if (oldNode.getNextSibling() == newNode && position == ABOVE) { |
| return false; |
| } |
| } |
| if (oldType == null) |
| return false; |
| if (oldType != newType) { |
| DefaultMutableTreeNode typeNode = getConfigurationTypeNode(oldType); |
| if (getKind(oldParent) == FOLDER && typeNode != null && typeNode.getNextSibling() == newNode && position == ABOVE) { |
| return true; |
| } |
| if (getKind(oldParent) == CONFIGURATION_TYPE && |
| oldKind == FOLDER && |
| typeNode != null && |
| typeNode.getNextSibling() == newNode && |
| position == ABOVE && |
| oldParent.getLastChild() != oldNode && |
| getKind((DefaultMutableTreeNode)oldParent.getLastChild()) == FOLDER) { |
| return true; |
| } |
| return false; |
| } |
| if (newParent == oldNode || oldParent == newNode) |
| return false; |
| if (oldKind == FOLDER && newKind != FOLDER) { |
| if (newKind.isConfiguration() && |
| position == ABOVE && |
| getKind(newParent) == CONFIGURATION_TYPE && |
| newIndex > 1 && |
| getKind((DefaultMutableTreeNode)myTree.getPathForRow(newIndex - 1).getParentPath().getLastPathComponent()) == FOLDER) { |
| return true; |
| } |
| return false; |
| } |
| if (!oldKind.supportsDnD() || !newKind.supportsDnD()) { |
| return false; |
| } |
| if (oldKind.isConfiguration() && newKind == FOLDER && position == ABOVE) |
| return false; |
| if (oldKind == TEMPORARY_CONFIGURATION && newKind == CONFIGURATION && position == ABOVE) |
| return false; |
| if (oldKind == CONFIGURATION && newKind == TEMPORARY_CONFIGURATION && position == BELOW) |
| return false; |
| if (oldKind == CONFIGURATION && newKind == TEMPORARY_CONFIGURATION && position == ABOVE) { |
| return newNode.getPreviousSibling() == null || |
| getKind(newNode.getPreviousSibling()) == CONFIGURATION || |
| getKind(newNode.getPreviousSibling()) == FOLDER; |
| } |
| if (oldKind == TEMPORARY_CONFIGURATION && newKind == CONFIGURATION && position == BELOW) |
| return newNode.getNextSibling() == null || getKind(newNode.getNextSibling()) == TEMPORARY_CONFIGURATION; |
| if (oldParent == newParent) { //Same parent |
| if (oldKind.isConfiguration() && newKind.isConfiguration()) { |
| return oldKind == newKind;//both are temporary or saved |
| } else if (oldKind == FOLDER) { |
| return !myTree.isExpanded(newIndex) || position == ABOVE; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isDropInto(JComponent component, int oldIndex, int newIndex) { |
| TreePath oldPath = myTree.getPathForRow(oldIndex); |
| TreePath newPath = myTree.getPathForRow(newIndex); |
| if (oldPath == null || newPath == null) { |
| return false; |
| } |
| DefaultMutableTreeNode oldNode = (DefaultMutableTreeNode)oldPath.getLastPathComponent(); |
| DefaultMutableTreeNode newNode = (DefaultMutableTreeNode)newPath.getLastPathComponent(); |
| return getKind(oldNode).isConfiguration() && getKind(newNode) == FOLDER; |
| } |
| |
| @Override |
| public void drop(int oldIndex, int newIndex, @NotNull Position position) { |
| DefaultMutableTreeNode oldNode = (DefaultMutableTreeNode)myTree.getPathForRow(oldIndex).getLastPathComponent(); |
| DefaultMutableTreeNode newNode = (DefaultMutableTreeNode)myTree.getPathForRow(newIndex).getLastPathComponent(); |
| DefaultMutableTreeNode newParent = (DefaultMutableTreeNode)newNode.getParent(); |
| NodeKind oldKind = getKind(oldNode); |
| boolean wasExpanded = myTree.isExpanded(new TreePath(oldNode.getPath())); |
| if (isDropInto(myTree, oldIndex, newIndex)) { //Drop in folder |
| removeNodeFromParent(oldNode); |
| int index = newNode.getChildCount(); |
| if (oldKind.isConfiguration()) { |
| int middleIndex = newNode.getChildCount(); |
| for (int i = 0; i < newNode.getChildCount(); i++) { |
| if (getKind((DefaultMutableTreeNode)newNode.getChildAt(i)) == TEMPORARY_CONFIGURATION) { |
| middleIndex = i;//index of first temporary configuration in target folder |
| break; |
| } |
| } |
| if (position != INTO) { |
| if (oldIndex < newIndex) { |
| index = oldKind == CONFIGURATION ? 0 : middleIndex; |
| } |
| else { |
| index = oldKind == CONFIGURATION ? middleIndex : newNode.getChildCount(); |
| } |
| } else { |
| index = oldKind == TEMPORARY_CONFIGURATION ? newNode.getChildCount() : middleIndex; |
| } |
| } |
| insertNodeInto(oldNode, newNode, index); |
| myTree.expandPath(new TreePath(newNode.getPath())); |
| } |
| else { |
| ConfigurationType type = getType(oldNode); |
| assert type != null; |
| removeNodeFromParent(oldNode); |
| int index; |
| if (type != getType(newNode)) { |
| DefaultMutableTreeNode typeNode = getConfigurationTypeNode(type); |
| assert typeNode != null; |
| newParent = typeNode; |
| index = newParent.getChildCount(); |
| } else { |
| index = newParent.getIndex(newNode); |
| if (position == BELOW) |
| index++; |
| } |
| insertNodeInto(oldNode, newParent, index); |
| } |
| TreePath treePath = new TreePath(oldNode.getPath()); |
| myTree.setSelectionPath(treePath); |
| if (wasExpanded) { |
| myTree.expandPath(treePath); |
| } |
| } |
| |
| @Override |
| public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { |
| super.insertNodeInto(newChild, parent, index); |
| if (!getKind((DefaultMutableTreeNode)newChild).isConfiguration()) { |
| return; |
| } |
| Object userObject = getSafeUserObject((DefaultMutableTreeNode)newChild); |
| String newFolderName = getKind((DefaultMutableTreeNode)parent) == FOLDER |
| ? (String)((DefaultMutableTreeNode)parent).getUserObject() |
| : null; |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| ((SingleConfigurationConfigurable)userObject).setFolderName(newFolderName); |
| } |
| } |
| |
| @Override |
| public void reload(TreeNode node) { |
| super.reload(node); |
| Object userObject = ((DefaultMutableTreeNode)node).getUserObject(); |
| if (userObject instanceof String) { |
| String folderName = (String)userObject; |
| for (int i = 0; i < node.getChildCount(); i++) { |
| DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i); |
| Object safeUserObject = getSafeUserObject(child); |
| if (safeUserObject instanceof SingleConfigurationConfigurable) { |
| ((SingleConfigurationConfigurable)safeUserObject).setFolderName(folderName); |
| } |
| } |
| } |
| } |
| |
| @Nullable |
| private RunnerAndConfigurationSettings getSettings(@NotNull DefaultMutableTreeNode treeNode) { |
| Object userObject = treeNode.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| SingleConfigurationConfigurable configurable = (SingleConfigurationConfigurable)userObject; |
| return (RunnerAndConfigurationSettings)configurable.getSettings(); |
| } else if (userObject instanceof RunnerAndConfigurationSettings) { |
| return (RunnerAndConfigurationSettings)userObject; |
| } |
| return null; |
| } |
| |
| @Nullable |
| private ConfigurationType getType(@Nullable DefaultMutableTreeNode treeNode) { |
| if (treeNode == null) |
| return null; |
| Object userObject = treeNode.getUserObject(); |
| if (userObject instanceof SingleConfigurationConfigurable) { |
| SingleConfigurationConfigurable configurable = (SingleConfigurationConfigurable)userObject; |
| return configurable.getConfiguration().getType(); |
| } else if (userObject instanceof RunnerAndConfigurationSettings) { |
| return ((RunnerAndConfigurationSettings)userObject).getType(); |
| } else if (userObject instanceof ConfigurationType) { |
| return (ConfigurationType)userObject; |
| } |
| if (treeNode.getParent() instanceof DefaultMutableTreeNode) { |
| return getType((DefaultMutableTreeNode)treeNode.getParent()); |
| } |
| return null; |
| } |
| } |
| } |