package com.explodingpixels.macwidgets.plaf; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import javax.swing.BorderFactory; import javax.swing.CellRendererPane; import javax.swing.DefaultCellEditor; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.plaf.basic.BasicTableUI; import javax.swing.table.TableCellEditor; import com.explodingpixels.data.Rating; import com.explodingpixels.macwidgets.ITunesRatingTableCellRenderer; import com.explodingpixels.macwidgets.ITunesTableHeaderRenderer; import com.explodingpixels.macwidgets.MacFontUtils; import com.explodingpixels.widgets.TableHeaderUtils; import com.explodingpixels.widgets.TableUtils; import com.explodingpixels.widgets.WindowUtils; /** * A UI delegate that renders an iTunes style table. <br/> * <img src="../../../../../graphics/iTunesTable.png"> * <p/> * Sorting indicators will be rendered if a * {@link com.explodingpixels.widgets.TableUtils.SortDelegate} is installed on * the associated {@link javax.swing.JTable} via the * {@link com.explodingpixels.widgets.TableUtils#makeSortable(javax.swing.JTable, com.explodingpixels.widgets.TableUtils.SortDelegate)} * method. */ public class ITunesTableUI extends BasicTableUI { protected static final Color EVEN_ROW_COLOR = new Color(241, 245, 250); protected static final Color TABLE_GRID_COLOR = new Color(0xd9d9d9); protected static final Color SELECTION_ACTIVE_SELECTION_FOREGROUND_COLOR = Color.WHITE; protected static final Color SELECTION_ACTIVE_SELECTION_BACKGROUND_COLOR = new Color( 0x3d80df); protected static final Color SELECTION_INACTIVE_SELECTION_FOREGROUND_COLOR = Color.BLACK; protected static final Color SELECTION_INACTIVE_SELECTION_BACKGROUND_COLOR = new Color( 0xc0c0c0); protected static final Color SELECTION_ACTIVE_BOTTOM_BORDER_COLOR = new Color( 125, 170, 234); protected static final Color SELECTION_INACTIVE_BOTTOM_BORDER_COLOR = new Color( 224, 224, 224); protected static final Color TRANSPARENT_COLOR = new Color(0, 0, 0, 0); @Override public void installUI(JComponent c) { super.installUI(c); table.remove(rendererPane); rendererPane = createCustomCellRendererPane(); table.add(rendererPane); // TODO save defaults. table.setOpaque(false); table.setFont(MacFontUtils.ITUNES_FONT); table.setGridColor(TABLE_GRID_COLOR); table.setIntercellSpacing(new Dimension(0, 0)); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); table.getTableHeader().setDefaultRenderer( new ITunesTableHeaderRenderer(table)); table.setShowHorizontalLines(false); TableHeaderUtils.makeHeaderFillEmptySpace(table); TableUtils.makeStriped(table, EVEN_ROW_COLOR); table.setDefaultRenderer(Rating.class, new ITunesRatingTableCellRenderer()); table.setDefaultEditor(Object.class, createDefaultTableCellEditor()); makeTableActive(); WindowUtils.installWeakWindowFocusListener(table, createWindowFocusListener()); } @Override protected void installListeners() { super.installListeners(); } private void makeTableActive() { table.setSelectionForeground(SELECTION_ACTIVE_SELECTION_FOREGROUND_COLOR); table.setSelectionBackground(SELECTION_ACTIVE_SELECTION_BACKGROUND_COLOR); } private void makeTableInactive() { table.setSelectionForeground(SELECTION_INACTIVE_SELECTION_FOREGROUND_COLOR); table.setSelectionBackground(SELECTION_INACTIVE_SELECTION_BACKGROUND_COLOR); } private TableCellEditor createDefaultTableCellEditor() { JTextField textField = new JTextField(); textField.setFont(MacFontUtils.ITUNES_FONT); textField.setBorder(BorderFactory.createLineBorder(Color.BLACK)); return new DefaultCellEditor(textField); } private WindowFocusListener createWindowFocusListener() { return new WindowFocusListener() { public void windowGainedFocus(WindowEvent e) { makeTableActive(); } public void windowLostFocus(WindowEvent e) { makeTableInactive(); } }; } public Border getRowBorder() { return BorderFactory.createEmptyBorder(0, 5, 0, 5); } public Border getSelectedRowBorder() { return BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(0, 0, 1, 0, getSelectedRowBottomHighlight()), BorderFactory .createEmptyBorder(1, 5, 0, 5)); } private Color getSelectedRowBottomHighlight() { return WindowUtils.isParentWindowFocused(table) ? SELECTION_ACTIVE_BOTTOM_BORDER_COLOR : SELECTION_INACTIVE_BOTTOM_BORDER_COLOR; } /** * Creates a custom {@link javax.swing.CellRendererPane} that sets the renderer * component to be non-opqaque if the associated row isn't selected. This * custom {@code CellRendererPane} is needed because a table UI delegate has * no prepare renderer like {@link javax.swing.JTable} has. */ protected CellRendererPane createCustomCellRendererPane() { return new CellRendererPane() { @Override public void paintComponent(Graphics graphics, Component component, Container container, int x, int y, int w, int h, boolean shouldValidate) { int rowAtPoint = table.rowAtPoint(new Point(x, y)); boolean isSelected = table.isRowSelected(rowAtPoint); if (component instanceof JComponent) { JComponent jComponent = (JComponent) component; jComponent.setOpaque(isSelected); jComponent.setBorder(isSelected ? getSelectedRowBorder() : getRowBorder()); jComponent.setBackground(isSelected ? table .getSelectionBackground() : TRANSPARENT_COLOR); } super.paintComponent(graphics, component, container, x, y, w, h, shouldValidate); } }; } }