package com.intellij.lang.javascript.flex.projectStructure.ui;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.treeStructure.treetable.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.EditableModel;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.tree.TreeUtil;
import javax.swing.*;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.tree.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author ksafonov
*/
abstract class EditableTreeTable<T> extends TreeTable {
private static final Object HEIGHT_TEST_MARKER = new Object();
public EditableTreeTable(String firstColumnName, ColumnInfo... columns) {
super(new ListTreeTableModelOnColumns(new DefaultMutableTreeNode(),
ArrayUtil.mergeArrays(new ColumnInfo[]{new FirstColumnInfo(firstColumnName)}, wrap(columns))));
final ColoredTreeCellRenderer r = new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (userObject == HEIGHT_TEST_MARKER) {
return;
}
render(this, (T)userObject);
setPaintFocusBorder(false);
}
};
setTreeCellRenderer(new TreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
return r.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true);
}
});
Dimension s = r.getTreeCellRendererComponent(getTree(), new DefaultMutableTreeNode(HEIGHT_TEST_MARKER), false, false, true, 0, false)
.getPreferredSize();
getTree().setRowHeight(s.height);
}
// copied from TableView
public void updateColumnSizes() {
final JTableHeader header = getTableHeader();
final TableCellRenderer defaultRenderer = header == null? null : header.getDefaultRenderer();
ColumnInfo[] columns = ((ListTreeTableModelOnColumns)getTableModel()).getColumnInfos();
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);
final Dimension headerSize = headerComponent == null ? JBUI.emptySize() : headerComponent.getPreferredSize();
final String maxStringValue;
final String preferredValue;
if (columnInfo.getWidth(this) > 0) {
int width = columnInfo.getWidth(this);
column.setMaxWidth(width);
column.setPreferredWidth(width);
column.setMinWidth(width);
}
else if ((maxStringValue = columnInfo.getMaxStringValue()) != null) {
int width = getFontMetrics(getFont()).stringWidth(maxStringValue) + columnInfo.getAdditionalWidth();
width = Math.max(width, headerSize.width);
column.setPreferredWidth(width);
column.setMaxWidth(width);
}
else if ((preferredValue = columnInfo.getPreferredStringValue()) != null) {
int width = getFontMetrics(getFont()).stringWidth(preferredValue) + columnInfo.getAdditionalWidth();
width = Math.max(width, headerSize.width);
column.setPreferredWidth(width);
}
}
}
@Override
public void setModel(TreeTableModel treeTableModel) {
super.setModel(treeTableModel);
updateColumnSizes();
}
// copied from TableView
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
ListTreeTableModelOnColumns model = (ListTreeTableModelOnColumns)getTableModel();
ColumnInfo columnInfo = model.getColumnInfos()[convertColumnIndexToModel(column)];
T item = (T)getValueAt(convertRowIndexToModel(row), 0);
final TableCellRenderer renderer = columnInfo.getCustomizedRenderer(item, columnInfo.getRenderer(item));
return renderer == null ? super.getCellRenderer(row, column) : renderer;
}
// copied from TableView
@Override
public TableCellEditor getCellEditor(int row, int column) {
ListTreeTableModelOnColumns model = (ListTreeTableModelOnColumns)getTableModel();
final ColumnInfo columnInfo = model.getColumnInfos()[convertColumnIndexToModel(column)];
T item = (T)getValueAt(convertRowIndexToModel(row), 0);
final TableCellEditor editor = columnInfo.getEditor(item);
return editor == null ? super.getCellEditor(row, column) : editor;
}
public void refreshItemAt(final Integer row) {
Object node = getTree().getPathForRow(row).getLastPathComponent();
((DefaultTreeModel)getTree().getModel()).nodeChanged((TreeNode)node);
}
private static ColumnInfo[] wrap(ColumnInfo[] columns) {
return ContainerUtil.map2Array(columns, ColumnInfo.class, (Function<ColumnInfo, ColumnInfo>)columnInfo -> new ColumnInfoWrapper(columnInfo));
}
public void refresh() {
List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(getTree());
((DefaultTreeModel)getTree().getModel()).reload();
TreeUtil.restoreExpandedPaths(getTree(), expandedPaths);
}
protected abstract void render(SimpleColoredComponent coloredTreeCellRenderer, T userObject);
//@Override
//protected TreeTableModelAdapter adapt(TreeTableModel treeTableModel) {
// return new EditableModelAdapter(treeTableModel, getTree(), this);
//}
public List<T> getItems() {
int rows = getRowCount();
List<T> result = new ArrayList<>(rows);
for (int row = 0; row < rows; row++) {
result.add(getItemAt(row));
}
return result;
}
public T getItemAt(int row) {
return (T)getValueAt(row, 0);
}
public DefaultMutableTreeNode getRoot() {
return (DefaultMutableTreeNode)getTree().getModel().getRoot();
}
@Override
public TreeTableCellRenderer createTableRenderer(TreeTableModel treeTableModel) {
TreeTableCellRenderer r = super.createTableRenderer(treeTableModel);
r.setDefaultBorder(null);
return r;
}
private static class EditableModelAdapter extends TreeTableModelAdapter implements EditableModel {
public EditableModelAdapter(TreeTableModel treeTableModel, JTree tree, JTable table) {
super(treeTableModel, tree, table);
}
@Override
public void addRow() {
}
@Override
public void removeRow(int index) {
}
@Override
public void exchangeRows(int oldIndex, int newIndex) {
}
@Override
public boolean canExchangeRows(int oldIndex, int newIndex) {
return false;
}
}
private static class FirstColumnInfo<T> extends ColumnInfo<DefaultMutableTreeNode, T> {
public FirstColumnInfo(String name) {
super(name);
}
@Override
public T valueOf(DefaultMutableTreeNode treeNode) {
return (T)treeNode.getUserObject();
}
public Class getColumnClass() {
return TreeTableModel.class;
}
}
private static class ColumnInfoWrapper<T, Aspect> extends ColumnInfo<Object, Aspect> {
private final ColumnInfo<T, Aspect> myDelegate;
public ColumnInfoWrapper(ColumnInfo<T, Aspect> columnInfo) {
super(columnInfo.getName());
myDelegate = columnInfo;
}
@Override
public Class getColumnClass() {
return ColumnInfoWrapper.class;
}
@Override
public Aspect valueOf(Object item) {
return myDelegate.valueOf((T)((DefaultMutableTreeNode)item).getUserObject());
}
@Override
public TableCellRenderer getRenderer(Object item) {
return myDelegate.getRenderer((T)item);
}
@Override
public TableCellRenderer getCustomizedRenderer(Object item, TableCellRenderer renderer) {
return myDelegate.getCustomizedRenderer((T)item, renderer);
}
@Override
public TableCellEditor getEditor(Object item) {
return myDelegate.getEditor((T)item);
}
@Override
public boolean isCellEditable(Object item) {
return myDelegate.isCellEditable((T)((DefaultMutableTreeNode)item).getUserObject());
}
@Override
public void setValue(Object item, Aspect value) {
myDelegate.setValue((T)((DefaultMutableTreeNode)item).getUserObject(), value);
}
@Override
public int getWidth(JTable table) {
return myDelegate.getWidth(table);
}
@Override
public String getMaxStringValue() {
return myDelegate.getMaxStringValue();
}
}
}