2 * Copyright 2000-2012 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.ui.table;
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;
30 import javax.swing.event.TableModelEvent;
31 import javax.swing.table.*;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
37 public class TableView<Item> extends BaseTableView implements ItemsProvider, SelectionProvider {
39 this(new ListTableModel<Item>(ColumnInfo.EMPTY_ARRAY));
42 public TableView(final ListTableModel<Item> model) {
44 setModelAndUpdateColumns(model);
48 public void setModel(@NotNull final TableModel dataModel) {
49 assert dataModel instanceof SortableColumnModel : "SortableColumnModel required";
50 super.setModel(dataModel);
54 * use {@link #setModelAndUpdateColumns(com.intellij.util.ui.ListTableModel<Item>)} instead
58 public void setModel(final ListTableModel<Item> model) {
59 setModelAndUpdateColumns(model);
62 public void setModelAndUpdateColumns(final ListTableModel<Item> model) {
63 super.setModel(model);
64 createDefaultColumnsFromModel();
68 public ListTableModel<Item> getListTableModel() {
69 return (ListTableModel<Item>)super.getModel();
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);
86 public void tableChanged(TableModelEvent e) {
87 if (isEditing()) getCellEditor().cancelCellEditing();
88 super.tableChanged(e);
91 public void setSelection(Collection<Item> selection) {
93 for (Object aSelection : selection) {
94 addSelection(aSelection);
98 public void updateColumnSizes() {
99 final JTableHeader header = getTableHeader();
100 final TableCellRenderer defaultRenderer = header == null? null : header.getDefaultRenderer();
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;
114 Icon sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
117 for (int i = 0; i < visibleColumnCount; i++) {
118 final TableColumn column = columnModel.getColumn(i);
119 final ColumnInfo columnInfo = columnInfos[column.getModelIndex()];
121 TableCellRenderer columnHeaderRenderer = column.getHeaderRenderer();
122 if (columnHeaderRenderer == null) {
123 columnHeaderRenderer = defaultRenderer;
125 final Component headerComponent = columnHeaderRenderer == null? null :
126 columnHeaderRenderer.getTableCellRendererComponent(this, column.getHeaderValue(), false, false, 0, i);
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);
136 final String maxStringValue;
137 final String preferredValue;
138 if (columnInfo.getWidth(this) > 0) {
140 int width = columnInfo.getWidth(this);
143 else if ((maxStringValue = columnInfo.getMaxStringValue()) != null) {
145 widths[i] = getFontMetrics(getFont()).stringWidth(maxStringValue) + columnInfo.getAdditionalWidth();
148 else if ((preferredValue = columnInfo.getPreferredStringValue()) != null) {
150 widths[i] = getFontMetrics(getFont()).stringWidth(preferredValue) + columnInfo.getAdditionalWidth();
153 allColumnWidth += widths[i];
154 allColumnCurrent += column.getPreferredWidth();
156 allColumnWidth = Math.max(allColumnWidth, allColumnCurrent);
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;
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);
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);
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);
191 public Collection<Item> getSelection() {
192 return getSelectedObjects();
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;
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();
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));
225 public void addSelection(Object item) {
226 @SuppressWarnings("unchecked")
227 int index = getListTableModel().indexOf((Item)item);
232 getSelectionModel().addSelectionInterval(convertRowIndexToView(index), convertRowIndexToView(index));
233 // fix cell selection case
234 getColumnModel().getSelectionModel().addSelectionInterval(0, getColumnCount()-1);
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;
245 public List<Item> getItems() {
246 return getListTableModel().getItems();
249 public Item getRow(int row) {
250 return getListTableModel().getRowValue(convertRowIndexToModel(row));
253 public void setMinRowHeight(int i) {
254 setRowHeight(Math.max(i, getRowHeight()));
257 public JTable getComponent() {
261 public TableViewModel<Item> getTableViewModel() {
262 return getListTableModel();
265 public void stopEditing() {
266 TableUtil.stopEditing(this);
270 protected void createDefaultEditors() {
271 super.createDefaultEditors();
273 //noinspection unchecked
274 defaultEditorsByColumnClass.put(String.class, new UIDefaults.LazyValue() {
276 public Object createValue(UIDefaults table) {
277 DefaultCellEditor editor = new DefaultCellEditor(GuiUtils.createUndoableTextField());
278 editor.setClickCountToStart(1);