/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.options.newEditor; import com.intellij.CommonBundle; import com.intellij.ide.ui.search.SearchUtil; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; import java.util.Map; public class OptionsEditorDialog extends DialogWrapper implements DataProvider{ private Project myProject; private ConfigurableGroup[] myGroups; private Configurable myPreselected; private OptionsEditor myEditor; private ApplyAction myApplyAction; public static final String DIMENSION_KEY = "OptionsEditor"; @NonNls static final String LAST_SELECTED_CONFIGURABLE = "options.lastSelected"; /** This constructor should be eliminated after the new modality approach * will have been checked. See a {@code Registry} key ide.perProjectModality * @deprecated */ public OptionsEditorDialog(Project project, ConfigurableGroup[] groups, @Nullable Configurable preselectedConfigurable, boolean applicationModalIfPossible) { super(project, true, applicationModalIfPossible); init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); } /** This constructor should be eliminated after the new modality approach * will have been checked. See a {@code Registry} key ide.perProjectModality * @deprecated */ public OptionsEditorDialog(Project project, ConfigurableGroup[] groups, @NotNull String preselectedConfigurableDisplayName, boolean applicationModalIfPossible) { super(project, true, applicationModalIfPossible); init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); } public OptionsEditorDialog(Project project, ConfigurableGroup[] groups, @Nullable Configurable preselectedConfigurable) { super(project, true); init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); } public OptionsEditorDialog(Project project, ConfigurableGroup[] groups, @NotNull String preselectedConfigurableDisplayName) { super(project, true); init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); } private void init(final Project project, final ConfigurableGroup[] groups, @Nullable final Configurable preselected) { myProject = project; myGroups = groups; myPreselected = preselected; setTitle(CommonBundle.settingsTitle()); init(); } @Nullable private static Configurable getPreselectedByDisplayName(final ConfigurableGroup[] groups, final String preselectedConfigurableDisplayName, final Project project) { Configurable result = findPreselectedByDisplayName(preselectedConfigurableDisplayName, groups); return result == null ? findLastSavedConfigurable(groups, project) : result; } @Override public boolean isTypeAheadEnabled() { return true; } protected JComponent createCenterPanel() { myEditor = new OptionsEditor(myProject, myGroups, myPreselected); myEditor.getContext().addColleague(new OptionsEditorColleague.Adapter() { @Override public ActionCallback onModifiedAdded(final Configurable configurable) { updateStatus(); return new ActionCallback.Done(); } @Override public ActionCallback onModifiedRemoved(final Configurable configurable) { updateStatus(); return new ActionCallback.Done(); } @Override public ActionCallback onErrorsChanged() { updateStatus(); return new ActionCallback.Done(); } }); Disposer.register(myDisposable, myEditor); return myEditor; } public boolean updateStatus() { myApplyAction.setEnabled(myEditor.canApply()); final Map errors = myEditor.getContext().getErrors(); if (errors.size() == 0) { setErrorText(null); } else { String text = "Changes were not applied because of an error"; final String errorMessage = getErrorMessage(errors); if (errorMessage != null) { text += "
" + errorMessage; } setErrorText(text); } return errors.size() == 0; } @Nullable private static String getErrorMessage(final Map errors) { final Collection values = errors.values(); final ConfigurationException[] exceptions = values.toArray(new ConfigurationException[values.size()]); if (exceptions.length > 0) { return exceptions[0].getMessage(); } return null; } @Override protected String getDimensionServiceKey() { return DIMENSION_KEY; } @Override protected void doOKAction() { myEditor.flushModifications(); if (myEditor.canApply()) { myEditor.apply(); if (!updateStatus()) return; } saveCurrentConfigurable(); ApplicationManager.getApplication().saveAll(); super.doOKAction(); } private void saveCurrentConfigurable() { final Configurable current = myEditor.getContext().getCurrentConfigurable(); if (current == null) return; final PropertiesComponent props = PropertiesComponent.getInstance(myProject); if (current instanceof SearchableConfigurable) { props.setValue(LAST_SELECTED_CONFIGURABLE, ((SearchableConfigurable)current).getId()); } else { props.setValue(LAST_SELECTED_CONFIGURABLE, current.getClass().getName()); } } @Nullable private static Configurable findLastSavedConfigurable(ConfigurableGroup[] groups, final Project project) { final String id = PropertiesComponent.getInstance(project).getValue(LAST_SELECTED_CONFIGURABLE); if (id == null) return null; return findConfigurableInGroups(id, groups); } @Nullable private static Configurable findConfigurableInGroups(String id, Configurable.Composite... groups) { // avoid unnecessary group expand: check top-level configurables in all groups before looking at children for (Configurable.Composite group : groups) { final Configurable[] configurables = group.getConfigurables(); for (Configurable c : configurables) { if (c instanceof SearchableConfigurable && id.equals(((SearchableConfigurable)c).getId())) { return c; } else if (id.equals(c.getClass().getName())) { return c; } } } for (Configurable.Composite group : groups) { final Configurable[] configurables = group.getConfigurables(); for (Configurable c : configurables) { if (c instanceof Configurable.Composite) { Configurable result = findConfigurableInGroups(id, (Configurable.Composite)c); if (result != null) { return result; } } } } return null; } @Nullable private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { final List all = SearchUtil.expand(groups); for (Configurable each : all) { if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; } return null; } @Override public void doCancelAction(final AWTEvent source) { if (source instanceof KeyEvent || source instanceof ActionEvent) { if (myEditor.getContext().isHoldingFilter()) { myEditor.clearFilter(); return; } } super.doCancelAction(source); } @Override public void doCancelAction() { saveCurrentConfigurable(); super.doCancelAction(); } @NotNull @Override protected Action[] createActions() { myApplyAction = new ApplyAction(); return new Action[] {getOKAction(), getCancelAction(), myApplyAction, getHelpAction()}; } @Override protected void doHelpAction() { final String topic = myEditor.getHelpTopic(); if (topic != null) { HelpManager.getInstance().invokeHelp(topic); } } @Override public JComponent getPreferredFocusedComponent() { return myEditor.getPreferredFocusedComponent(); } public Object getData(@NonNls String dataId) { if (OptionsEditor.KEY.is(dataId)) { return myEditor; } return null; } private class ApplyAction extends AbstractAction { public ApplyAction() { super(CommonBundle.getApplyButtonText()); setEnabled(false); } public void actionPerformed(final ActionEvent e) { myEditor.apply(); myEditor.revalidate(); myEditor.repaint(); } } }