| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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.android.tools.idea.gradle.variant.view; |
| |
| import com.android.builder.model.AndroidProject; |
| import com.android.sdklib.repository.FullRevision; |
| import com.android.sdklib.repository.FullRevision.PreviewComparison; |
| import com.android.sdklib.repository.PreciseRevision; |
| import com.android.tools.idea.gradle.GradleSyncState; |
| import com.android.tools.idea.gradle.IdeaAndroidProject; |
| import com.android.tools.idea.gradle.util.GradleUtil; |
| import com.android.tools.idea.gradle.util.ModuleTypeComparator; |
| import com.android.tools.idea.gradle.variant.conflict.Conflict; |
| import com.android.tools.idea.gradle.variant.conflict.ConflictSet; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.Lists; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.application.Application; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.components.ServiceManager; |
| import com.intellij.openapi.editor.colors.EditorColors; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.ComboBox; |
| import com.intellij.openapi.ui.MessageType; |
| import com.intellij.openapi.wm.ToolWindow; |
| import com.intellij.ui.IdeBorderFactory; |
| import com.intellij.ui.TableSpeedSearch; |
| import com.intellij.ui.TableUtil; |
| import com.intellij.ui.content.Content; |
| import com.intellij.ui.content.ContentFactory; |
| import com.intellij.ui.table.JBTable; |
| import com.intellij.util.ui.AbstractTableCellEditor; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties; |
| |
| import javax.swing.*; |
| import javax.swing.border.Border; |
| import javax.swing.table.DefaultTableCellRenderer; |
| import javax.swing.table.DefaultTableModel; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.text.Collator; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import static com.android.tools.idea.gradle.util.Projects.getAndroidModel; |
| import static com.android.tools.idea.gradle.variant.conflict.ConflictResolution.solveSelectionConflict; |
| |
| /** |
| * The contents of the "Build Variants" tool window. |
| */ |
| public class BuildVariantView { |
| private static final Object[] TABLE_COLUMN_NAMES = new Object[]{"Module", "Build Variant"}; |
| |
| private static final int MODULE_COLUMN_INDEX = 0; |
| private static final int VARIANT_COLUMN_INDEX = 1; |
| |
| private static final Color CONFLICT_CELL_BACKGROUND = MessageType.ERROR.getPopupBackground(); |
| |
| private final Project myProject; |
| private BuildVariantUpdater myUpdater; |
| |
| private JPanel myToolWindowPanel; |
| private JBTable myVariantsTable; |
| private JPanel myNotificationPanel; |
| private ComboBox myTestArtifactComboBox; |
| private JPanel myTestArtifactPanel; |
| |
| private final List<BuildVariantSelectionChangeListener> myBuildVariantSelectionChangeListeners = Lists.newArrayList(); |
| private final List<Conflict> myConflicts = Lists.newArrayList(); |
| |
| @NotNull |
| public static BuildVariantView getInstance(@NotNull Project project) { |
| return ServiceManager.getService(project, BuildVariantView.class); |
| } |
| |
| public BuildVariantView(@NotNull Project project) { |
| myProject = project; |
| myUpdater = new BuildVariantUpdater(); |
| |
| myTestArtifactComboBox.addItem(new NamedArtifactType(AndroidProject.ARTIFACT_ANDROID_TEST, "Android Instrumentation Tests")); |
| myTestArtifactComboBox.addItem(new NamedArtifactType(AndroidProject.ARTIFACT_UNIT_TEST, "Unit Tests")); |
| |
| myTestArtifactComboBox.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| NamedArtifactType namedArtifactType = (NamedArtifactType)myTestArtifactComboBox.getSelectedItem(); |
| if (namedArtifactType != null) { |
| updateModulesWithTestArtifact(namedArtifactType.artifactType); |
| } |
| } |
| }); |
| |
| myTestArtifactPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0)); |
| ((JComponent)myVariantsTable.getParent().getParent()).setBorder(IdeBorderFactory.createEmptyBorder()); |
| |
| // This makes the combo-box resize even if the even if it cannot show all its text |
| myTestArtifactComboBox.setPrototypeDisplayValue("XXXX"); |
| } |
| |
| public void updateTestArtifactComboBox() { |
| List<Module> modules = getModulesIfProjectSupportsUnitTests(); |
| |
| boolean hasModules = !modules.isEmpty(); |
| myTestArtifactComboBox.setEnabled(hasModules); |
| |
| String tooltip = hasModules ? "" : "Unit test support requires Android Gradle plugin version 1.1.0 (or newer)"; |
| myTestArtifactComboBox.setToolTipText(tooltip); |
| |
| if (hasModules) { |
| IdeaAndroidProject androidModel = getAndroidModel(modules.get(0)); |
| assert androidModel != null; // getGradleModules() returns only android modules and at this stage we have the IdeaAndroidProject. |
| String selectedTestArtifactName = androidModel.getSelectedTestArtifactName(); |
| for (int i = 0; i < myTestArtifactComboBox.getItemCount(); i++) { |
| NamedArtifactType namedArtifactType = (NamedArtifactType)myTestArtifactComboBox.getModel().getElementAt(i); |
| if (namedArtifactType.artifactType.equals(selectedTestArtifactName)) { |
| myTestArtifactComboBox.setSelectedIndex(i); |
| break; |
| } |
| } |
| |
| // Make sure all modules use the same test artifact. |
| updateModulesWithTestArtifact(selectedTestArtifactName); |
| } |
| } |
| |
| @NotNull |
| private List<Module> getModulesIfProjectSupportsUnitTests() { |
| FullRevision minimumSupportedVersion = new PreciseRevision(1, 1, 0); |
| |
| List<Module> modules = Lists.newArrayList(); |
| for (Module module : ModuleManager.getInstance(myProject).getModules()) { |
| AndroidFacet androidFacet = AndroidFacet.getInstance(module); |
| if (androidFacet != null && androidFacet.requiresAndroidModel()) { |
| IdeaAndroidProject androidModel = androidFacet.getAndroidModel(); |
| if (androidModel != null) { |
| if (!supportsUnitTests(androidModel.getAndroidProject(), minimumSupportedVersion)) { |
| return Collections.emptyList(); |
| } |
| modules.add(module); |
| } |
| } |
| } |
| return modules; |
| } |
| |
| @VisibleForTesting |
| static boolean supportsUnitTests(@NotNull AndroidProject androidProject, @NotNull FullRevision minimumSupportedVersion) { |
| try { |
| FullRevision modelVersion = PreciseRevision.parseRevision(androidProject.getModelVersion()); |
| return minimumSupportedVersion.compareTo(modelVersion, PreviewComparison.IGNORE) <= 0; |
| } |
| catch (NumberFormatException e) { |
| // failed to parse, assume unit tests are not supported. |
| } |
| return false; |
| } |
| |
| private void updateModulesWithTestArtifact(@NotNull String artifactType) { |
| if (!myUpdater.updateTestArtifactsNames(myProject, getGradleModulesWithAndroidProjects(), artifactType).isEmpty()) { |
| invokeListeners(); |
| } |
| } |
| |
| @VisibleForTesting |
| void setUpdater(@NotNull BuildVariantUpdater updater) { |
| myUpdater = updater; |
| } |
| |
| public void addListener(@NotNull BuildVariantSelectionChangeListener listener) { |
| //noinspection SynchronizeOnThis |
| synchronized (this) { |
| myBuildVariantSelectionChangeListeners.add(listener); |
| } |
| } |
| |
| public void removeListener(@NotNull BuildVariantSelectionChangeListener listener) { |
| //noinspection SynchronizeOnThis |
| synchronized (this) { |
| myBuildVariantSelectionChangeListeners.remove(listener); |
| } |
| } |
| |
| private void createUIComponents() { |
| myVariantsTable = new BuildVariantTable(); |
| new TableSpeedSearch(myVariantsTable); |
| myNotificationPanel = new NotificationPanel(); |
| myNotificationPanel.setVisible(false); |
| } |
| |
| public void projectImportStarted() { |
| getVariantsTable().setLoading(true); |
| myTestArtifactComboBox.setEnabled(false); |
| } |
| |
| /** |
| * Creates the contents of the "Build Variants" tool window. |
| * |
| * @param toolWindow the tool window whose contents will be created. |
| */ |
| public void createToolWindowContent(@NotNull ToolWindow toolWindow) { |
| ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); |
| Content content = contentFactory.createContent(myToolWindowPanel, "", false); |
| toolWindow.getContentManager().addContent(content); |
| updateContents(); |
| } |
| |
| public void updateContents() { |
| if (GradleSyncState.getInstance(myProject).isSyncInProgress()) { |
| projectImportStarted(); |
| return; |
| } |
| |
| final List<Object[]> rows = Lists.newArrayList(); |
| final List<BuildVariantItem[]> variantNamesPerRow = Lists.newArrayList(); |
| |
| for (Module module : getGradleModulesWithAndroidProjects()) { |
| AndroidFacet androidFacet = AndroidFacet.getInstance(module); |
| assert androidFacet != null; // getGradleModules() returns only relevant modules. |
| |
| JpsAndroidModuleProperties facetProperties = androidFacet.getProperties(); |
| String variantName = facetProperties.SELECTED_BUILD_VARIANT; |
| |
| BuildVariantItem[] variantNames = getVariantNames(module); |
| if (variantNames != null) { |
| // If we got here IdeaAndroidProject is *not* null. |
| IdeaAndroidProject androidModel = getAndroidModel(module); |
| assert androidModel != null; |
| variantName = androidModel.getSelectedVariant().getName(); |
| variantNamesPerRow.add(variantNames); |
| } |
| |
| if (variantName != null) { |
| Object[] row = {module, variantName}; |
| rows.add(row); |
| } |
| } |
| Runnable setModelTask = new Runnable() { |
| @Override |
| public void run() { |
| getVariantsTable().setModel(rows, variantNamesPerRow); |
| updateTestArtifactComboBox(); |
| } |
| }; |
| Application application = ApplicationManager.getApplication(); |
| if (application.isDispatchThread()) { |
| setModelTask.run(); |
| } |
| else { |
| application.invokeLater(setModelTask); |
| } |
| } |
| |
| @NotNull |
| private List<Module> getGradleModulesWithAndroidProjects() { |
| List<Module> gradleModules = Lists.newArrayList(); |
| for (Module module : ModuleManager.getInstance(myProject).getModules()) { |
| AndroidFacet androidFacet = AndroidFacet.getInstance(module); |
| if (androidFacet != null && androidFacet.requiresAndroidModel() && androidFacet.getAndroidModel() != null) { |
| gradleModules.add(module); |
| } |
| } |
| |
| if (!gradleModules.isEmpty()) { |
| Collections.sort(gradleModules, ModuleTypeComparator.INSTANCE); |
| return gradleModules; |
| } |
| return Collections.emptyList(); |
| } |
| |
| @NotNull |
| private BuildVariantTable getVariantsTable() { |
| return (BuildVariantTable)myVariantsTable; |
| } |
| |
| @Nullable |
| private static BuildVariantItem[] getVariantNames(@NotNull Module module) { |
| IdeaAndroidProject androidModel = getAndroidModel(module); |
| if (androidModel == null) { |
| return null; |
| } |
| Collection<String> variantNames = androidModel.getVariantNames(); |
| BuildVariantItem[] items = new BuildVariantItem[variantNames.size()]; |
| int i = 0; |
| for (String name : variantNames) { |
| items[i++] = new BuildVariantItem(module.getName(), name); |
| } |
| Arrays.sort(items); |
| return items; |
| } |
| |
| public void updateContents(@NotNull List<Conflict> conflicts) { |
| myNotificationPanel.setVisible(!conflicts.isEmpty()); |
| ((NotificationPanel)myNotificationPanel).myCurrentConflictIndex = -1; |
| myConflicts.clear(); |
| myConflicts.addAll(conflicts); |
| updateContents(); |
| } |
| |
| public void findAndSelect(@NotNull Module module) { |
| findAndSelect(module, MODULE_COLUMN_INDEX); |
| } |
| |
| public void findAndSelectVariantEditor(@NotNull Module module) { |
| findAndSelect(module, VARIANT_COLUMN_INDEX); |
| } |
| |
| private void findAndSelect(@NotNull Module module, int columnIndex) { |
| int rowCount = myVariantsTable.getRowCount(); |
| for (int row = 0; row < rowCount; row++) { |
| if (module.equals(myVariantsTable.getValueAt(row, MODULE_COLUMN_INDEX))) { |
| myVariantsTable.getSelectionModel().setSelectionInterval(row, row); |
| myVariantsTable.getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex); |
| TableUtil.scrollSelectionToVisible(myVariantsTable); |
| myVariantsTable.requestFocusInWindow(); |
| break; |
| } |
| } |
| } |
| |
| public interface BuildVariantSelectionChangeListener { |
| /** |
| * Indicates that a user selected a build variant from the "Build Variants" tool window. |
| * <p/> |
| * This notification occurs: |
| * <ul> |
| * <li>after the user selected a build variant from the drop-down</li> |
| * <li>project structure has been updated according to selected build variant</li> |
| * <li>the selected test artifact is changed</li> |
| * </ul> |
| * <p/> |
| * This listener will not be invoked if the project structure update fails. |
| * |
| */ |
| void buildVariantsConfigChanged(); |
| } |
| |
| private class NotificationPanel extends JPanel { |
| int myCurrentConflictIndex = -1; |
| |
| NotificationPanel() { |
| super(new BorderLayout()); |
| Color color = EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.NOTIFICATION_BACKGROUND); |
| setBackground(color == null ? UIUtil.getToolTipBackground() : color); |
| setBorder(BorderFactory.createEmptyBorder(1, 15, 1, 15)); // Same as EditorNotificationPanel |
| setPreferredSize(new Dimension(-1, 24)); |
| |
| JLabel textLabel = new JLabel("Variant selection conflicts found."); |
| textLabel.setOpaque(false); |
| add(textLabel, BorderLayout.CENTER); |
| |
| DefaultActionGroup group = new DefaultActionGroup(); |
| ActionManager actionManager = ActionManager.getInstance(); |
| |
| AnAction nextConflictAction = new AnAction() { |
| @Override |
| public void actionPerformed(@NotNull AnActionEvent e) { |
| navigateConflicts(true); |
| } |
| }; |
| nextConflictAction.copyFrom(actionManager.getAction(IdeActions.ACTION_NEXT_OCCURENCE)); |
| group.add(nextConflictAction); |
| |
| AnAction prevConflictAction = new AnAction() { |
| @Override |
| public void actionPerformed(@NotNull AnActionEvent e) { |
| navigateConflicts(false); |
| } |
| }; |
| prevConflictAction.copyFrom(actionManager.getAction(IdeActions.ACTION_PREVIOUS_OCCURENCE)); |
| group.add(prevConflictAction); |
| |
| ActionToolbar toolbar = actionManager.createActionToolbar("", group, true); |
| toolbar.setReservePlaceAutoPopupIcon(false); |
| toolbar.setMinimumButtonSize(new Dimension(23, 23)); // a little smaller than default (25 x 25) |
| |
| JComponent toolbarComponent = toolbar.getComponent(); |
| toolbarComponent.setBorder(null); |
| toolbarComponent.setOpaque(false); |
| add(toolbarComponent, BorderLayout.EAST); |
| } |
| |
| private void navigateConflicts(boolean forward) { |
| int conflictCount = myConflicts.size(); |
| if (conflictCount == 0) { |
| return; |
| } |
| if (forward) { |
| myCurrentConflictIndex++; |
| if (myCurrentConflictIndex >= conflictCount) { |
| myCurrentConflictIndex = 0; |
| } |
| } |
| else { |
| myCurrentConflictIndex--; |
| if (myCurrentConflictIndex < 0) { |
| myCurrentConflictIndex = conflictCount - 1; |
| } |
| } |
| Conflict conflict = myConflicts.get(myCurrentConflictIndex); |
| findAndSelect(conflict.getSource()); |
| } |
| } |
| |
| private static class BuildVariantItem implements Comparable<BuildVariantItem> { |
| @NotNull final String myModuleName; |
| @NotNull final String myBuildVariantName; |
| |
| BuildVariantItem(@NotNull String moduleName, @NotNull String buildVariantName) { |
| myModuleName = moduleName; |
| myBuildVariantName = buildVariantName; |
| } |
| |
| @Override |
| public int compareTo(@Nullable BuildVariantItem o) { |
| return o != null ? Collator.getInstance().compare(myBuildVariantName, o.myBuildVariantName) : 1; |
| } |
| |
| boolean hasBuildVariantName(@Nullable Object name) { |
| return myBuildVariantName.equals(name); |
| } |
| |
| @Override |
| public String toString() { |
| return myBuildVariantName; |
| } |
| } |
| |
| private class BuildVariantTable extends JBTable { |
| private boolean myLoading; |
| private final List<TableCellEditor> myCellEditors = Lists.newArrayList(); |
| |
| private final ModuleTableCell myModuleCellRenderer = new ModuleTableCell(); |
| private final ModuleTableCell myModuleCellEditor = new ModuleTableCell(); |
| private final VariantsCellRenderer myVariantsCellRenderer = new VariantsCellRenderer(); |
| |
| BuildVariantTable() { |
| super(new BuildVariantTableModel(Collections.<Object[]>emptyList())); |
| addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| int column = getSelectedColumn(); |
| int row = getSelectedRow(); |
| if (column == VARIANT_COLUMN_INDEX && row >= 0 && e.getKeyCode() == KeyEvent.VK_F2 && editCellAt(row, column)) { |
| Component editorComponent = getEditorComponent(); |
| if (editorComponent instanceof ComboBox) { |
| editorComponent.requestFocusInWindow(); |
| ((ComboBox)editorComponent).showPopup(); |
| } |
| } |
| } |
| }); |
| } |
| |
| @Nullable |
| Conflict findConflict(int row) { |
| for (Conflict conflict : myConflicts) { |
| Object module = getValueAt(row, MODULE_COLUMN_INDEX); |
| if (conflict.getSource().equals(module)) { |
| return conflict; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean isCellEditable(int row, int column) { |
| return true; |
| } |
| |
| void setLoading(boolean loading) { |
| myLoading = loading; |
| setPaintBusy(myLoading); |
| clearContents(); |
| String text = myLoading ? "Loading..." : "Nothing to Show"; |
| getEmptyText().setText(text); |
| } |
| |
| private void clearContents() { |
| setModel(new BuildVariantTableModel(Collections.<Object[]>emptyList())); |
| myCellEditors.clear(); |
| } |
| |
| void setModel(@NotNull List<Object[]> rows, @NotNull List<BuildVariantItem[]> variantNamesPerRow) { |
| setLoading(false); |
| if (rows.isEmpty()) { |
| // This is most likely an old-style (pre-Gradle) Android project. Just leave the table empty. |
| setModel(new BuildVariantTableModel(rows)); |
| return; |
| } |
| |
| boolean hasVariants = !variantNamesPerRow.isEmpty(); |
| List<Object[]> content = hasVariants ? rows : Collections.<Object[]>emptyList(); |
| |
| setModel(new BuildVariantTableModel(content)); |
| addBuildVariants(variantNamesPerRow); |
| } |
| |
| private void addBuildVariants(@NotNull List<BuildVariantItem[]> variantNamesPerRow) { |
| for (int row = 0; row < variantNamesPerRow.size(); row++) { |
| BuildVariantItem[] items = variantNamesPerRow.get(row); |
| BuildVariantItem selected = null; |
| for (BuildVariantItem item : items) { |
| if (item.hasBuildVariantName(getValueAt(row, VARIANT_COLUMN_INDEX))) { |
| selected = item; |
| break; |
| } |
| } |
| |
| ComboBox editor = new ComboBox(items); |
| if (selected != null) { |
| editor.setSelectedItem(selected); |
| } |
| |
| editor.setBorder(BorderFactory.createEmptyBorder()); |
| editor.addItemListener(new ItemListener() { |
| @Override |
| public void itemStateChanged(ItemEvent e) { |
| if (e.getStateChange() == ItemEvent.SELECTED) { |
| BuildVariantItem selected = (BuildVariantItem)e.getItem(); |
| buildVariantSelected(selected.myModuleName, selected.myBuildVariantName); |
| } |
| } |
| }); |
| final DefaultCellEditor defaultCellEditor = new DefaultCellEditor(editor); |
| |
| editor.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { |
| defaultCellEditor.cancelCellEditing(); |
| } |
| } |
| }); |
| |
| myCellEditors.add(defaultCellEditor); |
| } |
| } |
| |
| @Override |
| public TableCellRenderer getCellRenderer(int row, int column) { |
| return column == MODULE_COLUMN_INDEX ? myModuleCellRenderer : myVariantsCellRenderer; |
| } |
| |
| @Override |
| public TableCellEditor getCellEditor(int row, int column) { |
| if (column == VARIANT_COLUMN_INDEX && row >= 0 && row < myCellEditors.size()) { |
| return myCellEditors.get(row); |
| } |
| return myModuleCellEditor; |
| } |
| } |
| |
| @VisibleForTesting |
| void buildVariantSelected(@NotNull String moduleName, @NotNull String variantName) { |
| final List<AndroidFacet> facets = myUpdater.updateSelectedVariant(myProject, moduleName, variantName); |
| if (facets.isEmpty()) { |
| return; |
| } |
| invokeListeners(); |
| } |
| |
| private void invokeListeners() { |
| Runnable invokeListenersTask = new Runnable() { |
| @Override |
| public void run() { |
| updateContents(); |
| for (BuildVariantSelectionChangeListener listener : myBuildVariantSelectionChangeListeners) { |
| listener.buildVariantsConfigChanged(); |
| } |
| } |
| }; |
| |
| Application application = ApplicationManager.getApplication(); |
| if (application.isUnitTestMode()) { |
| invokeListenersTask.run(); |
| } |
| else { |
| application.invokeLater(invokeListenersTask); |
| } |
| } |
| |
| private static class BuildVariantTableModel extends DefaultTableModel { |
| BuildVariantTableModel(List<Object[]> rows) { |
| super(rows.toArray(new Object[rows.size()][TABLE_COLUMN_NAMES.length]), TABLE_COLUMN_NAMES); |
| } |
| } |
| |
| private static class VariantsCellRenderer extends DefaultTableCellRenderer { |
| @Override |
| public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); |
| |
| if (c instanceof JLabel) { |
| JLabel component = (JLabel)c; |
| |
| Color background = isSelected ? table.getSelectionBackground() : table.getBackground(); |
| Conflict conflictFound = ((BuildVariantTable)table).findConflict(row); |
| if (conflictFound != null) { |
| background = CONFLICT_CELL_BACKGROUND; |
| } |
| component.setBackground(background); |
| |
| String toolTip = conflictFound != null ? conflictFound.toString() : null; |
| component.setToolTipText(toolTip); |
| |
| // add some padding to table cells. It is hard to read text of combo box. |
| component.setBorder(BorderFactory.createCompoundBorder(component.getBorder(), BorderFactory.createEmptyBorder(3, 2, 4, 2))); |
| } |
| |
| return c; |
| } |
| } |
| |
| private static class ModuleTableCell extends AbstractTableCellEditor implements TableCellRenderer { |
| private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); |
| |
| @Nullable private Conflict myConflict; |
| |
| private JPanel myPanel; |
| private JLabel myModuleNameLabel; |
| private JPanel myButtonsPanel; |
| private JButton myInfoButton; |
| private JButton myFixButton; |
| |
| private Object myValue; |
| |
| ModuleTableCell() { |
| myModuleNameLabel = new JLabel(); |
| myModuleNameLabel.setOpaque(false); |
| |
| myInfoButton = createButton(AllIcons.General.BalloonInformation); |
| myInfoButton.setToolTipText("More info"); |
| myInfoButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (myValue instanceof Module) { |
| Module module = (Module)myValue; |
| IdeaAndroidProject androidModel = getAndroidModel(module); |
| assert androidModel != null; |
| ModuleVariantsInfoDialog dialog = new ModuleVariantsInfoDialog(module, androidModel); |
| dialog.show(); |
| } |
| } |
| }); |
| |
| myFixButton = createButton(AllIcons.Actions.QuickfixBulb); |
| myFixButton.setToolTipText("Fix problem"); |
| myFixButton.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (myConflict != null) { |
| Project project = myConflict.getSource().getProject(); |
| boolean solved = solveSelectionConflict(myConflict); |
| if (solved) { |
| ConflictSet conflicts = ConflictSet.findConflicts(project); |
| conflicts.showSelectionConflicts(); |
| } |
| } |
| stopCellEditing(); |
| } |
| }); |
| |
| myButtonsPanel = new JPanel(); |
| myButtonsPanel.setOpaque(false); |
| myButtonsPanel.add(myInfoButton); |
| myButtonsPanel.add(myFixButton); |
| |
| myPanel = new JPanel(new BorderLayout()) { |
| @Override |
| public String getToolTipText(MouseEvent e) { |
| String toolTip = getToolTipTextIfUnderX(myModuleNameLabel, e.getX()); |
| if (toolTip != null) { |
| return toolTip; |
| } |
| int x = e.getX() - myButtonsPanel.getX(); |
| toolTip = getToolTipTextIfUnderX(myInfoButton, x); |
| if (toolTip != null) { |
| return toolTip; |
| } |
| toolTip = getToolTipTextIfUnderX(myFixButton, x); |
| if (toolTip != null) { |
| return toolTip; |
| } |
| return super.getToolTipText(e); |
| } |
| }; |
| |
| myPanel.add(myModuleNameLabel, BorderLayout.CENTER); |
| myPanel.add(myButtonsPanel, BorderLayout.EAST); |
| } |
| |
| @NotNull |
| private static JButton createButton(@NotNull Icon icon) { |
| JButton button = new JButton(icon); |
| button.setBorder(null); |
| button.setBorderPainted(false); |
| button.setContentAreaFilled(false); |
| return button; |
| } |
| |
| @Nullable |
| private static String getToolTipTextIfUnderX(@NotNull JComponent c, int x) { |
| if (c.isVisible() && x >= c.getX() && x <= c.getX() + c.getWidth()) { |
| return c.getToolTipText(); |
| } |
| return null; |
| } |
| |
| @Override |
| public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { |
| setUpComponent(table, value, true, true, row); |
| return myPanel; |
| } |
| |
| @Override |
| public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| setUpComponent(table, value, isSelected, hasFocus, row); |
| return myPanel; |
| } |
| |
| private void setUpComponent(@NotNull JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row) { |
| myValue = value; |
| |
| String moduleName = null; |
| Icon moduleIcon = null; |
| if (value instanceof Module) { |
| Module module = (Module)value; |
| if (!module.isDisposed()) { |
| moduleName = module.getName(); |
| moduleIcon = GradleUtil.getModuleIcon(module); |
| } |
| } |
| |
| myModuleNameLabel.setText(moduleName == null ? "" : moduleName); |
| myModuleNameLabel.setIcon(moduleIcon); |
| |
| myConflict = ((BuildVariantTable)table).findConflict(row); |
| |
| myModuleNameLabel.setToolTipText(myConflict != null ? myConflict.toString() : null); |
| myFixButton.setVisible(myConflict != null); |
| |
| Color background = isSelected ? table.getSelectionBackground() : table.getBackground(); |
| if (myConflict != null) { |
| background = CONFLICT_CELL_BACKGROUND; |
| } |
| myPanel.setBackground(background); |
| |
| Border border = hasFocus ? UIUtil.getTableFocusCellHighlightBorder() : EMPTY_BORDER; |
| myPanel.setBorder(border); |
| } |
| |
| @Override |
| public Object getCellEditorValue() { |
| return myValue; |
| } |
| } |
| |
| /** |
| * Mapping from the unique artifact name string to a human readable name. Used as a model for the combo box. |
| */ |
| private static class NamedArtifactType { |
| @NotNull final String artifactType; |
| @NotNull final String description; |
| |
| public NamedArtifactType(@NotNull String artifactName, @NotNull String description) { |
| this.artifactType = artifactName; |
| this.description = description; |
| } |
| |
| @Override |
| public String toString() { |
| return description; |
| } |
| } |
| } |