| package com.intellij.tasks.redmine; |
| |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.ComboBox; |
| import com.intellij.tasks.config.BaseRepositoryEditor; |
| import com.intellij.tasks.impl.TaskUiUtil; |
| import com.intellij.tasks.redmine.model.RedmineProject; |
| import com.intellij.ui.ListCellRendererWrapper; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.util.Consumer; |
| import com.intellij.util.containers.Stack; |
| import com.intellij.util.ui.FormBuilder; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * @author Mikhail Golubev |
| * @author Dennis.Ushakov |
| */ |
| public class RedmineRepositoryEditor extends BaseRepositoryEditor<RedmineRepository> { |
| private ComboBox myProjectCombo; |
| private JTextField myAPIKey; |
| private JBLabel myProjectLabel; |
| private JBLabel myAPIKeyLabel; |
| |
| public RedmineRepositoryEditor(final Project project, final RedmineRepository repository, Consumer<RedmineRepository> changeListener) { |
| super(project, repository, changeListener); |
| |
| myTestButton.setEnabled(myRepository.isConfigured()); |
| myAPIKey.setText(repository.getAPIKey()); |
| |
| installListener(myProjectCombo); |
| installListener(myAPIKey); |
| |
| toggleCredentialsVisibility(); |
| |
| UIUtil.invokeLaterIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| initialize(); |
| } |
| }); |
| } |
| |
| @Override |
| protected void afterTestConnection(boolean connectionSuccessful) { |
| if (connectionSuccessful) { |
| new FetchProjectsTask().queue(); |
| } |
| else { |
| myProjectCombo.removeAllItems(); |
| } |
| } |
| |
| private void initialize() { |
| final RedmineProject currentProject = myRepository.getCurrentProject(); |
| if (currentProject != null && myRepository.isConfigured()) { |
| new FetchProjectsTask().queue(); |
| } |
| else { |
| myProjectCombo.removeAllItems(); |
| } |
| } |
| |
| @Override |
| public void apply() { |
| super.apply(); |
| RedmineProjectItem selected = (RedmineProjectItem)myProjectCombo.getSelectedItem(); |
| myRepository.setCurrentProject(selected != null ? selected.myProject : null); |
| myRepository.setAPIKey(myAPIKey.getText().trim()); |
| myTestButton.setEnabled(myRepository.isConfigured()); |
| toggleCredentialsVisibility(); |
| } |
| |
| private void toggleCredentialsVisibility() { |
| myPasswordLabel.setVisible(myRepository.isUseHttpAuthentication()); |
| myPasswordText.setVisible(myRepository.isUseHttpAuthentication()); |
| |
| myUsernameLabel.setVisible(myRepository.isUseHttpAuthentication()); |
| myUserNameText.setVisible(myRepository.isUseHttpAuthentication()); |
| |
| myAPIKeyLabel.setVisible(!myRepository.isUseHttpAuthentication()); |
| myAPIKey.setVisible(!myRepository.isUseHttpAuthentication()); |
| } |
| |
| @Nullable |
| @Override |
| protected JComponent createCustomPanel() { |
| myProjectLabel = new JBLabel("Project:", SwingConstants.RIGHT); |
| myProjectCombo = new ComboBox(300); |
| //myProjectCombo.setRenderer(new TaskUiUtil.SimpleComboBoxRenderer("Set URL and password/token first")); |
| myProjectCombo.setRenderer(new ListCellRendererWrapper<RedmineProjectItem>() { |
| @Override |
| public void customize(JList list, RedmineProjectItem value, int index, boolean selected, boolean hasFocus) { |
| if (value == null) { |
| setText("Set URL and password/token first"); |
| } |
| else { |
| if (myProjectCombo.isPopupVisible()) { |
| //if (value.myLevel == 0 && value.myProject != RedmineRepository.UNSPECIFIED_PROJECT) { |
| //setFont(UIUtil.getListFont().deriveFont(Font.BOLD)); |
| //} |
| StringBuilder builder = new StringBuilder(); |
| if (value.myLevel != 0) { |
| for (int i = 0; i < value.myLevel; i++) { |
| builder.append(" "); |
| } |
| } |
| setText(builder.append(value.myProject.getIdentifier()).toString()); |
| } |
| else { |
| setText(value.myProject.getIdentifier()); |
| } |
| } |
| } |
| }); |
| |
| myAPIKeyLabel = new JBLabel("API Token:", SwingConstants.RIGHT); |
| myAPIKey = new JPasswordField(); |
| return FormBuilder.createFormBuilder() |
| .addLabeledComponent(myAPIKeyLabel, myAPIKey) |
| .addLabeledComponent(myProjectLabel, myProjectCombo) |
| .getPanel(); |
| } |
| |
| @Override |
| public void setAnchor(@Nullable final JComponent anchor) { |
| super.setAnchor(anchor); |
| myProjectLabel.setAnchor(anchor); |
| myAPIKeyLabel.setAnchor(anchor); |
| } |
| |
| private static class RedmineProjectItem { |
| public final RedmineProject myProject; |
| public final int myLevel; |
| |
| public RedmineProjectItem(@NotNull RedmineProject project, int level) { |
| myProject = project; |
| myLevel = level; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null) return false; |
| |
| if (o instanceof RedmineProject) { |
| return myProject.equals(o); |
| } |
| else if (o instanceof RedmineProjectItem) { |
| return myProject.equals(((RedmineProjectItem)o).myProject); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| return myProject.hashCode(); |
| } |
| } |
| |
| private class FetchProjectsTask extends TaskUiUtil.ComboBoxUpdater<RedmineProjectItem> { |
| private FetchProjectsTask() { |
| super(RedmineRepositoryEditor.this.myProject, "Downloading Redmine projects...", myProjectCombo); |
| } |
| |
| @Override |
| public RedmineProjectItem getExtraItem() { |
| return new RedmineProjectItem(RedmineRepository.UNSPECIFIED_PROJECT, 0); |
| } |
| |
| @Nullable |
| @Override |
| public RedmineProjectItem getSelectedItem() { |
| RedmineProject currentProject = myRepository.getCurrentProject(); |
| return currentProject != null ? new RedmineProjectItem(currentProject, -1) : null; |
| } |
| |
| @NotNull |
| @Override |
| protected List<RedmineProjectItem> fetch(@NotNull ProgressIndicator indicator) throws Exception { |
| // Seems that Redmine always return its project hierarchy in DFS order. |
| // So it's easy to find level of each project using stack of parents. |
| Stack<RedmineProject> parents = new Stack<RedmineProject>(); |
| List<RedmineProjectItem> items = new ArrayList<RedmineProjectItem>(); |
| for (RedmineProject project : myRepository.fetchProjects()) { |
| RedmineProject parentProject = project.getParent(); |
| if (parentProject == null) { |
| items.add(new RedmineProjectItem(project, 0)); |
| parents.clear(); |
| } |
| else { |
| while (!parents.isEmpty() && !parents.peek().equals(parentProject)) { |
| parents.pop(); |
| } |
| items.add(new RedmineProjectItem(project, parents.size())); |
| } |
| parents.push(project); |
| } |
| return items; |
| } |
| } |
| } |