IDEA-103464 Adding environment variables to a Tomcat server is a UX nightmare
[idea/community.git] / platform / platform-api / src / com / intellij / ui / table / TableView.java
1 /*
2  * Copyright 2000-2012 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.ui.table;
17
18 import com.intellij.ui.GuiUtils;
19 import com.intellij.ui.TableUtil;
20 import com.intellij.util.SmartList;
21 import com.intellij.util.containers.ContainerUtil;
22 import com.intellij.util.ui.ColumnInfo;
23 import com.intellij.util.ui.ListTableModel;
24 import com.intellij.util.ui.SortableColumnModel;
25 import com.intellij.util.ui.TableViewModel;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import javax.swing.*;
30 import javax.swing.event.TableModelEvent;
31 import javax.swing.table.*;
32 import java.awt.*;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36
37 public class TableView<Item> extends BaseTableView implements ItemsProvider, SelectionProvider {
38   public TableView() {
39     this(new ListTableModel<Item>(ColumnInfo.EMPTY_ARRAY));
40   }
41
42   public TableView(final ListTableModel<Item> model) {
43     super(model);
44     setModelAndUpdateColumns(model);
45   }
46
47   @Override
48   public void setModel(@NotNull final TableModel dataModel) {
49     assert dataModel instanceof SortableColumnModel : "SortableColumnModel required";
50     super.setModel(dataModel);
51   }
52
53   /**
54    * use {@link #setModelAndUpdateColumns(com.intellij.util.ui.ListTableModel<Item>)} instead
55    * @param model
56    */
57   @Deprecated
58   public void setModel(final ListTableModel<Item> model) {
59     setModelAndUpdateColumns(model);
60   }
61   
62   public void setModelAndUpdateColumns(final ListTableModel<Item> model) {
63     super.setModel(model);
64     createDefaultColumnsFromModel();
65     updateColumnSizes();
66   }
67
68   public ListTableModel<Item> getListTableModel() {
69     return (ListTableModel<Item>)super.getModel();
70   }
71
72   @Override
73   public TableCellRenderer getCellRenderer(int row, int column) {
74     final ColumnInfo<Item, ?> columnInfo = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)];
75     final Item item = getRow(row);
76     final TableCellRenderer renderer = columnInfo.getCustomizedRenderer(item, columnInfo.getRenderer(item));
77     if (renderer == null) {
78       return super.getCellRenderer(row, column);
79     }
80     else {
81       return renderer;
82     }
83   }
84
85   @Override
86   public void tableChanged(TableModelEvent e) {
87     if (isEditing()) getCellEditor().cancelCellEditing();
88     super.tableChanged(e);
89   }
90
91   public void setSelection(Collection<Item> selection) {
92     clearSelection();
93     for (Object aSelection : selection) {
94       addSelection(aSelection);
95     }
96   }
97
98   public void updateColumnSizes() {
99     final JTableHeader header = getTableHeader();
100     final TableCellRenderer defaultRenderer = header == null? null : header.getDefaultRenderer();
101
102     final RowSorter<? extends TableModel> sorter = getRowSorter();
103     final RowSorter.SortKey sortKey = sorter == null ? null : ContainerUtil.getFirstItem(sorter.getSortKeys());
104     ColumnInfo[] columnInfos = getListTableModel().getColumnInfos();
105     TableColumnModel columnModel = getColumnModel();
106     int visibleColumnCount = columnModel.getColumnCount();
107     int[] sizeMode = new int[visibleColumnCount];
108     int[] headers = new int[visibleColumnCount];
109     int[] widths = new int[visibleColumnCount];
110     int allColumnWidth = 0;
111     int allColumnCurrent = 0;
112     int varCount = 0;
113
114     Icon sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
115
116     // calculate
117     for (int i = 0; i < visibleColumnCount; i++) {
118       final TableColumn column = columnModel.getColumn(i);
119       final ColumnInfo columnInfo = columnInfos[column.getModelIndex()];
120
121       TableCellRenderer columnHeaderRenderer = column.getHeaderRenderer();
122       if (columnHeaderRenderer == null) {
123         columnHeaderRenderer = defaultRenderer;
124       }
125       final Component headerComponent = columnHeaderRenderer == null? null :
126         columnHeaderRenderer.getTableCellRendererComponent(this, column.getHeaderValue(), false, false, 0, i);
127
128       if (headerComponent != null) {
129         headers[i] = headerComponent.getPreferredSize().width;
130         // add sort icon width
131         if (sorter != null && columnInfo.isSortable() && sortIcon != null &&
132             (sortKey == null || sortKey.getColumn() != i)) {
133           headers[i] += sortIcon.getIconWidth() + (headerComponent instanceof JLabel? ((JLabel)headerComponent).getIconTextGap() : 0);
134         }
135       }
136       final String maxStringValue;
137       final String preferredValue;
138       if (columnInfo.getWidth(this) > 0) {
139         sizeMode[i] = 1;
140         int width = columnInfo.getWidth(this);
141         widths[i] = width;
142       }
143       else if ((maxStringValue = columnInfo.getMaxStringValue()) != null) {
144         sizeMode[i] = 2;
145         widths[i] = getFontMetrics(getFont()).stringWidth(maxStringValue) + columnInfo.getAdditionalWidth();
146         varCount ++;
147       }
148       else if ((preferredValue = columnInfo.getPreferredStringValue()) != null) {
149         sizeMode[i] = 3;
150         widths[i] = getFontMetrics(getFont()).stringWidth(preferredValue) + columnInfo.getAdditionalWidth();
151         varCount ++;
152       }
153       allColumnWidth += widths[i];
154       allColumnCurrent += column.getPreferredWidth();
155     }
156     allColumnWidth = Math.max(allColumnWidth, allColumnCurrent);
157
158     // apply: distribute available space between resizable columns
159     //        and make sure that header will fit as well
160     int viewWidth = getParent() != null? getParent().getWidth() : getWidth();
161     double gold = 0.5 * (3 - Math.sqrt(5));
162     int addendum = varCount == 0 || viewWidth < allColumnWidth ?
163                    0 : (int)((allColumnWidth < gold * viewWidth ? gold * viewWidth :
164                               allColumnWidth < (1 - gold) * viewWidth ? (1 - gold) * viewWidth :
165                               viewWidth) - allColumnWidth) / varCount;
166
167     for (int i = 0 ; i < visibleColumnCount; i++) {
168       TableColumn column = columnModel.getColumn(i);
169       int width = widths[i];
170       if (sizeMode[i] == 1) {
171         column.setMaxWidth(width);
172         column.setPreferredWidth(width);
173         column.setMinWidth(width);
174       }
175       else if (sizeMode[i] == 2) {
176         // do not shrink columns
177         width = Math.max(column.getPreferredWidth(), Math.max(width + addendum, headers[i]));
178         column.setPreferredWidth(width);
179         column.setMaxWidth(width);
180       }
181       else if (sizeMode[i] == 3) {
182         // do not shrink columns
183         width = Math.max(column.getPreferredWidth(), Math.max(width + addendum, headers[i]));
184         column.setPreferredWidth(width);
185       }
186     }
187   }
188
189
190   @Override
191   public Collection<Item> getSelection() {
192     return getSelectedObjects();
193   }
194
195   @Nullable
196   public Item getSelectedObject() {
197     final int row = getSelectedRow();
198     ListTableModel<Item> model = getListTableModel();
199     return row >= 0 && row < model.getRowCount() ? model.getRowValue(convertRowIndexToModel(row)) : null;
200   }
201
202   @NotNull
203   public List<Item> getSelectedObjects() {
204     ListSelectionModel selectionModel = getSelectionModel();
205     int minSelectionIndex = selectionModel.getMinSelectionIndex();
206     int maxSelectionIndex = selectionModel.getMaxSelectionIndex();
207     if (minSelectionIndex == -1 || maxSelectionIndex == -1) {
208       return Collections.emptyList();
209     }
210
211     List<Item> result = new SmartList<Item>();
212     ListTableModel<Item> model = getListTableModel();
213     for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) {
214       if (selectionModel.isSelectedIndex(i)) {
215         int modelIndex = convertRowIndexToModel(i);
216         if (modelIndex >= 0 && modelIndex < model.getRowCount()) {
217           result.add(model.getRowValue(modelIndex));
218         }
219       }
220     }
221     return result;
222   }
223
224   @Override
225   public void addSelection(Object item) {
226     @SuppressWarnings("unchecked")
227     int index = getListTableModel().indexOf((Item)item);
228     if (index < 0) {
229       return;
230     }
231
232     getSelectionModel().addSelectionInterval(convertRowIndexToView(index), convertRowIndexToView(index));
233     // fix cell selection case
234     getColumnModel().getSelectionModel().addSelectionInterval(0, getColumnCount()-1);
235   }
236
237   @Override
238   public TableCellEditor getCellEditor(int row, int column) {
239     @SuppressWarnings("unchecked")
240     TableCellEditor editor = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)].getEditor(getRow(row));
241     return editor == null ? super.getCellEditor(row, column) : editor;
242   }
243
244   @Override
245   public List<Item> getItems() {
246     return getListTableModel().getItems();
247   }
248
249   public Item getRow(int row) {
250     return getListTableModel().getRowValue(convertRowIndexToModel(row));
251   }
252
253   public void setMinRowHeight(int i) {
254     setRowHeight(Math.max(i, getRowHeight()));
255   }
256
257   public JTable getComponent() {
258     return this;
259   }
260
261   public TableViewModel<Item> getTableViewModel() {
262     return getListTableModel();
263   }
264
265   public void stopEditing() {
266     TableUtil.stopEditing(this);
267   }
268
269   @Override
270   protected void createDefaultEditors() {
271     super.createDefaultEditors();
272
273     //noinspection unchecked
274     defaultEditorsByColumnClass.put(String.class, new UIDefaults.LazyValue() {
275       @Override
276       public Object createValue(UIDefaults table) {
277         DefaultCellEditor editor = new DefaultCellEditor(GuiUtils.createUndoableTextField());
278         editor.setClickCountToStart(1);
279         return editor;
280       }
281     });
282   }
283 }