package org.incha.ui.classview;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import org.incha.core.JavaProject;
import org.incha.core.jswingripples.eig.JSwingRipplesEIGNode;
public abstract class AbstractHierarchicalView extends JTable {
private static final long serialVersionUID = -725916023414871313L;
/**
* The member hierarchy support.
*/
private MemberHierarchySupport support = new MemberHierarchySupport(
new LinkedList<JSwingRipplesEIGNode>());
private Map<JSwingRipplesEIGNode, Boolean> expandedStates = new HashMap<JSwingRipplesEIGNode, Boolean>();
private final JavaProject project;
/**
* Default constructor.
*/
public AbstractHierarchicalView(final JavaProject project) {
super();
this.project = project;
setModel(createModel());
setAutoCreateColumnsFromModel(true);
setColumnSelectionAllowed(false);
setShowHorizontalLines(false);
setDragEnabled(false);
setRowHeight(20);
final TableCellRenderer cellRenderer = createCellRenderer();
final TableCellRenderer headerRenderer = createHeaderRenderer();
//set renderer to all columns
final int count = getColumnCount();
for (int i = 0; i < count; i++) {
final TableColumn column = getColumnModel().getColumn(i);
column.setCellRenderer(cellRenderer);
if (headerRenderer != null) {
column.setHeaderRenderer(headerRenderer);
}
}
addMouseListener(new MouseAdapter() {
/* (non-Javadoc)
* @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
*/
@Override
public void mouseClicked(final MouseEvent e) {
if (!SwingUtilities.isRightMouseButton(e)) {
expandOrCollapse(e.getX(), e.getY());
}
}
});
}
/**
* @return
*/
protected TableCellRenderer createHeaderRenderer() {
return new HeaderRenderer(this);
}
public void setData(final Collection<JSwingRipplesEIGNode> members) {
getClassHierarchyModel().clear();
support = new MemberHierarchySupport(members);
//add data from provider
final JSwingRipplesEIGNode[] types = support.getRootTypes();
Arrays.sort(types, new MemberComparator());
getClassHierarchyModel().addAll(types, 0);
}
/**
* @return
*/
protected abstract ClassTreeDataModel createModel();
/**
* @return table cell renderer
*/
protected abstract AbstractMemberRenderer createCellRenderer();
public boolean isExpanded(final JSwingRipplesEIGNode member) {
return Boolean.TRUE == expandedStates.get(member);
}
/**
* @return
*/
public ClassTreeDataModel getClassHierarchyModel() {
return (ClassTreeDataModel) getModel();
}
/**
* @param x mouse x coordinate.
* @param y mouse y coordinate.
*/
protected void expandOrCollapse(final int x, final int y) {
final ClassTreeDataModel model = getClassHierarchyModel();
final JSwingRipplesEIGNode m = getSelectedItem(x, y);
if(m != null && hasChildren(m)) {
int row = rowAtPoint(new Point(x, y));
final Rectangle rect = getCellRect(row, 0, true);
if (!rect.contains(x, y)
|| x < getHierarchyOffset(m)
|| x > getHierarchyOffset(m) + 16) {
return;
}
if (isExpanded(m)) {
expandedStates.put(m, Boolean.FALSE);
final int depth = getHierarchyDepth(m);
//collapse node
row++;
while (row < getRowCount() && depth < getHierarchyDepth(model.getValueAt(row, 0))) {
model.removeRow(row);
}
} else {
expandedStates.put(m, Boolean.TRUE);
//expand
final JSwingRipplesEIGNode[] members = support.getChildren(m);
Arrays.sort(members, new MemberComparator());
model.addAll(members, row + 1);
}
repaint();
}
}
/**
* @param x
* @param y
* @return
*/
protected JSwingRipplesEIGNode getSelectedItem(final int x, final int y) {
final int row = rowAtPoint(new Point(x, y));
if (row < 0) {
return null;
}
final ClassTreeDataModel model = getClassHierarchyModel();
return model.getValueAt(row, 0);
}
/**
* @param member
* @return
*/
public boolean hasChildren(final JSwingRipplesEIGNode member) {
return support.hasChildren(member);
}
/**
* @param member
* @return
*/
public int getHierarchyDepth(final JSwingRipplesEIGNode member) {
return support.getHierarchyDepth(member);
}
/**
* @param member
* @return
*/
public int getHierarchyOffset(final JSwingRipplesEIGNode member) {
return 20 * getHierarchyDepth(member);
}
/**
* @return the project
*/
public JavaProject getProject() {
return project;
}
}