replaced <code></code> with more concise {@code}
[idea/community.git] / platform / external-system-impl / src / com / intellij / openapi / externalSystem / service / settings / AbstractExternalSystemConfigurable.java
1 /*
2  * Copyright 2000-2017 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 package com.intellij.openapi.externalSystem.service.settings;
17
18 import com.intellij.openapi.externalSystem.ExternalSystemManager;
19 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
20 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
21 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
22 import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener;
23 import com.intellij.openapi.externalSystem.util.*;
24 import com.intellij.openapi.options.Configurable;
25 import com.intellij.openapi.options.ConfigurationException;
26 import com.intellij.openapi.options.SearchableConfigurable;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.ui.IdeBorderFactory;
29 import com.intellij.ui.components.JBList;
30 import com.intellij.ui.components.JBScrollPane;
31 import com.intellij.util.containers.ContainerUtil;
32 import com.intellij.util.containers.ContainerUtilRt;
33 import com.intellij.util.ui.JBUI;
34 import org.jetbrains.annotations.Nls;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import javax.swing.*;
39 import javax.swing.event.ListSelectionEvent;
40 import javax.swing.event.ListSelectionListener;
41 import java.awt.*;
42 import java.io.File;
43 import java.util.Comparator;
44 import java.util.List;
45
46 /**
47  * Base class that simplifies external system settings management.
48  * <p/>
49  * The general idea is to provide a control which looks like below:
50  * <pre>
51  *    ----------------------------------------------
52  *   |   linked external projects list              |
53  *   |----------------------------------------------
54  *   |   linked project-specific settings           |
55  *   |----------------------------------------------
56  *   |   external system-wide settings (optional)   |
57       ----------------------------------------------
58  * </pre>
59  * 
60  * @author Denis Zhdanov
61  * @since 4/30/13 12:50 PM
62  */
63 public abstract class AbstractExternalSystemConfigurable<
64   ProjectSettings extends ExternalProjectSettings,
65   L extends ExternalSystemSettingsListener<ProjectSettings>,
66   SystemSettings extends AbstractExternalSystemSettings<SystemSettings, ProjectSettings, L>
67   > implements SearchableConfigurable, Configurable.NoScroll
68 {
69
70   @NotNull private final List<ExternalSystemSettingsControl<ProjectSettings>> myProjectSettingsControls = ContainerUtilRt.newArrayList();
71
72   @NotNull private final ProjectSystemId myExternalSystemId;
73   @NotNull private final Project         myProject;
74
75   @Nullable private ExternalSystemSettingsControl<SystemSettings>  mySystemSettingsControl;
76   @Nullable private ExternalSystemSettingsControl<ProjectSettings> myActiveProjectSettingsControl;
77
78   private PaintAwarePanel  myComponent;
79   private JBList           myProjectsList;
80   private DefaultListModel myProjectsModel;
81
82   protected AbstractExternalSystemConfigurable(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) {
83     myProject = project;
84     myExternalSystemId = externalSystemId;
85   }
86
87   @NotNull
88   public Project getProject() {
89     return myProject;
90   }
91
92   @Nls
93   @Override
94   public String getDisplayName() {
95     return myExternalSystemId.getReadableName();
96   }
97
98   @Nullable
99   @Override
100   public JComponent createComponent() {
101     if (myComponent == null) {
102       myComponent = new PaintAwarePanel(new GridBagLayout());
103       SystemSettings settings = getSettings();
104       prepareProjectSettings(settings);
105       prepareSystemSettings(settings);
106       ExternalSystemUiUtil.fillBottom(myComponent);
107     }
108     return myComponent;
109   }
110
111   @SuppressWarnings("unchecked")
112   @NotNull
113   private SystemSettings getSettings() {
114     ExternalSystemManager<ProjectSettings, L, SystemSettings, ?, ?> manager =
115       (ExternalSystemManager<ProjectSettings, L, SystemSettings, ?, ?>)ExternalSystemApiUtil.getManager(myExternalSystemId);
116     assert manager != null;
117     return manager.getSettingsProvider().fun(myProject);
118   }
119
120   @SuppressWarnings("unchecked")
121   private void prepareProjectSettings(@NotNull SystemSettings s) {
122     myProjectsModel = new DefaultListModel();
123     myProjectsList = new JBList(myProjectsModel);
124     myProjectsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
125
126     addTitle(ExternalSystemBundle.message("settings.title.linked.projects", myExternalSystemId.getReadableName()));
127     myComponent.add(new JBScrollPane(myProjectsList), ExternalSystemUiUtil.getFillLineConstraints(1));
128
129     addTitle(ExternalSystemBundle.message("settings.title.project.settings"));
130     List<ProjectSettings> settings = ContainerUtilRt.newArrayList(s.getLinkedProjectsSettings());
131     myProjectsList.setVisibleRowCount(Math.max(3, Math.min(5, settings.size())));
132     ContainerUtil.sort(settings, Comparator.comparing(s2 -> getProjectName(s2.getExternalProjectPath())));
133
134     myProjectSettingsControls.clear();
135     for (ProjectSettings setting : settings) {
136       ExternalSystemSettingsControl<ProjectSettings> control = createProjectSettingsControl(setting);
137       control.fillUi(myComponent, 1);
138       myProjectsModel.addElement(getProjectName(setting.getExternalProjectPath()));
139       myProjectSettingsControls.add(control);
140       if (control instanceof AbstractExternalProjectSettingsControl<?>) {
141         ((AbstractExternalProjectSettingsControl)control).setCurrentProject(myProject);
142       }
143       control.showUi(false);
144     }
145
146     myProjectsList.addListSelectionListener(new ListSelectionListener() {
147       @SuppressWarnings("unchecked")
148       @Override
149       public void valueChanged(ListSelectionEvent e) {
150         if (e.getValueIsAdjusting()) {
151           return;
152         }
153         int i = myProjectsList.getSelectedIndex();
154         if (i < 0) {
155           return;
156         }
157         if (myActiveProjectSettingsControl != null) {
158           myActiveProjectSettingsControl.showUi(false);
159         }
160         myActiveProjectSettingsControl = myProjectSettingsControls.get(i);
161         myActiveProjectSettingsControl.showUi(true);
162       }
163     });
164
165     
166     if (!myProjectsModel.isEmpty()) {
167       myProjectsList.setSelectedIndex(0);
168     }
169   }
170
171   public void selectProject(@NotNull String linkedProjectPath) {
172     myProjectsList.setSelectedValue(getProjectName(linkedProjectPath), true);
173   }
174   
175   private void addTitle(@NotNull String title) {
176     JPanel panel = new JPanel(new GridBagLayout());
177     panel.setBorder(IdeBorderFactory.createTitledBorder(title, false, JBUI.emptyInsets()));
178     myComponent.add(panel, ExternalSystemUiUtil.getFillLineConstraints(0));
179   }
180
181   /**
182    * Creates a control for managing given project settings.
183    * 
184    * @param settings  target external project settings
185    * @return          control for managing given project settings
186    */
187   @NotNull
188   protected abstract ExternalSystemSettingsControl<ProjectSettings> createProjectSettingsControl(@NotNull ProjectSettings settings);
189   
190   @SuppressWarnings("MethodMayBeStatic")
191   @NotNull
192   protected String getProjectName(@NotNull String path) {
193     File file = new File(path);
194     return file.isDirectory() || file.getParentFile() == null ? file.getName() : file.getParentFile().getName();
195   }
196
197   private void prepareSystemSettings(@NotNull SystemSettings s) {
198     mySystemSettingsControl = createSystemSettingsControl(s);
199     if (mySystemSettingsControl != null) {
200       addTitle(ExternalSystemBundle.message("settings.title.system.settings", myExternalSystemId.getReadableName()));
201       mySystemSettingsControl.fillUi(myComponent, 1);
202     }
203   }
204
205   /**
206    * Creates a control for managing given system-level settings (if any).
207    * 
208    * @param settings  target system settings
209    * @return          a control for managing given system-level settings;
210    *                  {@code null} if current external system doesn't have system-level settings (only project-level settings)
211    */
212   @Nullable
213   protected abstract ExternalSystemSettingsControl<SystemSettings> createSystemSettingsControl(@NotNull SystemSettings settings);
214
215   @Override
216   public boolean isModified() {
217     for (ExternalSystemSettingsControl<ProjectSettings> control : myProjectSettingsControls) {
218       if (control.isModified()) {
219         return true;
220       }
221     }
222     return mySystemSettingsControl != null && mySystemSettingsControl.isModified();
223   }
224
225   @Override
226   public void apply() throws ConfigurationException {
227     SystemSettings systemSettings = getSettings();
228     L publisher = systemSettings.getPublisher();
229     publisher.onBulkChangeStart();
230     try {
231       List<ProjectSettings> projectSettings = ContainerUtilRt.newArrayList();
232       for (ExternalSystemSettingsControl<ProjectSettings> control : myProjectSettingsControls) {
233         ProjectSettings s = newProjectSettings();
234         control.apply(s);
235         projectSettings.add(s);
236       }
237       systemSettings.setLinkedProjectsSettings(projectSettings);
238       for (ExternalSystemSettingsControl<ProjectSettings> control : myProjectSettingsControls) {
239         if(control instanceof AbstractExternalProjectSettingsControl){
240           AbstractExternalProjectSettingsControl.class.cast(control).updateInitialSettings();
241         }
242       }
243       if (mySystemSettingsControl != null) {
244         mySystemSettingsControl.apply(systemSettings);
245       }
246     }
247     finally {
248       publisher.onBulkChangeEnd();
249     }
250   }
251
252   /**
253    * @return    new empty project-level settings object
254    */
255   @NotNull
256   protected abstract ProjectSettings newProjectSettings();
257
258   @Override
259   public void reset() {
260     for (ExternalSystemSettingsControl<ProjectSettings> control : myProjectSettingsControls) {
261       control.reset();
262     }
263     if (mySystemSettingsControl != null) {
264       mySystemSettingsControl.reset();
265     }
266   }
267
268   @Override
269   public void disposeUIResources() {
270     for (ExternalSystemSettingsControl<ProjectSettings> control : myProjectSettingsControls) {
271       control.disposeUIResources();
272     }
273     myProjectSettingsControls.clear();
274     myComponent = null;
275     myProjectsList = null;
276     myProjectsModel = null;
277     mySystemSettingsControl = null;
278   }
279 }