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