/*
* 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.TableUtil;
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.ArrayList;
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);
}
public void setModel(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();
}
public TableCellRenderer getCellRenderer(int row, int column) {
final ColumnInfo<Item, ?> columnInfo = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)];
final Item item = getListTableModel().getItems().get(convertRowIndexToModel(row));
final TableCellRenderer renderer = columnInfo.getCustomizedRenderer(item, columnInfo.getRenderer(item));
if (renderer == null) {
return super.getCellRenderer(row, column);
}
else {
return renderer;
}
}
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[] columns = getListTableModel().getColumnInfos();
int[] sizeMode = new int[columns.length];
int[] headers = new int[columns.length];
int[] widths = new int[columns.length];
int allColumnWidth = 0;
int allColumnCurrent = 0;
int varCount = 0;
Icon sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
// calculate
for (int i = 0; i < columns.length; i++) {
final ColumnInfo columnInfo = columns[i];
final TableColumn column = getColumnModel().getColumn(i);
final Component headerComponent = defaultRenderer == null? null :
defaultRenderer.getTableCellRendererComponent(this, column.getHeaderValue(), false, false, 0, 0);
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<columns.length; i ++) {
TableColumn column = getColumnModel().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);
}
}
}
public Collection<Item> getSelection() {
ArrayList<Item> result = new ArrayList<Item>();
int[] selectedRows = getSelectedRows();
if (selectedRows == null) return result;
final List<Item> items = getItems();
if (! items.isEmpty()) {
for (int selectedRow : selectedRows) {
final int modelIndex = convertRowIndexToModel(selectedRow);
if (modelIndex >= 0 && modelIndex < items.size()) {
result.add(items.get(modelIndex));
}
}
}
return result;
}
@Nullable
public Item getSelectedObject() {
final int row = getSelectedRow();
final List<Item> list = getItems();
return row >= 0 && row < list.size() ? list.get(convertRowIndexToModel(row)) : null;
}
@NotNull
public List<Item> getSelectedObjects() {
final int[] selectedRows = getSelectedRows();
if (selectedRows == null || (selectedRows.length == 0)) return Collections.emptyList();
final List<Item> items = getItems();
final List<Item> result = new ArrayList<Item>();
for (int selectedRow : selectedRows) {
result.add(items.get(convertRowIndexToModel(selectedRow)));
}
return result;
}
public void addSelection(Object item) {
List items = getItems();
if (!items.contains(item)) return;
int index = items.indexOf(item);
getSelectionModel().addSelectionInterval(convertRowIndexToView(index), convertRowIndexToView(index));
// fix cell selection case
getColumnModel().getSelectionModel().addSelectionInterval(0, getColumnCount()-1);
}
public TableCellEditor getCellEditor(int row, int column) {
final ColumnInfo<Item, ?> columnInfo = getListTableModel().getColumnInfos()[convertColumnIndexToModel(column)];
final TableCellEditor editor = columnInfo.getEditor(getListTableModel().getItems().get(convertRowIndexToModel(row)));
return editor == null ? super.getCellEditor(row, column) : editor;
}
public List<Item> getItems() {
return getListTableModel().getItems();
}
public Item getRow(int row) {
return getItems().get(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);
}
}