package hirondelle.stocks.table;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import hirondelle.stocks.util.Consts;
import hirondelle.stocks.util.Args;
final class TableSortIndicator extends Observable {
TableSortIndicator(JTable aTable, Icon aUpIcon, Icon aDownIcon) {
Args.checkForNull(aUpIcon);
Args.checkForNull(aDownIcon);
fTable = aTable;
fUpIcon = aUpIcon;
fDownIcon = aDownIcon;
fCurrentSort = SortBy.NONE;
initHeaderClickListener();
initHeaderRenderers();
assert getRenderer(0) != null : "Ctor - renderer 0 is null.";
}
SortBy getSortBy(){
return fCurrentSort;
}
void setSortBy( SortBy aTargetSort ){
validateIdx( aTargetSort.getColumn() );
initHeaderRenderers();
assert getRenderer(0) != null : "setSortBy - renderer 0 is null.";
fTargetSort = aTargetSort;
if ( fCurrentSort == SortBy.NONE ){
setInitialHeader();
}
else if (
fCurrentSort.getColumn() == fTargetSort.getColumn() &&
fCurrentSort.getOrder() != fTargetSort.getOrder()
) {
toggleIcon();
}
else {
updateTwoHeaders();
}
synchCurrentSortWithSelectedSort();
notifyAndPaint();
}
private final JTable fTable;
private final Icon fUpIcon;
private final Icon fDownIcon;
private SortBy fCurrentSort;
private SortBy fTargetSort;
private static final SortOrder fDEFAULT_SORT_ORDER = SortOrder.DESCENDING;
private boolean isValidColumnIdx(int aColumnIdx) {
return 0 <= aColumnIdx && aColumnIdx <= fTable.getColumnCount()-1 ;
}
private void validateIdx(int aSelectedIdx) {
if ( ! isValidColumnIdx(aSelectedIdx) ) {
throw new IllegalArgumentException("Column index is out of range: " + aSelectedIdx);
}
}
private void initHeaderRenderers(){
for (int idx=0; idx < fTable.getColumnCount(); ++idx) {
TableColumn column = fTable.getColumnModel().getColumn(idx);
column.setHeaderRenderer(new Renderer(fTable.getTableHeader()));
assert column.getHeaderRenderer() != null : "Header Renderer is null";
}
}
private Renderer getRenderer(int aColumnIdx) {
TableColumn column = fTable.getColumnModel().getColumn(aColumnIdx);
return (Renderer)column.getHeaderRenderer();
}
private void initHeaderClickListener() {
fTable.getTableHeader().addMouseListener( new MouseAdapter() {
@Override public void mouseClicked(MouseEvent event) {
int selectedIdx = fTable.getColumnModel().getColumnIndexAtX(event.getX());
processClick( selectedIdx );
}
});
}
private void processClick(int aSelectedIdx){
validateIdx(aSelectedIdx);
if (fCurrentSort.getColumn() == aSelectedIdx) {
fTargetSort = new SortBy( fCurrentSort.getOrder().toggle(), aSelectedIdx);
}
else {
fTargetSort = new SortBy(fDEFAULT_SORT_ORDER , aSelectedIdx);
}
if (fCurrentSort == SortBy.NONE){
setInitialHeader();
}
if (fCurrentSort.getColumn() == fTargetSort.getColumn()) {
toggleIcon();
}
else {
updateTwoHeaders();
}
synchCurrentSortWithSelectedSort();
notifyAndPaint();
}
private void notifyAndPaint(){
setChanged();
notifyObservers();
fTable.getTableHeader().resizeAndRepaint();
}
private void setInitialHeader(){
if (fTargetSort.getOrder() == SortOrder.DESCENDING){
getRenderer(fTargetSort.getColumn()).setIcon(fDownIcon);
}
else {
getRenderer(fTargetSort.getColumn()).setIcon(fUpIcon);
}
}
private void toggleIcon(){
Renderer renderer = getRenderer(fCurrentSort.getColumn());
if (fCurrentSort.getOrder() == SortOrder.ASCENDING) {
renderer.setIcon(fDownIcon);
}
else {
renderer.setIcon(fUpIcon);
}
}
private void updateTwoHeaders() {
getRenderer(fCurrentSort.getColumn()).setIcon(null);
getRenderer(fTargetSort.getColumn()).setIcon(fDownIcon);
}
private void synchCurrentSortWithSelectedSort(){
fCurrentSort = fTargetSort;
}
private final class Renderer extends DefaultTableCellRenderer {
Renderer(JTableHeader aTableHeader){
setHorizontalAlignment(JLabel.CENTER);
setForeground(aTableHeader.getForeground());
setBackground(aTableHeader.getBackground());
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
fTableHeader = aTableHeader;
}
@Override public Component getTableCellRendererComponent(
JTable aTable, Object aValue, boolean aIsSelected,
boolean aHasFocus, int aRowIdx, int aColumnIdx
) {
setText((aValue == null) ? Consts.EMPTY_STRING : aValue.toString());
setFont(fTableHeader.getFont());
return this;
}
private JTableHeader fTableHeader;
}
}