001 /* 002 * $Id: BetterJTable.java,v 1.3 2012/02/19 17:35:50 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 package edu.wisc.ssec.mcidasv.ui; 031 032 import java.awt.Color; 033 import java.awt.Component; 034 import java.awt.Dimension; 035 import java.awt.Graphics; 036 import java.beans.PropertyChangeEvent; 037 import java.beans.PropertyChangeListener; 038 039 import javax.swing.BorderFactory; 040 import javax.swing.CellRendererPane; 041 import javax.swing.JComponent; 042 import javax.swing.JScrollPane; 043 import javax.swing.JTable; 044 import javax.swing.JViewport; 045 import javax.swing.table.JTableHeader; 046 import javax.swing.table.TableCellRenderer; 047 import javax.swing.table.TableColumn; 048 import javax.swing.table.TableModel; 049 050 public class BetterJTable extends JTable { 051 052 private static final Color EVEN_ROW_COLOR = new Color(241, 245, 250); 053 private static final Color TABLE_GRID_COLOR = new Color(0xd9d9d9); 054 055 private static final CellRendererPane CELL_RENDER_PANE = new CellRendererPane(); 056 057 public BetterJTable() { 058 super(); 059 init(); 060 } 061 public BetterJTable(TableModel dm) { 062 super(dm); 063 init(); 064 } 065 066 private void init() { 067 // setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 068 setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 069 setTableHeader(createTableHeader()); 070 getTableHeader().setReorderingAllowed(false); 071 setOpaque(false); 072 setGridColor(TABLE_GRID_COLOR); 073 // turn off grid painting as we'll handle this manually in order to paint 074 // grid lines over the entire viewport. 075 setShowGrid(false); 076 } 077 078 /** 079 * Creates a JTableHeader that paints the table header background to the right 080 * of the right-most column if neccesasry. 081 */ 082 private JTableHeader createTableHeader() { 083 return new JTableHeader(getColumnModel()) { 084 @Override protected void paintComponent(Graphics g) { 085 super.paintComponent(g); 086 // if this JTableHeader is parented in a JViewport, then paint the 087 // table header background to the right of the last column if 088 // neccessary. 089 JViewport viewport = (JViewport) table.getParent(); 090 if (viewport != null && table.getWidth() < viewport.getWidth()) { 091 int x = table.getWidth(); 092 int width = viewport.getWidth() - table.getWidth(); 093 paintHeader(g, getTable(), x, width); 094 } 095 } 096 }; 097 } 098 099 /** 100 * Paints the given JTable's table default header background at given 101 * x for the given width. 102 */ 103 private static void paintHeader(Graphics g, JTable table, int x, int width) { 104 TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer(); 105 Component component = renderer.getTableCellRendererComponent( 106 table, "", false, false, -1, 2); 107 108 component.setBounds(0, 0, width, table.getTableHeader().getHeight()); 109 110 ((JComponent)component).setOpaque(false); 111 CELL_RENDER_PANE.paintComponent(g, component, null, x, 0, 112 width, table.getTableHeader().getHeight(), true); 113 } 114 115 @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { 116 Component component = super.prepareRenderer(renderer, row, column); 117 // if the rendere is a JComponent and the given row isn't part of a 118 // selection, make the renderer non-opaque so that striped rows show 119 // through. 120 if (component instanceof JComponent) { 121 ((JComponent)component).setOpaque(getSelectionModel().isSelectedIndex(row)); 122 } 123 return component; 124 } 125 126 // Stripe painting Viewport. ////////////////////////////////////////////// 127 128 /** 129 * Creates a JViewport that draws a striped backgroud corresponding to the 130 * row positions of the given JTable. 131 */ 132 public static class StripedViewport extends JViewport { 133 134 private final JTable fTable; 135 136 public StripedViewport(JTable table) { 137 fTable = table; 138 setOpaque(false); 139 initListeners(); 140 } 141 142 private void initListeners() { 143 // install a listener to cause the whole table to repaint when 144 // a column is resized. we do this because the extended grid 145 // lines may need to be repainted. this could be cleaned up, 146 // but for now, it works fine. 147 PropertyChangeListener listener = createTableColumnWidthListener(); 148 for (int i = 0; i < fTable.getColumnModel().getColumnCount(); i++) { 149 fTable.getColumnModel().getColumn(i).addPropertyChangeListener(listener); 150 } 151 } 152 153 private PropertyChangeListener createTableColumnWidthListener() { 154 return new PropertyChangeListener() { 155 public void propertyChange(PropertyChangeEvent evt) { 156 repaint(); 157 } 158 }; 159 } 160 161 @Override protected void paintComponent(Graphics g) { 162 paintStripedBackground(g); 163 paintVerticalGridLines(g); 164 super.paintComponent(g); 165 } 166 167 private void paintStripedBackground(Graphics g) { 168 // get the row index at the top of the clip bounds (the first row 169 // to paint). 170 int rowAtPoint = fTable.rowAtPoint(g.getClipBounds().getLocation()); 171 // get the y coordinate of the first row to paint. if there are no 172 // rows in the table, start painting at the top of the supplied 173 // clipping bounds. 174 int topY = rowAtPoint < 0 175 ? g.getClipBounds().y : fTable.getCellRect(rowAtPoint,0,true).y; 176 177 // create a counter variable to hold the current row. if there are no 178 // rows in the table, start the counter at 0. 179 int currentRow = rowAtPoint < 0 ? 0 : rowAtPoint; 180 while (topY < g.getClipBounds().y + g.getClipBounds().height) { 181 int bottomY = topY + fTable.getRowHeight(); 182 g.setColor(getRowColor(currentRow)); 183 g.fillRect(g.getClipBounds().x, topY, g.getClipBounds().width, bottomY); 184 topY = bottomY; 185 currentRow ++; 186 } 187 } 188 189 private Color getRowColor(int row) { 190 return row % 2 == 0 ? EVEN_ROW_COLOR : getBackground(); 191 } 192 193 private void paintVerticalGridLines(Graphics g) { 194 // paint the column grid dividers for the non-existent rows. 195 int x = 0; 196 for (int i = 0; i < fTable.getColumnCount(); i++) { 197 TableColumn column = fTable.getColumnModel().getColumn(i); 198 // increase the x position by the width of the current column. 199 x += column.getWidth(); 200 g.setColor(TABLE_GRID_COLOR); 201 // draw the grid line (not sure what the -1 is for, but BasicTableUI 202 // also does it. 203 g.drawLine(x - 1, g.getClipBounds().y, x - 1, getHeight()); 204 } 205 } 206 } 207 208 public static JScrollPane createStripedJScrollPane(JTable table) { 209 JScrollPane scrollPane = new JScrollPane(table); 210 scrollPane.setViewport(new StripedViewport(table)); 211 scrollPane.getViewport().setView(table); 212 scrollPane.setBorder(BorderFactory.createEmptyBorder()); 213 // scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, 214 // createCornerComponent(table)); 215 return scrollPane; 216 } 217 218 /** 219 * Creates a component that paints the header background for use in a 220 * JScrollPane corner. 221 */ 222 private static JComponent createCornerComponent(final JTable table) { 223 return new JComponent() { 224 @Override 225 protected void paintComponent(Graphics g) { 226 paintHeader(g, table, 0, getWidth()); 227 } 228 }; 229 } 230 }