Run Configurations: selection lost after rename
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / impl / SingleConfigurationConfigurable.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution.impl;
18
19 import com.intellij.execution.*;
20 import com.intellij.execution.configurations.*;
21 import com.intellij.execution.runners.ProgramRunner;
22 import com.intellij.icons.AllIcons;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.options.ConfigurationException;
25 import com.intellij.openapi.options.SettingsEditor;
26 import com.intellij.openapi.options.SettingsEditorConfigurable;
27 import com.intellij.openapi.options.SettingsEditorListener;
28 import com.intellij.openapi.util.Comparing;
29 import com.intellij.ui.DocumentAdapter;
30 import com.intellij.ui.components.JBCheckBox;
31 import org.jetbrains.annotations.NonNls;
32 import org.jetbrains.annotations.Nullable;
33
34 import javax.swing.*;
35 import javax.swing.event.ChangeListener;
36 import javax.swing.event.DocumentEvent;
37 import javax.swing.event.DocumentListener;
38 import javax.swing.text.BadLocationException;
39 import javax.swing.text.PlainDocument;
40 import java.awt.*;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43
44 public final class SingleConfigurationConfigurable<Config extends RunConfiguration>
45     extends SettingsEditorConfigurable<RunnerAndConfigurationSettings> {
46   private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.SingleConfigurationConfigurable");
47   private final PlainDocument myNameDocument = new PlainDocument();
48   @Nullable private Executor myExecutor;
49
50   private ValidationResult myLastValidationResult = null;
51   private boolean myValidationResultValid = false;
52   private MyValidatableComponent myComponent;
53   private final String myDisplayName;
54   private final String myHelpTopic;
55   private final boolean myBrokenConfiguration;
56   private boolean myStoreProjectConfiguration;
57   private boolean mySingleton;
58   private String myFolderName;
59   private boolean myChangingNameFromCode;
60
61
62   private SingleConfigurationConfigurable(RunnerAndConfigurationSettings settings, @Nullable Executor executor) {
63     super(new ConfigurationSettingsEditorWrapper(settings), settings);
64     myExecutor = executor;
65
66     final Config configuration = getConfiguration();
67     myDisplayName = getSettings().getName();
68     myHelpTopic = "reference.dialogs.rundebug." + configuration.getType().getId();
69
70     myBrokenConfiguration = configuration instanceof UnknownRunConfiguration;
71     setFolderName(getSettings().getFolderName());
72
73     setNameText(configuration.getName());
74     myNameDocument.addDocumentListener(new DocumentAdapter() {
75       @Override
76       public void textChanged(DocumentEvent event) {
77         setModified(true);
78         if (!myChangingNameFromCode) {
79           RunConfiguration runConfiguration = getSettings().getConfiguration();
80           if (runConfiguration instanceof LocatableConfigurationBase) {
81             ((LocatableConfigurationBase) runConfiguration).setNameChangedByUser(true);
82           }
83         }
84       }
85     });
86
87     getEditor().addSettingsEditorListener(new SettingsEditorListener<RunnerAndConfigurationSettings>() {
88       @Override
89       public void stateChanged(SettingsEditor<RunnerAndConfigurationSettings> settingsEditor) {
90         myValidationResultValid = false;
91       }
92     });
93   }
94
95   public static <Config extends RunConfiguration> SingleConfigurationConfigurable<Config> editSettings(RunnerAndConfigurationSettings settings,
96                                                                                                        @Nullable Executor executor) {
97     SingleConfigurationConfigurable<Config> configurable = new SingleConfigurationConfigurable<Config>(settings, executor);
98     configurable.reset();
99     return configurable;
100   }
101
102   @Override
103   public void apply() throws ConfigurationException {
104     RunnerAndConfigurationSettings settings = getSettings();
105     RunConfiguration runConfiguration = settings.getConfiguration();
106     final RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(runConfiguration.getProject());
107     runManager.shareConfiguration(settings, myStoreProjectConfiguration);
108     settings.setName(getNameText());
109     settings.setSingleton(mySingleton);
110     settings.setFolderName(myFolderName);
111     super.apply();
112     runManager.addConfiguration(settings, myStoreProjectConfiguration, runManager.getBeforeRunTasks(settings.getConfiguration()), false);
113   }
114
115   @Override
116   public void reset() {
117     RunnerAndConfigurationSettings configuration = getSettings();
118     setNameText(configuration.getName());
119     super.reset();
120     if (myComponent == null) {
121       myComponent = new MyValidatableComponent();
122     }
123     myComponent.doReset(configuration);
124   }
125
126   @Override
127   public final JComponent createComponent() {
128     myComponent.myNameText.setEnabled(!myBrokenConfiguration);
129     return myComponent.getWholePanel();
130   }
131
132   final JComponent getValidationComponent() {
133     return myComponent.myValidationPanel;
134   }
135
136   public boolean isStoreProjectConfiguration() {
137     return myStoreProjectConfiguration;
138   }
139
140   public boolean isSingleton() {
141     return mySingleton;
142   }
143
144   @Nullable
145   private ValidationResult getValidationResult() {
146     if (!myValidationResultValid) {
147       myLastValidationResult = null;
148       RunnerAndConfigurationSettings snapshot = null;
149       try {
150         snapshot = getSnapshot();
151         if (snapshot != null) {
152           snapshot.setName(getNameText());
153           snapshot.checkSettings(myExecutor);
154           for (ProgramRunner runner : RunnerRegistry.getInstance().getRegisteredRunners()) {
155             for (Executor executor : ExecutorRegistry.getInstance().getRegisteredExecutors()) {
156               if (runner.canRun(executor.getId(), snapshot.getConfiguration())) {
157                 checkConfiguration(runner, snapshot);
158                 break;
159               }
160             }
161           }
162         }
163       }
164       catch (RuntimeConfigurationException exception) {
165         final Runnable quickFix = exception.getQuickFix();
166         Runnable resultQuickFix;
167         if (quickFix != null && snapshot != null) {
168           final RunnerAndConfigurationSettings fixedSettings = snapshot;
169           resultQuickFix = new Runnable() {
170
171             @Override
172             public void run() {
173               quickFix.run();
174               getEditor().resetFrom(fixedSettings);
175             }
176           };
177         }
178         else {
179           resultQuickFix = quickFix;
180         }
181         myLastValidationResult = new ValidationResult(exception.getLocalizedMessage(), exception.getTitle(), resultQuickFix);
182       }
183       catch (ConfigurationException e) {
184         myLastValidationResult = new ValidationResult(e.getLocalizedMessage(), ExecutionBundle.message("invalid.data.dialog.title"), null);
185       }
186
187       myValidationResultValid = true;
188     }
189     return myLastValidationResult;
190   }
191
192   private static void checkConfiguration(final ProgramRunner runner, final RunnerAndConfigurationSettings snapshot)
193       throws RuntimeConfigurationException {
194     final RunnerSettings runnerSettings = snapshot.getRunnerSettings(runner);
195     final ConfigurationPerRunnerSettings configurationSettings = snapshot.getConfigurationSettings(runner);
196     try {
197       runner.checkConfiguration(runnerSettings, configurationSettings);
198     }
199     catch (AbstractMethodError e) {
200       //backward compatibility
201     }
202   }
203
204   @Override
205   public final void disposeUIResources() {
206     super.disposeUIResources();
207     myComponent = null;
208   }
209
210   public final String getNameText() {
211     try {
212       return myNameDocument.getText(0, myNameDocument.getLength());
213     }
214     catch (BadLocationException e) {
215       LOG.error(e);
216       return "";
217     }
218   }
219
220   public final void addNameListener(DocumentListener listener) {
221     myNameDocument.addDocumentListener(listener);
222   }
223
224   public final void addSharedListener(ChangeListener changeListener) {
225     myComponent.myCbStoreProjectConfiguration.addChangeListener(changeListener);
226   }
227
228   public final void setNameText(final String name) {
229     myChangingNameFromCode = true;
230     try {
231       try {
232         if (!myNameDocument.getText(0, myNameDocument.getLength()).equals(name)) {
233           myNameDocument.replace(0, myNameDocument.getLength(), name, null);
234         }
235       }
236       catch (BadLocationException e) {
237         LOG.error(e);
238       }
239     }
240     finally {
241       myChangingNameFromCode = false;
242     }
243   }
244
245   public final boolean isValid() {
246     return getValidationResult() == null;
247   }
248
249   public final JTextField getNameTextField() {
250     return myComponent.myNameText;
251   }
252
253   @Override
254   public String getDisplayName() {
255     return myDisplayName;
256   }
257
258   @Override
259   public String getHelpTopic() {
260     return myHelpTopic;
261   }
262
263   public Config getConfiguration() {
264     return (Config)getSettings().getConfiguration();
265   }
266
267   public RunnerAndConfigurationSettings getSnapshot() throws ConfigurationException {
268     final SettingsEditor<RunnerAndConfigurationSettings> editor = getEditor();
269     return editor == null ? null : editor.getSnapshot();
270   }
271
272   @Override
273   public String toString() {
274     return myDisplayName;
275   }
276
277   public void setFolderName(@Nullable String folderName) {
278     if (!Comparing.equal(myFolderName, folderName)) {
279       myFolderName = folderName;
280       setModified(true);
281     }
282   }
283
284   @Nullable
285   public String getFolderName() {
286     return myFolderName;
287   }
288
289   private class MyValidatableComponent {
290     private JLabel myNameLabel;
291     private JTextField myNameText;
292     private JComponent myWholePanel;
293     private JPanel myComponentPlace;
294     private JLabel myWarningLabel;
295     private JButton myFixButton;
296     private JSeparator mySeparator;
297     private JCheckBox myCbStoreProjectConfiguration;
298     private JBCheckBox myCbSingleton;
299     private JPanel myValidationPanel;
300
301     private Runnable myQuickFix = null;
302
303     public MyValidatableComponent() {
304       myNameLabel.setLabelFor(myNameText);
305       myNameText.setDocument(myNameDocument);
306
307       getEditor().addSettingsEditorListener(new SettingsEditorListener() {
308         @Override
309         public void stateChanged(SettingsEditor settingsEditor) {
310           updateWarning();
311         }
312       });
313
314       myWarningLabel.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
315
316       myComponentPlace.setLayout(new GridBagLayout());
317       myComponentPlace.add(getEditorComponent(),
318                            new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
319                                                   new Insets(0, 0, 0, 0), 0, 0));
320       myComponentPlace.doLayout();
321       myFixButton.setIcon(AllIcons.Actions.QuickfixBulb);
322       updateWarning();
323       myFixButton.addActionListener(new ActionListener() {
324         @Override
325         public void actionPerformed(final ActionEvent e) {
326           if (myQuickFix == null) {
327             return;
328           }
329           myQuickFix.run();
330           myValidationResultValid = false;
331           updateWarning();
332         }
333       });
334       ActionListener actionListener = new ActionListener() {
335         @Override
336         public void actionPerformed(ActionEvent e) {
337           setModified(true);
338           myStoreProjectConfiguration = myCbStoreProjectConfiguration.isSelected();
339           mySingleton = myCbSingleton.isSelected();
340         }
341       };
342       myCbStoreProjectConfiguration.addActionListener(actionListener);
343       myCbSingleton.addActionListener(actionListener);
344       settingAnchor();
345     }
346
347     private void doReset(RunnerAndConfigurationSettings settings) {
348       final RunConfiguration runConfiguration = settings.getConfiguration();
349       final RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(runConfiguration.getProject());
350       myStoreProjectConfiguration = runManager.isConfigurationShared(settings);
351       myCbStoreProjectConfiguration.setEnabled(!(runConfiguration instanceof UnknownRunConfiguration));
352       myCbStoreProjectConfiguration.setSelected(myStoreProjectConfiguration);
353       myCbStoreProjectConfiguration.setVisible(!settings.isTemplate());
354
355       mySingleton = settings.isSingleton();
356       myCbSingleton.setEnabled(!(runConfiguration instanceof UnknownRunConfiguration));
357       myCbSingleton.setSelected(mySingleton);
358       ConfigurationFactory factory = settings.getFactory();
359       myCbSingleton.setVisible(factory != null && factory.canConfigurationBeSingleton());
360     }
361
362     private void settingAnchor() {
363     }
364
365     public final JComponent getWholePanel() {
366       return myWholePanel;
367     }
368
369     public JComponent getEditorComponent() {
370       return getEditor().getComponent();
371     }
372
373     @Nullable
374     public ValidationResult getValidationResult() {
375       return SingleConfigurationConfigurable.this.getValidationResult();
376     }
377
378     private void updateWarning() {
379       final ValidationResult configurationException = getValidationResult();
380
381       if (configurationException != null) {
382         mySeparator.setVisible(true);
383         myWarningLabel.setVisible(true);
384         myWarningLabel.setText(generateWarningLabelText(configurationException));
385         final Runnable quickFix = configurationException.getQuickFix();
386         if (quickFix == null) {
387           myFixButton.setVisible(false);
388         }
389         else {
390           myFixButton.setVisible(true);
391           myQuickFix = quickFix;
392         }
393         myValidationPanel.setVisible(true);
394       }
395       else {
396         mySeparator.setVisible(false);
397         myWarningLabel.setVisible(false);
398         myFixButton.setVisible(false);
399         myValidationPanel.setVisible(false);
400       }
401     }
402
403     @NonNls
404     private String generateWarningLabelText(final ValidationResult configurationException) {
405       return "<html><body><b>" + configurationException.getTitle() + ": </b>" + configurationException.getMessage() + "</body></html>";
406     }
407   }
408 }