simplify GTDUCollector a bit more
[idea/community.git] / java / idea-ui / src / com / intellij / ide / util / importProject / DetectedRootsChooser.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.ide.util.importProject;
3
4 import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
5 import com.intellij.openapi.ui.ComboBox;
6 import com.intellij.openapi.ui.ComboBoxTableRenderer;
7 import com.intellij.ui.CollectionComboBoxModel;
8 import com.intellij.ui.ScrollPaneFactory;
9 import com.intellij.ui.SimpleListCellRenderer;
10 import com.intellij.ui.table.TableView;
11 import com.intellij.util.EventDispatcher;
12 import com.intellij.util.ui.ColumnInfo;
13 import com.intellij.util.ui.ListTableModel;
14 import org.jetbrains.annotations.NotNull;
15
16 import javax.swing.*;
17 import javax.swing.table.DefaultTableCellRenderer;
18 import javax.swing.table.TableCellEditor;
19 import javax.swing.table.TableCellRenderer;
20 import javax.swing.table.TableColumn;
21 import java.awt.*;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.KeyEvent;
25 import java.util.List;
26 import java.util.*;
27
28 public class DetectedRootsChooser {
29   private static final int CHECKBOX_COLUMN_WIDTH = new JCheckBox().getPreferredSize().width + 4;
30   private final ColumnInfo<DetectedRootData,Boolean> myIncludedColumn = new ColumnInfo<DetectedRootData, Boolean>("") {
31
32     @Override
33     public Class<Boolean> getColumnClass() {
34       return Boolean.class;
35     }
36
37     @Override
38     public Boolean valueOf(DetectedRootData detectedRootData) {
39       return detectedRootData.isIncluded();
40     }
41
42     @Override
43     public boolean isCellEditable(DetectedRootData detectedRootData) {
44       return true;
45     }
46
47     @Override
48     public int getWidth(JTable table) {
49       return CHECKBOX_COLUMN_WIDTH;
50     }
51
52     @Override
53     public void setValue(DetectedRootData detectedRootData, Boolean value) {
54       if (value.booleanValue() != detectedRootData.isIncluded()) {
55         detectedRootData.setIncluded(value);
56         myDispatcher.getMulticaster().selectionChanged();
57       }
58     }
59   };
60   private static final ColumnInfo<DetectedRootData, String> ROOT_COLUMN = new ColumnInfo<DetectedRootData, String>("") {
61     @Override
62     public String valueOf(DetectedRootData detectedRootData) {
63       return detectedRootData.getDirectory().getAbsolutePath();
64     }
65   };
66   private static final ColumnInfo<DetectedRootData, DetectedProjectRoot> ROOT_TYPE_COLUMN = new ColumnInfo<DetectedRootData, DetectedProjectRoot>("") {
67     @Override
68     public DetectedProjectRoot valueOf(DetectedRootData detectedRootData) {
69       return detectedRootData.getSelectedRoot();
70     }
71
72     @Override
73     public TableCellRenderer getRenderer(DetectedRootData detectedRootData) {
74       if (isCellEditable(detectedRootData)) {
75         return new ComboBoxTableRenderer<DetectedProjectRoot>(detectedRootData.getAllRoots()) {
76           @Override
77           protected String getTextFor(@NotNull DetectedProjectRoot value) {
78             return value.getRootTypeName();
79           }
80         };
81       }
82       return new DefaultTableCellRenderer() {
83         @Override
84         public Component getTableCellRendererComponent(JTable table,
85                                                        Object value,
86                                                        boolean isSelected,
87                                                        boolean hasFocus,
88                                                        int row,
89                                                        int column) {
90           final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
91           if (value instanceof DetectedProjectRoot) {
92             setText(((DetectedProjectRoot)value).getRootTypeName());
93           }
94           return component;
95         }
96       };
97     }
98
99     @Override
100     public TableCellEditor getEditor(DetectedRootData o) {
101       ComboBox<DetectedProjectRoot> comboBox = new ComboBox<>(new CollectionComboBoxModel<>(Arrays.asList(o.getAllRoots()), o.getSelectedRoot()));
102       comboBox.setRenderer(SimpleListCellRenderer.create("", DetectedProjectRoot::getRootTypeName));
103       return new DefaultCellEditor(comboBox);
104     }
105
106     @Override
107     public boolean isCellEditable(DetectedRootData detectedRootData) {
108       return detectedRootData.getAllRoots().length > 1;
109     }
110
111     @Override
112     public void setValue(DetectedRootData detectedRootData, DetectedProjectRoot value) {
113       detectedRootData.setSelectedRoot(value);
114     }
115   };
116   private final TableView<DetectedRootData> myTable;
117   private final JComponent myComponent;
118   private final ListTableModel<DetectedRootData> myModel;
119   private final EventDispatcher<RootSelectionListener> myDispatcher = EventDispatcher.create(RootSelectionListener.class);
120
121   public DetectedRootsChooser() {
122     myModel = new ListTableModel<>();
123     myTable = new TableView<>(myModel);
124     myTable.setTableHeader(null);
125     myTable.setShowGrid(false);
126     myComponent = ScrollPaneFactory.createScrollPane(myTable);
127     myTable.registerKeyboardAction(
128       new ActionListener() {
129         @Override
130         public void actionPerformed(ActionEvent e) {
131           invertSelectedRows();
132         }
133       },
134       KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
135       JComponent.WHEN_FOCUSED
136     );
137   }
138
139   private void invertSelectedRows() {
140     final int[] selectedRows = myTable.getSelectedRows();
141     if (selectedRows.length == 0) return;
142
143     boolean included = false;
144     for (int selectedRow : selectedRows) {
145       included |= myModel.getItems().get(selectedRow).isIncluded();
146     }
147     int first = Integer.MAX_VALUE;
148     int last = -1;
149     for (int selectedRow : selectedRows) {
150       first = Math.min(first, selectedRow);
151       last = Math.max(last, selectedRow);
152       myModel.getItems().get(selectedRow).setIncluded(!included);
153     }
154     myModel.fireTableRowsUpdated(first, last+1);
155     myDispatcher.getMulticaster().selectionChanged();
156   }
157
158   public JComponent getComponent() {
159     return myComponent;
160   }
161
162   public void addSelectionListener(RootSelectionListener listener) {
163     myDispatcher.addListener(listener);
164   }
165
166   public void setAllElementsMarked(boolean mark) {
167     for (DetectedRootData data : myModel.getItems()) {
168       data.setIncluded(mark);
169     }
170     myModel.fireTableRowsUpdated(0, myModel.getRowCount()-1);
171     myDispatcher.getMulticaster().selectionChanged();
172   }
173
174   public List<DetectedRootData> getMarkedElements() {
175     final List<DetectedRootData> result = new ArrayList<>();
176     for (DetectedRootData data : myModel.getItems()) {
177       if (data.isIncluded()) {
178         result.add(data);
179       }
180     }
181     return result;
182   }
183
184   public void setElements(List<? extends DetectedRootData> roots) {
185     Set<String> rootTypes = new HashSet<>();
186     for (DetectedRootData root : roots) {
187       for (DetectedProjectRoot projectRoot : root.getAllRoots()) {
188         rootTypes.add(projectRoot.getRootTypeName());
189       }
190     }
191     myModel.setColumnInfos(new ColumnInfo[]{myIncludedColumn, ROOT_COLUMN, ROOT_TYPE_COLUMN});
192     int max = 0;
193     for (String rootType : rootTypes) {
194       max = Math.max(max, myTable.getFontMetrics(myTable.getFont()).stringWidth(rootType));
195     }
196     final TableColumn column = myTable.getColumnModel().getColumn(2);
197     int width = max + 20;//add space for combobox button
198     column.setPreferredWidth(width);
199     column.setMaxWidth(width);
200     myTable.updateColumnSizes();
201     List<DetectedRootData> sortedRoots = new ArrayList<>(roots);
202     sortedRoots.sort(Comparator.comparing(DetectedRootData::getDirectory));
203     myModel.setItems(sortedRoots);
204   }
205
206   interface RootSelectionListener extends EventListener {
207     void selectionChanged();
208   }
209 }