2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.facet.impl.ui.libraries;
18 import com.intellij.framework.library.DownloadableLibraryDescription;
19 import com.intellij.framework.library.DownloadableLibraryType;
20 import com.intellij.framework.library.FrameworkLibraryVersion;
21 import com.intellij.framework.library.FrameworkLibraryVersionFilter;
22 import com.intellij.icons.AllIcons;
23 import com.intellij.ide.IdeBundle;
24 import com.intellij.ide.util.frameworkSupport.OldCustomLibraryDescription;
25 import com.intellij.openapi.Disposable;
26 import com.intellij.openapi.application.Result;
27 import com.intellij.openapi.application.WriteAction;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.project.DumbModePermission;
30 import com.intellij.openapi.project.DumbService;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.project.ProjectManager;
33 import com.intellij.openapi.roots.OrderRootType;
34 import com.intellij.openapi.roots.impl.libraries.LibraryEx;
35 import com.intellij.openapi.roots.libraries.Library;
36 import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
37 import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
38 import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
39 import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
40 import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
41 import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
42 import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
43 import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
44 import com.intellij.openapi.util.Comparing;
45 import com.intellij.openapi.util.Disposer;
46 import com.intellij.openapi.util.NotNullComputable;
47 import com.intellij.openapi.util.io.FileUtil;
48 import com.intellij.openapi.util.text.StringUtil;
49 import com.intellij.openapi.vfs.LocalFileSystem;
50 import com.intellij.openapi.vfs.VirtualFile;
51 import com.intellij.ui.ColoredListCellRenderer;
52 import com.intellij.ui.SortedComboBoxModel;
53 import com.intellij.ui.components.JBLabel;
54 import com.intellij.util.PathUtil;
55 import com.intellij.util.PlatformIcons;
56 import com.intellij.util.containers.ContainerUtil;
57 import com.intellij.util.download.DownloadableFileSetVersions;
58 import com.intellij.util.ui.RadioButtonEnumModel;
59 import com.intellij.xml.util.XmlStringUtil;
60 import org.jetbrains.annotations.NotNull;
61 import org.jetbrains.annotations.Nullable;
65 import java.awt.event.ActionEvent;
66 import java.awt.event.ActionListener;
67 import java.awt.event.ItemEvent;
68 import java.awt.event.ItemListener;
69 import java.text.MessageFormat;
70 import java.util.ArrayList;
71 import java.util.Comparator;
72 import java.util.List;
75 * @author Dmitry Avdeev
77 public class LibraryOptionsPanel implements Disposable {
78 private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel");
80 private JBLabel myMessageLabel;
81 private JPanel myPanel;
82 private JButton myConfigureButton;
83 private JComboBox myExistingLibraryComboBox;
84 private JRadioButton myDoNotCreateRadioButton;
85 private JPanel myConfigurationPanel;
86 private JButton myCreateButton;
87 private JRadioButton myDownloadRadioButton;
88 private JRadioButton myUseLibraryRadioButton;
89 private JLabel myUseLibraryLabel;
90 private JLabel myHiddenLabel;
91 private JPanel myRootPanel;
92 private JRadioButton myUseFromProviderRadioButton;
93 private JPanel mySimplePanel;
94 private ButtonGroup myButtonGroup;
96 private LibraryCompositionSettings mySettings;
97 private final CustomLibraryDescription myLibraryDescription;
98 private final LibrariesContainer myLibrariesContainer;
99 private SortedComboBoxModel<LibraryEditor> myLibraryComboBoxModel;
100 private FrameworkLibraryProvider myLibraryProvider;
101 private boolean myDisposed;
103 private enum Choice {
110 private RadioButtonEnumModel<Choice> myButtonEnumModel;
112 public LibraryOptionsPanel(@NotNull final CustomLibraryDescription libraryDescription,
113 @NotNull final String path,
114 @NotNull final FrameworkLibraryVersionFilter versionFilter,
115 @NotNull final LibrariesContainer librariesContainer,
116 final boolean showDoNotCreateOption) {
118 this(libraryDescription, () -> path, versionFilter, librariesContainer, showDoNotCreateOption);
121 public LibraryOptionsPanel(@NotNull final CustomLibraryDescription libraryDescription,
122 @NotNull final NotNullComputable<String> pathProvider,
123 @NotNull final FrameworkLibraryVersionFilter versionFilter,
124 @NotNull final LibrariesContainer librariesContainer,
125 final boolean showDoNotCreateOption) {
126 myLibraryDescription = libraryDescription;
127 myLibrariesContainer = librariesContainer;
128 final DownloadableLibraryDescription description = getDownloadableDescription(libraryDescription);
129 if (description != null) {
131 description.fetchVersions(new DownloadableFileSetVersions.FileSetVersionsCallback<FrameworkLibraryVersion>() {
133 public void onSuccess(@NotNull final List<? extends FrameworkLibraryVersion> versions) {
134 //noinspection SSBasedInspection
135 SwingUtilities.invokeLater(() -> {
137 showSettingsPanel(libraryDescription, pathProvider, versionFilter, showDoNotCreateOption, versions);
138 onVersionChanged(getPresentableVersion());
145 showSettingsPanel(libraryDescription, pathProvider, versionFilter, showDoNotCreateOption,
151 private String getPresentableVersion() {
152 switch (myButtonEnumModel.getSelected()) {
154 LibraryDownloadSettings settings = mySettings.getDownloadSettings();
155 if (settings != null) {
156 return settings.getVersion().getVersionNumber();
160 LibraryEditor item = myLibraryComboBoxModel.getSelectedItem();
161 if (item instanceof ExistingLibraryEditor) {
162 return item.getName();
171 protected void onVersionChanged(@Nullable String version) {
174 public JPanel getSimplePanel() {
175 return mySimplePanel;
179 private static DownloadableLibraryDescription getDownloadableDescription(CustomLibraryDescription libraryDescription) {
180 final DownloadableLibraryType type = libraryDescription.getDownloadableLibraryType();
181 if (type != null) return type.getLibraryDescription();
182 if (libraryDescription instanceof OldCustomLibraryDescription) {
183 return ((OldCustomLibraryDescription)libraryDescription).getDownloadableDescription();
188 private void showCard(final String editing) {
189 ((CardLayout)myRootPanel.getLayout()).show(myRootPanel, editing);
192 private void showSettingsPanel(CustomLibraryDescription libraryDescription,
193 NotNullComputable<String> pathProvider,
194 FrameworkLibraryVersionFilter versionFilter,
195 boolean showDoNotCreateOption, final List<? extends FrameworkLibraryVersion> versions) {
196 //todo[nik] create mySettings only in apply() method
197 mySettings = new LibraryCompositionSettings(libraryDescription, pathProvider, versionFilter, versions);
198 Disposer.register(this, mySettings);
199 List<Library> libraries = calculateSuitableLibraries();
201 myButtonEnumModel = RadioButtonEnumModel.bindEnum(Choice.class, myButtonGroup);
202 myButtonEnumModel.addActionListener(new ActionListener() {
204 public void actionPerformed(ActionEvent e) {
206 onVersionChanged(getPresentableVersion());
210 myDoNotCreateRadioButton.setVisible(showDoNotCreateOption);
211 myLibraryComboBoxModel = new SortedComboBoxModel<>((o1, o2) -> {
212 final String name1 = o1.getName();
213 final String name2 = o2.getName();
214 return -StringUtil.notNullize(name1).compareToIgnoreCase(StringUtil.notNullize(name2));
217 for (Library library : libraries) {
218 ExistingLibraryEditor libraryEditor = myLibrariesContainer.getLibraryEditor(library);
219 if (libraryEditor == null) {
220 libraryEditor = mySettings.getOrCreateEditor(library);
222 myLibraryComboBoxModel.add(libraryEditor);
224 myExistingLibraryComboBox.setModel(myLibraryComboBoxModel);
225 if (libraries.isEmpty()) {
226 myLibraryComboBoxModel.add(null);
228 myExistingLibraryComboBox.setSelectedIndex(0);
229 myExistingLibraryComboBox.addItemListener(new ItemListener() {
231 public void itemStateChanged(ItemEvent e) {
232 if (e.getStateChange() == ItemEvent.SELECTED && e.getItem() != null) {
233 myButtonEnumModel.setSelected(Choice.USE_LIBRARY);
236 onVersionChanged(getPresentableVersion());
239 myExistingLibraryComboBox.setRenderer(new ColoredListCellRenderer(myExistingLibraryComboBox) {
241 protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) {
243 append("[No library selected]");
245 else if (value instanceof ExistingLibraryEditor) {
246 final Library library = ((ExistingLibraryEditor)value).getLibrary();
247 final boolean invalid = !((LibraryEx)library).getInvalidRootUrls(OrderRootType.CLASSES).isEmpty();
248 OrderEntryAppearanceService.getInstance().forLibrary(getProject(), library, invalid).customize(this);
250 else if (value instanceof NewLibraryEditor) {
251 setIcon(PlatformIcons.LIBRARY_ICON);
252 final String name = ((NewLibraryEditor)value).getName();
253 append(name != null ? name : "<unnamed>");
258 boolean canDownload = mySettings.getDownloadSettings() != null;
259 boolean canUseFromProvider = myLibraryProvider != null;
260 myDownloadRadioButton.setVisible(canDownload);
261 myUseFromProviderRadioButton.setVisible(canUseFromProvider);
262 Choice selectedOption;
263 if (canUseFromProvider) {
264 selectedOption = Choice.USE_FROM_PROVIDER;
266 else if (libraries.isEmpty() && canDownload) {
267 selectedOption = Choice.DOWNLOAD;
270 selectedOption = Choice.USE_LIBRARY;
273 myButtonEnumModel.setSelected(selectedOption);
275 if (!canDownload && !canUseFromProvider && !showDoNotCreateOption) {
276 myUseLibraryRadioButton.setVisible(false);
277 myUseLibraryLabel.setVisible(true);
280 myUseLibraryLabel.setVisible(false);
283 final Dimension minimumSize = new Dimension(-1, myMessageLabel.getFontMetrics(myMessageLabel.getFont()).getHeight() * 2);
284 myHiddenLabel.setMinimumSize(minimumSize);
286 myCreateButton.addActionListener(new ActionListener() {
288 public void actionPerformed(ActionEvent e) {
292 myConfigureButton.addActionListener(new ActionListener() {
293 public void actionPerformed(final ActionEvent e) {
294 DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, () -> doConfigure());
301 private Project getProject() {
302 Project project = myLibrariesContainer.getProject();
303 if (project == null) {
304 project = ProjectManager.getInstance().getDefaultProject();
309 private void doConfigure() {
310 switch (myButtonEnumModel.getSelected()) {
312 final LibraryDownloadSettings oldDownloadSettings = mySettings.getDownloadSettings();
313 LOG.assertTrue(oldDownloadSettings != null);
314 final LibraryDownloadSettings newDownloadSettings = DownloadingOptionsDialog.showDialog(myPanel, oldDownloadSettings,
315 mySettings.getCompatibleVersions(), true);
316 if (newDownloadSettings != null) {
317 mySettings.setDownloadSettings(newDownloadSettings);
322 final Object item = myExistingLibraryComboBox.getSelectedItem();
323 if (item instanceof LibraryEditor) {
324 EditLibraryDialog dialog = new EditLibraryDialog(myPanel, mySettings, (LibraryEditor)item);
326 if (item instanceof ExistingLibraryEditor) {
328 protected void run(@NotNull final Result result) {
329 ((ExistingLibraryEditor)item).commit();
336 case USE_FROM_PROVIDER:
337 case SETUP_LIBRARY_LATER:
343 public void setLibraryProvider(@Nullable FrameworkLibraryProvider provider) {
344 if (provider != null && !ContainerUtil.intersects(provider.getAvailableLibraryKinds(), myLibraryDescription.getSuitableLibraryKinds())) {
348 if (!Comparing.equal(myLibraryProvider, provider)) {
349 myLibraryProvider = provider;
351 if (mySettings != null) {
352 if (provider != null && !myUseFromProviderRadioButton.isVisible()) {
353 myUseFromProviderRadioButton.setSelected(true);
355 myUseFromProviderRadioButton.setVisible(provider != null);
361 public void setVersionFilter(@NotNull FrameworkLibraryVersionFilter versionFilter) {
362 if (mySettings != null) {
363 mySettings.setVersionFilter(versionFilter);
368 private void doCreate(boolean useDefaultSettings) {
369 final NewLibraryConfiguration libraryConfiguration = useDefaultSettings
370 ? myLibraryDescription.createNewLibraryWithDefaultSettings(getBaseDirectory())
371 : myLibraryDescription.createNewLibrary(myCreateButton, getBaseDirectory());
372 if (libraryConfiguration != null) {
373 final NewLibraryEditor libraryEditor = new NewLibraryEditor(libraryConfiguration.getLibraryType(), libraryConfiguration.getProperties());
374 libraryEditor.setName(myLibrariesContainer.suggestUniqueLibraryName(libraryConfiguration.getDefaultLibraryName()));
375 libraryConfiguration.addRoots(libraryEditor);
376 if (myLibraryComboBoxModel.get(0) == null) {
377 myLibraryComboBoxModel.remove(0);
379 myLibraryComboBoxModel.add(libraryEditor);
380 myLibraryComboBoxModel.setSelectedItem(libraryEditor);
381 myButtonEnumModel.setSelected(Choice.USE_LIBRARY);
385 private List<Library> calculateSuitableLibraries() {
386 List<Library> suitableLibraries = new ArrayList<>();
387 for (Library library : myLibrariesContainer.getAllLibraries()) {
388 if (myLibraryDescription instanceof OldCustomLibraryDescription &&
389 ((OldCustomLibraryDescription)myLibraryDescription).isSuitableLibrary(library, myLibrariesContainer)
390 || LibraryPresentationManager.getInstance().isLibraryOfKind(library, myLibrariesContainer, myLibraryDescription.getSuitableLibraryKinds())) {
391 suitableLibraries.add(library);
394 return suitableLibraries;
398 private VirtualFile getBaseDirectory() {
399 String path = mySettings.getBaseDirectoryPath();
400 VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(path);
402 path = path.substring(0, path.lastIndexOf('/'));
403 dir = LocalFileSystem.getInstance().findFileByPath(path);
408 private void updateState() {
409 myMessageLabel.setIcon(null);
410 myConfigureButton.setVisible(true);
411 final LibraryDownloadSettings settings = mySettings.getDownloadSettings();
412 myDownloadRadioButton.setVisible(settings != null);
413 myUseFromProviderRadioButton.setVisible(myLibraryProvider != null);
414 if (!myUseFromProviderRadioButton.isVisible() && myUseFromProviderRadioButton.isSelected()) {
415 if (myDownloadRadioButton.isVisible()) {
416 myDownloadRadioButton.setSelected(true);
419 myUseLibraryRadioButton.setSelected(true);
422 if (!myDownloadRadioButton.isVisible() && myDownloadRadioButton.isSelected() && myUseLibraryRadioButton.isVisible()) {
423 myUseLibraryRadioButton.setSelected(true);
426 boolean showConfigurePanel = true;
427 switch (myButtonEnumModel.getSelected()) {
429 message = getDownloadFilesMessage();
431 case USE_FROM_PROVIDER:
432 if (myLibraryProvider != null) {
433 message = "Library from " + myLibraryProvider.getPresentableName() + " will be used";
435 myConfigureButton.setVisible(false);
438 final Object item = myExistingLibraryComboBox.getSelectedItem();
440 myMessageLabel.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
441 message = "<b>Error:</b> library is not specified";
442 myConfigureButton.setVisible(false);
444 else if (item instanceof NewLibraryEditor) {
445 final LibraryEditor libraryEditor = (LibraryEditor)item;
446 message = IdeBundle.message("label.library.will.be.created.description.text", mySettings.getNewLibraryLevel(),
447 libraryEditor.getName(), libraryEditor.getFiles(OrderRootType.CLASSES).length);
450 message = MessageFormat.format("<b>{0}</b> library will be used", ((ExistingLibraryEditor)item).getName());
454 showConfigurePanel = false;
457 if (myLibraryProvider != null) {
458 myUseFromProviderRadioButton.setText("Use library from " + myLibraryProvider.getPresentableName());
461 //show the longest message on the hidden card to ensure that dialog won't jump if user selects another option
462 if (mySettings.getDownloadSettings() != null) {
463 myHiddenLabel.setText(getDownloadFilesMessage());
466 myHiddenLabel.setText(IdeBundle.message("label.library.will.be.created.description.text", mySettings.getNewLibraryLevel(),
469 ((CardLayout)myConfigurationPanel.getLayout()).show(myConfigurationPanel, showConfigurePanel ? "configure" : "empty");
470 myMessageLabel.setText(XmlStringUtil.wrapInHtml(message));
473 private String getDownloadFilesMessage() {
474 final LibraryDownloadSettings downloadSettings = mySettings.getDownloadSettings();
475 if (downloadSettings == null) return "";
477 final String downloadPath = downloadSettings.getDirectoryForDownloadedLibrariesPath();
478 final String basePath = mySettings.getBaseDirectoryPath();
480 if (!StringUtil.isEmpty(basePath) && FileUtil.startsWith(downloadPath, basePath)) {
481 path = FileUtil.getRelativePath(basePath, downloadPath, '/');
484 path = PathUtil.getFileName(downloadPath);
486 return MessageFormat.format("{0} {0, choice, 1#JAR|2#JARs} will be downloaded into <b>{1}</b> directory<br>" +
487 "{2} library <b>{3}</b> will be created",
488 downloadSettings.getSelectedDownloads().size(),
490 downloadSettings.getLibraryLevel(),
491 downloadSettings.getLibraryName());
494 public LibraryCompositionSettings getSettings() {
499 public LibraryCompositionSettings apply() {
500 if (mySettings == null) return null;
502 final Choice option = myButtonEnumModel.getSelected();
503 mySettings.setDownloadLibraries(option == Choice.DOWNLOAD);
505 final Object item = myExistingLibraryComboBox.getSelectedItem();
506 if (option == Choice.USE_LIBRARY && item instanceof ExistingLibraryEditor) {
507 mySettings.setSelectedExistingLibrary(((ExistingLibraryEditor)item).getLibrary());
510 mySettings.setSelectedExistingLibrary(null);
513 if (option == Choice.USE_LIBRARY && item instanceof NewLibraryEditor) {
514 mySettings.setNewLibraryEditor((NewLibraryEditor)item);
517 mySettings.setNewLibraryEditor(null);
520 mySettings.setLibraryProvider(option == Choice.USE_FROM_PROVIDER ? myLibraryProvider : null);
524 public JComponent getMainPanel() {
529 public void dispose() {