package magic.ui.widget.cards.table;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import magic.model.MagicCardDefinition;
import magic.model.MagicManaCost;
import magic.ui.helpers.ColorHelper;
import magic.ui.utility.MagicStyle;
import magic.ui.widget.CostPanel;
@SuppressWarnings("serial")
public class CardsJTable extends JTable
implements MouseListener, MouseMotionListener {
private static final int ROW_HEIGHT = 20; // pixels
private static final Color GRID_COLOR = new Color(194, 197, 203);
private Color DEFAULT_GRID_COLOR;
private final Border NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
private static final Border SELECTED_BORDER =
BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(1, 0, 1, 0, MagicStyle.getRolloverColor()),
BorderFactory.createEmptyBorder(0, 1, 0, 0)
);
private final Color defaultForeColor = getForeground();
private int mouseOverRow = -1;
private final CardTableModel tableModel;
public CardsJTable(CardTableModel dm) {
super(dm);
this.tableModel = dm;
setDefaultProperties();
setColumnRenderers();
setDefaultColumnProperties();
}
private void setMouseListeners() {
if (CardsTableStyle.getStyle() == CardsTableStyle.THEME) {
addMouseMotionListener(this);
addMouseListener(this);
} else {
removeMouseMotionListener(this);
removeMouseListener(this);
}
}
private void setDefaultProperties() {
DEFAULT_GRID_COLOR = getGridColor();
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // otherwise horizontal scrollbar won't work
setRowHeight(ROW_HEIGHT);
setOpaque(false);
setForeground(ColorHelper.getOppositeColor(getForeground()));
setStyleProperties();
setFillsViewportHeight(true);
}
private void setStyleProperties() {
setShowGrid(CardsTableStyle.getStyle() != CardsTableStyle.THEME);
setGridColor(CardsTableStyle.getStyle() == CardsTableStyle.LIGHT ? GRID_COLOR : DEFAULT_GRID_COLOR);
setMouseListeners();
}
private void setDefaultColumnProperties() {
final TableColumnModel cm = getColumnModel();
// set initial column widths.
for (int i = 0; i < cm.getColumnCount(); i++) {
cm.getColumn(i).setMinWidth(CardTableColumn.getMinWidth(i));
cm.getColumn(i).setPreferredWidth(CardTableColumn.getMinWidth(i));
}
final JTableHeader header = getTableHeader();
header.setReorderingAllowed(true);
final DefaultTableCellRenderer renderer =
(DefaultTableCellRenderer) header.getDefaultRenderer();
// center the column header captions.
renderer.setHorizontalAlignment(SwingConstants.CENTER);
}
/*
Default cell renderer
*/
private static final DefaultTableCellRenderer defaultCellRenderer = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final JComponent c = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBorder(noFocusBorder);
return c;
}
};
private static final DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
static {
centerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
}
private void setCostColumnRenderer() {
getColumnModel().getColumn(CardTableColumn.Cost.ordinal())
.setCellRenderer(getCostRenderer());
}
private void setColCentered(CardTableColumn col) {
getColumnModel().getColumn(col.ordinal()).setCellRenderer(centerRenderer);
}
private void setColumnRenderers() {
setColCentered(CardTableColumn.Rating);
setColCentered(CardTableColumn.Power);
setColCentered(CardTableColumn.Toughness);
setCostColumnRenderer();
setDefaultRenderer(Object.class, defaultCellRenderer);
}
/*
Per-cell rendering.
*/
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
switch (CardsTableStyle.getStyle()) {
case LIGHT: return prepareDefaultRenderer(renderer, row, column);
case THEME: return prepareThemedRenderer(renderer, row, column);
case DARK: return prepareMidnightRenderer(renderer, row, column);
default: throw new UnsupportedOperationException();
}
}
private DefaultTableCellRenderer getCostRenderer() {
switch (CardsTableStyle.getStyle()) {
case LIGHT: return defaultCostRenderer;
case THEME: return themedCostRenderer;
case DARK: return midnightCostRenderer;
default: throw new UnsupportedOperationException();
}
}
/******************************************************************
* DARK THEME
******************************************************************/
private static final Color DEF_ROW_COLOR =
UIManager.getLookAndFeelDefaults().getColor("Table:\"Table.cellRenderer\".background");
private static final Color DEF_ROW_COLOR_NEG =
ColorHelper.getOppositeColor(DEF_ROW_COLOR);
private static final Color ALT_ROW_COLOR =
UIManager.getLookAndFeelDefaults().getColor("Table.alternateRowColor");
private static final Color ALT_ROW_COLOR_NEG =
ColorHelper.getOppositeColor(ALT_ROW_COLOR);
//
// default cell renderer
//
private Component prepareMidnightRenderer(TableCellRenderer renderer, int row, int column) {
Component cell = super.prepareRenderer(renderer, row, column);
final MagicCardDefinition card = tableModel.getCardDef(row);
final boolean isRowSelected = isRowSelected(row);
final boolean isAlternate = row % 2 == 0;
((JComponent) cell).setOpaque(true);
((JComponent) cell).setBorder(NO_FOCUS_BORDER);
// Text Color
final Color c1 = isRowSelected
? card.isInvalid()
? Color.LIGHT_GRAY
: getSelectionForeground()
: card.isInvalid()
? Color.GRAY
: defaultForeColor;
final Color c2 = ColorHelper.getOppositeColor(c1);
cell.setForeground(c2);
cell.setBackground(isRowSelected
? getSelectionBackground()
: isAlternate ? DEF_ROW_COLOR_NEG : ALT_ROW_COLOR_NEG
);
return cell;
}
//
// Mana cost renderer
//
private final DefaultTableCellRenderer midnightCostRenderer = new DefaultTableCellRenderer() {
private final Border CostBorder = new EmptyBorder(1, 1, 1, 4);
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int col) {
// match border and background formatting with default
final JComponent defaultRender = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
final MagicCardDefinition card = tableModel.getCardDef(row);
final CostPanel costPanel = new CostPanel(card.hasCost() ? card.getCost() : null);
final boolean isAlternate = row % 2 == 0;
costPanel.setBackground(isSelected
? getSelectionBackground()
: isAlternate
? DEF_ROW_COLOR_NEG
: ALT_ROW_COLOR_NEG
);
costPanel.setForeground(defaultRender.getForeground());
costPanel.setOpaque(defaultRender.isOpaque());
costPanel.setBorder(CostBorder);
return costPanel;
}
};
/******************************************************************
* DEFAULT THEME
******************************************************************/
private static final Color SELECT_BCOLOR =
UIManager.getColor("Table[Enabled+Selected].textBackground");
private static final Color ALTERNATE_COLOR =
UIManager.getColor("Table.alternateRowColor");
//
// default cell renderer
//
private Component prepareDefaultRenderer(TableCellRenderer renderer, int row, int column) {
final Component cell = super.prepareRenderer(renderer, row, column);
final MagicCardDefinition card = tableModel.getCardDef(row);
final boolean isRowSelected = isRowSelected(row);
final boolean isAlternate = row % 2 == 0;
((JComponent) cell).setOpaque(true);
((JComponent) cell).setBorder(NO_FOCUS_BORDER);
/*
Set text color.
*/
cell.setBackground(isRowSelected
? SELECT_BCOLOR
: isAlternate
? ALTERNATE_COLOR
// have to create a new color object because Nimbus returns
// a color of type DerivedColor, which behaves strange, not sure why.
: new Color(getBackground().getRed(), getBackground().getGreen(), getBackground().getBlue())
);
cell.setForeground(isRowSelected
? card.isInvalid()
? Color.LIGHT_GRAY
: getSelectionForeground()
: card.isInvalid()
? Color.GRAY
: defaultForeColor
);
return cell;
}
//
// Mana cost renderer
//
private static final DefaultTableCellRenderer defaultCostRenderer = new DefaultTableCellRenderer() {
private MagicManaCost getManaCost(MagicCardDefinition card, Object value) {
return card.hasCost() ? (MagicManaCost)value : null;
}
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int col) {
final MagicCardDefinition card = ((CardTableModel)table.getModel()).getCardDef(row);
final CostPanel panel = new CostPanel(getManaCost(card, value));
// match border and background formatting with default
final JComponent defaultRender = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
panel.setOpaque(false);
panel.setBorder(defaultRender.getBorder());
if (isSelected) {
panel.setForeground(table.getSelectionForeground());
panel.setBackground(Color.RED);
} else {
panel.setForeground(getForeground());
// We have to create a new color object because Nimbus returns
// a color of type DerivedColor, which behaves strange, not sure
// why.
// panel.setBackground(new Color(getBackground().getRed(), getBackground().getGreen(), getBackground().getBlue()));
}
panel.setBorder(noFocusBorder);
return panel;
}
};
/******************************************************************
* TRANSLUCENT THEME
******************************************************************/
//
// default cell renderer
//
private Component prepareThemedRenderer(TableCellRenderer renderer, int row, int column) {
final Component cell = super.prepareRenderer(renderer, row, column);
final MagicCardDefinition card = tableModel.getCardDef(row);
final boolean isRowSelected = isRowSelected(row);
/*
Set text color.
*/
cell.setForeground(card.isInvalid()
? Color.GRAY
: ColorHelper.getOppositeColor(defaultForeColor)
);
/*
default cell properties
*/
final int ordinal = convertColumnIndexToModel(column);
if (CardTableColumn.values()[ordinal] != CardTableColumn.Cost) {
if (cell instanceof JComponent) {
final JComponent jc = (JComponent) cell;
jc.setBorder(isRowSelected ? SELECTED_BORDER : NO_FOCUS_BORDER);
jc.setOpaque(false);
}
}
/*
Highlight row on mouse over.
*/
if (row == mouseOverRow) {
cell.setForeground(MagicStyle.getRolloverColor());
}
return cell;
}
//
// Mana cost renderer
//
private static final DefaultTableCellRenderer themedCostRenderer = new DefaultTableCellRenderer() {
private final Border DEFAULT_BORDER = new EmptyBorder(1, 1, 1, 1);
private final Border SELECTED_COST_BORDER =
BorderFactory.createCompoundBorder(
SELECTED_BORDER,
BorderFactory.createEmptyBorder(0, 0, 0, 1)
);
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int col) {
final MagicCardDefinition card =
((CardTableModel)table.getModel()).getCardDef(row);
final CostPanel panel = new CostPanel(card.hasCost()
? (MagicManaCost) value
: null
);
panel.setOpaque(false);
panel.setBorder(isSelected ? SELECTED_COST_BORDER : DEFAULT_BORDER);
return panel;
}
};
@Override
public void mouseClicked(MouseEvent e) {
// NA
}
@Override
public void mousePressed(MouseEvent e) {
// NA
}
@Override
public void mouseReleased(MouseEvent e) {
// NA
}
@Override
public void mouseEntered(MouseEvent e) {
// NA
}
@Override
public void mouseExited(MouseEvent e) {
mouseOverRow = -1;
repaint();
}
@Override
public void mouseDragged(MouseEvent e) {
// NA
}
@Override
public void mouseMoved(MouseEvent e) {
final int row = rowAtPoint(e.getPoint());
if (row != mouseOverRow) {
mouseOverRow = row;
repaint();
}
}
void setStyle() {
setColumnRenderers();
setStyleProperties();
repaint();
}
}