blob: 2a94fdb527786bb5b3fe45597bbce9651faf375c [file] [log] [blame]
/*
* Copyright 2000-2012 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.lang.javascript.boilerplate;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.projectWizard.SettingsStep;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.WebProjectGenerator;
import com.intellij.platform.templates.github.GithubTagInfo;
import com.intellij.ui.JBColor;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
/**
* @author Sergey Simonchik
*/
public class GithubProjectGeneratorPeer implements WebProjectGenerator.GeneratorPeer<GithubTagInfo> {
private static final String CONTROL_PLACE = "Github.Project.Generator.Reload";
private enum UpdateStatus {
UPDATING, IDLE
}
private final List<WebProjectGenerator.SettingsStateListener> myListeners = ContainerUtil.newArrayList();
private final GithubTagInfo myMasterTag;
private final GithubTagListProvider myTagListProvider;
private final AsyncProcessIcon myLoadingVersionIcon = new AsyncProcessIcon("Getting github tags");
private final JLabel myErrorMessage = new JLabel();
private JComboBox myComboBox;
private JComponent myComponent;
private JPanel myVersionPanel;
private JPanel myActionPanel;
private UpdateStatus myUpdateStatus;
public GithubProjectGeneratorPeer(@NotNull AbstractGithubTagDownloadedProjectGenerator generator) {
myErrorMessage.setForeground(JBColor.RED);
String ghUserName = generator.getGithubUserName();
String ghRepoName = generator.getGithubRepositoryName();
myMasterTag = new GithubTagInfo(
"master",
String.format("https://github.com/%s/%s/zipball/master", ghUserName, ghRepoName)
);
myComboBox.setRenderer(new ListCellRendererWrapper<GithubTagInfo>() {
@Override
public void customize(JList list, GithubTagInfo tag, int index, boolean selected, boolean hasFocus) {
final String text;
if (tag == null) {
text = isBackgroundJobRunning() ? "Loading..." : "Unavailable";
}
else {
text = tag.getName();
}
setText(text);
}
});
myTagListProvider = new GithubTagListProvider(ghUserName, ghRepoName);
fillActionPanel();
ImmutableSet<GithubTagInfo> cachedTags = myTagListProvider.getCachedTags();
if (cachedTags != null) {
onTagsUpdated(cachedTags);
}
reloadTagsInBackground();
}
void onTagsUpdated(@NotNull ImmutableSet<GithubTagInfo> tags) {
changeUpdateStatus(UpdateStatus.IDLE);
if (!shouldUpdate(tags)) {
return;
}
List<GithubTagInfo> sortedTags = createSortedTagList(tags);
GithubTagInfo selectedItem = getSelectedTag();
if (selectedItem == null && sortedTags.size() > 0) {
selectedItem = sortedTags.get(0);
}
myComboBox.removeAllItems();
myComboBox.addItem(myMasterTag);
for (GithubTagInfo tag : sortedTags) {
myComboBox.addItem(tag);
}
if (selectedItem != null) {
// restore previously selected item
for (int i = 0; i < myComboBox.getItemCount(); i++) {
GithubTagInfo item = GithubTagInfo.tryCast(myComboBox.getItemAt(i));
if (item != null && item.getName().equals(selectedItem.getName())) {
myComboBox.setSelectedIndex(i);
break;
}
}
}
myComboBox.updateUI();
fireStateChanged();
}
void onTagsUpdateError(@NotNull final String errorMessage) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (getSelectedTag() == null) {
myErrorMessage.setText(errorMessage);
}
changeUpdateStatus(UpdateStatus.IDLE);
}
});
}
private boolean shouldUpdate(@NotNull ImmutableSet<GithubTagInfo> newTags) {
if (myComboBox.getItemCount() == 0) {
return true;
}
int count = myComboBox.getItemCount();
Set<GithubTagInfo> oldTags = Sets.newHashSet();
for (int i = 1; i < count; i++) {
GithubTagInfo item = ObjectUtils.tryCast(myComboBox.getItemAt(i), GithubTagInfo.class);
if (item != null) {
oldTags.add(item);
}
}
return !oldTags.equals(newTags);
}
@NotNull
private static List<GithubTagInfo> createSortedTagList(@NotNull ImmutableCollection<GithubTagInfo> tags) {
List<GithubTagInfo> sortedTags = ContainerUtil.newArrayList(tags);
Collections.sort(sortedTags, new Comparator<GithubTagInfo>() {
@Override
public int compare(GithubTagInfo tag1, GithubTagInfo tag2) {
GithubTagInfo.Version v1 = tag1.getVersion();
GithubTagInfo.Version v2 = tag2.getVersion();
return v2.compareTo(v1);
}
});
for (GithubTagInfo tag : sortedTags) {
tag.setRecentTag(false);
}
if (!sortedTags.isEmpty()) {
sortedTags.get(0).setRecentTag(true);
}
return sortedTags;
}
@NotNull
@Override
public JComponent getComponent() {
return myComponent;
}
@Override
public void buildUI(@NotNull SettingsStep settingsStep) {
settingsStep.addSettingsField("\u001BVersion:", myVersionPanel);
settingsStep.addSettingsComponent(myErrorMessage);
}
@NotNull
@Override
public GithubTagInfo getSettings() {
GithubTagInfo tag = getSelectedTag();
if (tag == null) {
throw new RuntimeException("[internal error] No versions available.");
}
return tag;
}
@Override
@Nullable
public ValidationInfo validate() {
GithubTagInfo tag = getSelectedTag();
if (tag != null) {
return null;
}
String errorMessage = StringUtil.notNullize(myErrorMessage.getText());
if (errorMessage.isEmpty()) {
errorMessage = "Versions have not been loaded yet.";
}
return new ValidationInfo(errorMessage);
}
@Override
public boolean isBackgroundJobRunning() {
return myUpdateStatus == UpdateStatus.UPDATING;
}
@Override
public void addSettingsStateListener(@NotNull WebProjectGenerator.SettingsStateListener listener) {
myListeners.add(listener);
}
@Nullable
private GithubTagInfo getSelectedTag() {
return GithubTagInfo.tryCast(myComboBox.getSelectedItem());
}
private void fireStateChanged() {
GithubTagInfo tag = getSelectedTag();
for (WebProjectGenerator.SettingsStateListener listener : myListeners) {
listener.stateChanged(tag != null);
}
}
private void reloadTagsInBackground() {
changeUpdateStatus(UpdateStatus.UPDATING);
myErrorMessage.setText(null);
myTagListProvider.updateTagListAsynchronously(this);
}
private void changeUpdateStatus(@NotNull UpdateStatus status) {
CardLayout cardLayout = (CardLayout) myActionPanel.getLayout();
cardLayout.show(myActionPanel, status.name());
if (status == UpdateStatus.UPDATING) {
myLoadingVersionIcon.resume();
}
else {
myLoadingVersionIcon.suspend();
}
myUpdateStatus = status;
}
private void fillActionPanel() {
myActionPanel.add(createReloadButtonPanel(), UpdateStatus.IDLE.name());
myActionPanel.add(createReloadInProgressPanel(), UpdateStatus.UPDATING.name());
changeUpdateStatus(UpdateStatus.IDLE);
}
@NotNull
private JPanel createReloadButtonPanel() {
ReloadAction reloadAction = new ReloadAction();
ActionButton reloadButton = new ActionButton(
reloadAction,
reloadAction.getTemplatePresentation().clone(),
CONTROL_PLACE,
ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE
);
JPanel panel = new JPanel(new BorderLayout(0, 0));
panel.add(reloadButton, BorderLayout.WEST);
return panel;
}
@NotNull
private JPanel createReloadInProgressPanel() {
JPanel panel = new JPanel();
panel.add(myLoadingVersionIcon);
return panel;
}
private class ReloadAction extends AnAction {
private ReloadAction() {
super("Reload versions", null, AllIcons.Actions.Refresh);
}
@Override
public void actionPerformed(AnActionEvent e) {
GithubProjectGeneratorPeer.this.reloadTagsInBackground();
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(true);
}
}
}