Merge remote-tracking branch 'origin/master'
[idea/community.git] / platform / lang-api / src / com / intellij / execution / util / ListTableWithButtons.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.execution.util;
17
18 import com.intellij.openapi.actionSystem.AnActionEvent;
19 import com.intellij.openapi.util.Condition;
20 import com.intellij.ui.*;
21 import com.intellij.ui.table.TableView;
22 import com.intellij.util.containers.ContainerUtil;
23 import com.intellij.util.ui.ColumnInfo;
24 import com.intellij.util.ui.JBUI;
25 import com.intellij.util.ui.ListTableModel;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import javax.swing.*;
30 import javax.swing.table.DefaultTableCellRenderer;
31 import javax.swing.table.TableCellRenderer;
32 import java.awt.event.KeyAdapter;
33 import java.awt.event.KeyEvent;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Observable;
38
39 /**
40  * @author traff
41  */
42 public abstract class ListTableWithButtons<T> extends Observable {
43   private final List<T> myElements = ContainerUtil.newArrayList();
44   private final JPanel myPanel;
45   private final TableView<T> myTableView;
46   private final CommonActionsPanel myActionsPanel;
47   private boolean myIsEnabled = true;
48
49   protected ListTableWithButtons() {
50     myTableView = new TableView(createListModel()) {
51       @Override
52       protected void createDefaultEditors() {
53         super.createDefaultEditors();
54         Object editor = defaultEditorsByColumnClass.get(String.class);
55         if (editor instanceof DefaultCellEditor) {
56           ((DefaultCellEditor)editor).getComponent().addKeyListener(new KeyAdapter() {
57             @Override
58             public void keyPressed(KeyEvent e) {
59               final int column = myTableView.getEditingColumn();
60               final int row = myTableView.getEditingRow();
61               if (e.getModifiers() == 0 && (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB)) {
62                 e.consume();
63                 SwingUtilities.invokeLater(new Runnable() {
64                   @Override
65                   public void run() {
66                     stopEditing();
67                     int nextColumn = column < myTableView.getColumnCount() - 1 ? column + 1 : 0;
68                     int nextRow = nextColumn == 0 ? row + 1 : row;
69                     if (nextRow > myTableView.getRowCount() - 1) {
70                       if (myElements.isEmpty() || !ListTableWithButtons.this.isEmpty(myElements.get(myElements.size() - 1))) {
71                         ToolbarDecorator.findAddButton(myPanel).actionPerformed(null);
72                         return;
73                       }
74                       else {
75                         nextRow = 0;
76                       }
77                     }
78                     myTableView.scrollRectToVisible(myTableView.getCellRect(nextRow, nextColumn, true));
79                     myTableView.editCellAt(nextRow, nextColumn);
80                   }
81                 });
82               }
83             }
84           });
85         }
86       }
87     };
88     myTableView.setRowHeight(new JTextField().getPreferredSize().height);
89     myTableView.setIntercellSpacing(JBUI.emptySize());
90     myTableView.setStriped(true);
91     
92     myTableView.getTableViewModel().setSortable(false);
93     ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTableView);
94     myPanel = decorator
95       .setAddAction(new AnActionButtonRunnable() {
96         @Override
97         public void run(AnActionButton button) {
98           myTableView.stopEditing();
99           setModified();
100           SwingUtilities.invokeLater(new Runnable() {
101             @Override
102             public void run() {
103               if (myElements.isEmpty() || !isEmpty(myElements.get(myElements.size() - 1))) {
104                 myElements.add(createElement());
105                 myTableView.getTableViewModel().setItems(myElements);
106               }
107               myTableView.scrollRectToVisible(myTableView.getCellRect(myElements.size() - 1, 0, true));
108               myTableView.getComponent().editCellAt(myElements.size() - 1, 0);
109             }
110           });
111         }
112       }).setRemoveAction(new AnActionButtonRunnable() {
113         @Override
114         public void run(AnActionButton button) {
115           removeSelected();
116         }
117       }).disableUpDownActions().addExtraActions(createExtraActions()).createPanel();
118
119     ToolbarDecorator.findRemoveButton(myPanel).addCustomUpdater(new AnActionButtonUpdater() {
120       @Override
121       public boolean isEnabled(AnActionEvent e) {
122         List<T> selection = getSelection();
123         if (selection.isEmpty() || !myIsEnabled) return false;
124         for (T t : selection) {
125           if (!canDeleteElement(t)) return false;
126         }
127         return true;
128       }
129     });
130     ToolbarDecorator.findAddButton(myPanel).addCustomUpdater(new AnActionButtonUpdater() {
131       @Override
132       public boolean isEnabled(AnActionEvent e) {
133         return myIsEnabled;
134       }
135     });
136
137     myActionsPanel = decorator.getActionsPanel();
138
139     myTableView.getComponent().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
140   }
141
142   protected void removeSelected() {
143     List<T> selected = getSelection();
144     if (!selected.isEmpty()) {
145       myTableView.stopEditing();
146       setModified();
147       int selectedIndex = myTableView.getSelectionModel().getLeadSelectionIndex();
148       myTableView.scrollRectToVisible(myTableView.getCellRect(selectedIndex, 0, true));
149       selected = ContainerUtil.filter(selected, new Condition<T>() {
150         @Override
151         public boolean value(T t) {
152           return canDeleteElement(t);
153         }
154       });
155       myElements.removeAll(selected);
156       myTableView.getTableViewModel().setItems(myElements);
157
158       int prev = selectedIndex - 1;
159       if (prev >= 0) {
160         myTableView.getComponent().getSelectionModel().setSelectionInterval(prev, prev);
161       }
162       else if (selectedIndex < myElements.size()) {
163         myTableView.getComponent().getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
164       }
165     }
166   }
167
168   @NotNull
169   public TableView<T> getTableView() {
170     return myTableView;
171   }
172
173   protected abstract ListTableModel createListModel();
174
175   protected void setModified() {
176     setChanged();
177     notifyObservers();
178   }
179
180   protected List<T> getElements() {
181     return myElements;
182   }
183
184   public JComponent getComponent() {
185     return myPanel;
186   }
187
188   public CommonActionsPanel getActionsPanel() {
189     return myActionsPanel;
190   }
191
192   public void setEnabled() {
193     myTableView.getComponent().setEnabled(true);
194     myIsEnabled = true;
195   }
196
197   public void setDisabled() {
198     myTableView.getComponent().setEnabled(false);
199     myIsEnabled = false;
200   }
201
202   public void stopEditing() {
203     myTableView.stopEditing();
204   }
205
206   public void refreshValues() {
207     myTableView.getComponent().repaint();
208   }
209
210   protected void setSelection(T element) {
211     myTableView.setSelection(Collections.singleton(element));
212     TableUtil.scrollSelectionToVisible(myTableView);
213   }
214
215   protected void editSelection(int column) {
216     List<T> selection = getSelection();
217     if (selection.size() != 1) return;
218     int row = myElements.indexOf(selection.get(0));
219     if (row != -1) {
220       TableUtil.editCellAt(myTableView, row, column);
221     }
222   }
223
224   protected abstract T createElement();
225
226   protected abstract boolean isEmpty(T element);
227
228   @NotNull
229   protected AnActionButton[] createExtraActions() {
230     return new AnActionButton[0];
231   }
232
233
234   @NotNull
235   protected List<T> getSelection() {
236     int[] selection = myTableView.getComponent().getSelectedRows();
237     if (selection.length == 0) {
238       return Collections.emptyList();
239     }
240     else {
241       List<T> result = new ArrayList<T>(selection.length);
242       for (int row : selection) {
243         result.add(myElements.get(row));
244       }
245       return result;
246     }
247   }
248
249   public void setValues(List<T> envVariables) {
250     myElements.clear();
251     for (T envVariable : envVariables) {
252       myElements.add(cloneElement(envVariable));
253     }
254     myTableView.getTableViewModel().setItems(myElements);
255   }
256
257   protected abstract T cloneElement(T variable);
258
259   protected abstract boolean canDeleteElement(T selection);
260
261   protected static abstract class ElementsColumnInfoBase<T> extends ColumnInfo<T, String> {
262     private DefaultTableCellRenderer myRenderer;
263
264     protected ElementsColumnInfoBase(String name) {
265       super(name);
266     }
267
268     @Override
269     public TableCellRenderer getRenderer(T element) {
270       if (myRenderer == null) {
271         myRenderer = new DefaultTableCellRenderer();
272       }
273       if (element != null) {
274         myRenderer.setText(valueOf(element));
275         myRenderer.setToolTipText(getDescription(element));
276       }
277       return myRenderer;
278     }
279
280     @Nullable
281     protected abstract String getDescription(T element);
282   }
283 }