blob: 44b40e938cdbce667a97c1bb5b8e443984925d85 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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.intellij.ui.table;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.TableUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.SortableColumnModel;
import com.intellij.util.ui.TableViewModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;
import java.awt.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class TableView<Item> extends BaseTableView implements ItemsProvider, SelectionProvider {
public TableView() {
this(new ListTableModel<Item>(ColumnInfo.EMPTY_ARRAY));
}
public TableView(final ListTableModel<Item> model) {
super(model);
setModelAndUpdateColumns(model);
}
@Override
public void setModel(@NotNull final TableModel dataModel) {
assert dataModel instanceof SortableColumnModel : "SortableColumnModel required";
super.setModel(dataModel);
}
/**
* use {@link #setModelAndUpdateColumns(com.intellij.util.ui.ListTableModel<Item>)} instead
* @param model
*/
@Deprecated
public void setModel(final ListTableModel<Item> model) {
setModelAndUpdateColumns(model);
}
public void setModelAndUpdateColumns(final ListTableModel<Item> model) {
super.setModel(model);
createDefaultColumnsFromModel();
updateColumnSizes();
}
public ListTableModel<Item> getListTableModel() {
return (ListTableModel<Item>)super.getModel();
}
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
final ColumnInfo<Item, ?> columnInfo = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)];
final Item item = getRow(row);
final TableCellRenderer renderer = columnInfo.getCustomizedRenderer(item, columnInfo.getRenderer(item));
if (renderer == null) {
return super.getCellRenderer(row, column);
}
else {
return renderer;
}
}
@Override
public void tableChanged(TableModelEvent e) {
if (isEditing()) getCellEditor().cancelCellEditing();
super.tableChanged(e);
}
public void setSelection(Collection<Item> selection) {
clearSelection();
for (Object aSelection : selection) {
addSelection(aSelection);
}
}
public void updateColumnSizes() {
final JTableHeader header = getTableHeader();
final TableCellRenderer defaultRenderer = header == null? null : header.getDefaultRenderer();
final RowSorter<? extends TableModel> sorter = getRowSorter();
final RowSorter.SortKey sortKey = sorter == null ? null : ContainerUtil.getFirstItem(sorter.getSortKeys());
ColumnInfo[] columnInfos = getListTableModel().getColumnInfos();
TableColumnModel columnModel = getColumnModel();
int visibleColumnCount = columnModel.getColumnCount();
int[] sizeMode = new int[visibleColumnCount];
int[] headers = new int[visibleColumnCount];
int[] widths = new int[visibleColumnCount];
int allColumnWidth = 0;
int allColumnCurrent = 0;
int varCount = 0;
Icon sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
// calculate
for (int i = 0; i < visibleColumnCount; i++) {
final TableColumn column = columnModel.getColumn(i);
final ColumnInfo columnInfo = columnInfos[column.getModelIndex()];
TableCellRenderer columnHeaderRenderer = column.getHeaderRenderer();
if (columnHeaderRenderer == null) {
columnHeaderRenderer = defaultRenderer;
}
final Component headerComponent = columnHeaderRenderer == null? null :
columnHeaderRenderer.getTableCellRendererComponent(this, column.getHeaderValue(), false, false, 0, i);
if (headerComponent != null) {
headers[i] = headerComponent.getPreferredSize().width;
// add sort icon width
if (sorter != null && columnInfo.isSortable() && sortIcon != null &&
(sortKey == null || sortKey.getColumn() != i)) {
headers[i] += sortIcon.getIconWidth() + (headerComponent instanceof JLabel? ((JLabel)headerComponent).getIconTextGap() : 0);
}
}
final String maxStringValue;
final String preferredValue;
if (columnInfo.getWidth(this) > 0) {
sizeMode[i] = 1;
int width = columnInfo.getWidth(this);
widths[i] = width;
}
else if ((maxStringValue = columnInfo.getMaxStringValue()) != null) {
sizeMode[i] = 2;
widths[i] = getFontMetrics(getFont()).stringWidth(maxStringValue) + columnInfo.getAdditionalWidth();
varCount ++;
}
else if ((preferredValue = columnInfo.getPreferredStringValue()) != null) {
sizeMode[i] = 3;
widths[i] = getFontMetrics(getFont()).stringWidth(preferredValue) + columnInfo.getAdditionalWidth();
varCount ++;
}
allColumnWidth += widths[i];
allColumnCurrent += column.getPreferredWidth();
}
allColumnWidth = Math.max(allColumnWidth, allColumnCurrent);
// apply: distribute available space between resizable columns
// and make sure that header will fit as well
int viewWidth = getParent() != null? getParent().getWidth() : getWidth();
double gold = 0.5 * (3 - Math.sqrt(5));
int addendum = varCount == 0 || viewWidth < allColumnWidth ?
0 : (int)((allColumnWidth < gold * viewWidth ? gold * viewWidth :
allColumnWidth < (1 - gold) * viewWidth ? (1 - gold) * viewWidth :
viewWidth) - allColumnWidth) / varCount;
for (int i = 0 ; i < visibleColumnCount; i++) {
TableColumn column = columnModel.getColumn(i);
int width = widths[i];
if (sizeMode[i] == 1) {
column.setMaxWidth(width);
column.setPreferredWidth(width);
column.setMinWidth(width);
}
else if (sizeMode[i] == 2) {
// do not shrink columns
width = Math.max(column.getPreferredWidth(), Math.max(width + addendum, headers[i]));
column.setPreferredWidth(width);
column.setMaxWidth(width);
}
else if (sizeMode[i] == 3) {
// do not shrink columns
width = Math.max(column.getPreferredWidth(), Math.max(width + addendum, headers[i]));
column.setPreferredWidth(width);
}
}
}
@Override
public Collection<Item> getSelection() {
return getSelectedObjects();
}
@Nullable
public Item getSelectedObject() {
final int row = getSelectedRow();
ListTableModel<Item> model = getListTableModel();
return row >= 0 && row < model.getRowCount() ? model.getRowValue(convertRowIndexToModel(row)) : null;
}
@NotNull
public List<Item> getSelectedObjects() {
ListSelectionModel selectionModel = getSelectionModel();
int minSelectionIndex = selectionModel.getMinSelectionIndex();
int maxSelectionIndex = selectionModel.getMaxSelectionIndex();
if (minSelectionIndex == -1 || maxSelectionIndex == -1) {
return Collections.emptyList();
}
List<Item> result = new SmartList<Item>();
ListTableModel<Item> model = getListTableModel();
for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) {
if (selectionModel.isSelectedIndex(i)) {
int modelIndex = convertRowIndexToModel(i);
if (modelIndex >= 0 && modelIndex < model.getRowCount()) {
result.add(model.getRowValue(modelIndex));
}
}
}
return result;
}
@Override
public void addSelection(Object item) {
@SuppressWarnings("unchecked")
int index = getListTableModel().indexOf((Item)item);
if (index < 0) {
return;
}
getSelectionModel().addSelectionInterval(convertRowIndexToView(index), convertRowIndexToView(index));
// fix cell selection case
getColumnModel().getSelectionModel().addSelectionInterval(0, getColumnCount()-1);
}
@Override
public TableCellEditor getCellEditor(int row, int column) {
@SuppressWarnings("unchecked")
TableCellEditor editor = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)].getEditor(getRow(row));
return editor == null ? super.getCellEditor(row, column) : editor;
}
@Override
public List<Item> getItems() {
return getListTableModel().getItems();
}
public Item getRow(int row) {
return getListTableModel().getRowValue(convertRowIndexToModel(row));
}
public void setMinRowHeight(int i) {
setRowHeight(Math.max(i, getRowHeight()));
}
public JTable getComponent() {
return this;
}
public TableViewModel getTableViewModel() {
return getListTableModel();
}
public void stopEditing() {
TableUtil.stopEditing(this);
}
@Override
protected void createDefaultEditors() {
super.createDefaultEditors();
//noinspection unchecked
defaultEditorsByColumnClass.put(String.class, new UIDefaults.LazyValue() {
@Override
public Object createValue(UIDefaults table) {
return new DefaultCellEditor(GuiUtils.createUndoableTextField());
}
});
}
}