001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 */ 028package edu.wisc.ssec.mcidasv.ui; 029 030import java.awt.Color; 031import java.awt.Component; 032import java.awt.Graphics; 033 034import java.beans.PropertyChangeEvent; 035import java.beans.PropertyChangeListener; 036 037import javax.swing.BorderFactory; 038import javax.swing.CellRendererPane; 039import javax.swing.JComponent; 040import javax.swing.JScrollPane; 041import javax.swing.JTable; 042import javax.swing.JViewport; 043import javax.swing.event.ChangeEvent; 044import javax.swing.event.ListSelectionEvent; 045import javax.swing.event.TableColumnModelEvent; 046import javax.swing.event.TableColumnModelListener; 047import javax.swing.table.JTableHeader; 048import javax.swing.table.TableCellRenderer; 049import javax.swing.table.TableColumn; 050import javax.swing.table.TableModel; 051 052public class BetterJTable extends JTable { 053 054 private static final Color EVEN_ROW_COLOR = new Color(241, 245, 250); 055 private static final Color TABLE_GRID_COLOR = new Color(0xd9d9d9); 056 057 private static final CellRendererPane CELL_RENDER_PANE = new CellRendererPane(); 058 059 public BetterJTable() { 060 super(); 061 init(); 062 } 063 public BetterJTable(TableModel dm) { 064 super(dm); 065 init(); 066 } 067 068 private void init() { 069// setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 070 setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 071 setTableHeader(createTableHeader()); 072 getTableHeader().setReorderingAllowed(false); 073 setOpaque(false); 074 setGridColor(TABLE_GRID_COLOR); 075 // turn off grid painting as we'll handle this manually in order to paint 076 // grid lines over the entire viewport. 077 setShowGrid(false); 078 } 079 080 /** 081 * Creates a JTableHeader that paints the table header background to the right 082 * of the right-most column if neccesasry. 083 */ 084 private JTableHeader createTableHeader() { 085 return new JTableHeader(getColumnModel()) { 086 @Override protected void paintComponent(Graphics g) { 087 super.paintComponent(g); 088 // if this JTableHeader is parented in a JViewport, then paint the 089 // table header background to the right of the last column if 090 // neccessary. 091 JViewport viewport = (JViewport) table.getParent(); 092 if (viewport != null && table.getWidth() < viewport.getWidth()) { 093 int x = table.getWidth(); 094 int width = viewport.getWidth() - table.getWidth(); 095 paintHeader(g, getTable(), x, width); 096 } 097 } 098 }; 099 } 100 101 /** 102 * Paints the given JTable's table default header background at given 103 * x for the given width. 104 */ 105 private static void paintHeader(Graphics g, JTable table, int x, int width) { 106 TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer(); 107 Component component = renderer.getTableCellRendererComponent( 108 table, "", false, false, -1, 2); 109 110 component.setBounds(0, 0, width, table.getTableHeader().getHeight()); 111 112 ((JComponent)component).setOpaque(false); 113 CELL_RENDER_PANE.paintComponent(g, component, null, x, 0, 114 width, table.getTableHeader().getHeight(), true); 115 } 116 117 @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { 118 Component component = super.prepareRenderer(renderer, row, column); 119 // if the rendere is a JComponent and the given row isn't part of a 120 // selection, make the renderer non-opaque so that striped rows show 121 // through. 122 if (component instanceof JComponent) { 123 ((JComponent)component).setOpaque(getSelectionModel().isSelectedIndex(row)); 124 } 125 return component; 126 } 127 128 // Stripe painting Viewport. ////////////////////////////////////////////// 129 130 /** 131 * Creates a JViewport that draws a striped backgroud corresponding to the 132 * row positions of the given JTable. 133 */ 134 public static class StripedViewport extends JViewport { 135 136 private final JTable fTable; 137 138 public StripedViewport(JTable table) { 139 fTable = table; 140 setOpaque(false); 141 initListeners(); 142 } 143 144 private void initListeners() { 145 // install a listener to cause the whole table to repaint when 146 // a column is resized. we do this because the extended grid 147 // lines may need to be repainted. this could be cleaned up, 148 // but for now, it works fine. 149 fTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() { 150 @Override public void columnMarginChanged(ChangeEvent e) { 151 repaint(); 152 } 153 154 @Override public void columnAdded(TableColumnModelEvent e) {} 155 156 @Override public void columnRemoved(TableColumnModelEvent e) {} 157 158 @Override public void columnMoved(TableColumnModelEvent e) {} 159 160 @Override public void columnSelectionChanged(ListSelectionEvent e) {} 161 }); 162 PropertyChangeListener listener = createTableColumnWidthListener(); 163 for (int i = 0; i < fTable.getColumnModel().getColumnCount(); i++) { 164 fTable.getColumnModel().getColumn(i).addPropertyChangeListener(listener); 165 } 166 } 167 168 private PropertyChangeListener createTableColumnWidthListener() { 169 return new PropertyChangeListener() { 170 public void propertyChange(PropertyChangeEvent evt) { 171 repaint(); 172 } 173 }; 174 } 175 176 @Override protected void paintComponent(Graphics g) { 177 paintStripedBackground(g); 178 paintVerticalGridLines(g); 179 super.paintComponent(g); 180 } 181 182 private void paintStripedBackground(Graphics g) { 183 // get the row index at the top of the clip bounds (the first row 184 // to paint). 185 int rowAtPoint = fTable.rowAtPoint(g.getClipBounds().getLocation()); 186 // get the y coordinate of the first row to paint. if there are no 187 // rows in the table, start painting at the top of the supplied 188 // clipping bounds. 189 int topY = rowAtPoint < 0 190 ? g.getClipBounds().y : fTable.getCellRect(rowAtPoint,0,true).y; 191 192 // create a counter variable to hold the current row. if there are no 193 // rows in the table, start the counter at 0. 194 int currentRow = rowAtPoint < 0 ? 0 : rowAtPoint; 195 while (topY < g.getClipBounds().y + g.getClipBounds().height) { 196 int bottomY = topY + fTable.getRowHeight(); 197 g.setColor(getRowColor(currentRow)); 198 g.fillRect(g.getClipBounds().x, topY, g.getClipBounds().width, bottomY); 199 topY = bottomY; 200 currentRow++; 201 } 202 } 203 204 private Color getRowColor(int row) { 205 return row % 2 == 0 ? EVEN_ROW_COLOR : getBackground(); 206 } 207 208 private void paintVerticalGridLines(Graphics g) { 209 // paint the column grid dividers for the non-existent rows. 210 int x = 0; 211 for (int i = 0; i < fTable.getColumnCount(); i++) { 212 TableColumn column = fTable.getColumnModel().getColumn(i); 213 // increase the x position by the width of the current column. 214 x += column.getWidth(); 215 g.setColor(TABLE_GRID_COLOR); 216 // draw the grid line (not sure what the -1 is for, but BasicTableUI 217 // also does it. 218 g.drawLine(x - 1, g.getClipBounds().y, x - 1, getHeight()); 219 } 220 } 221 } 222 223 public static JScrollPane createStripedJScrollPane(JTable table) { 224 JScrollPane scrollPane = new JScrollPane(table); 225 scrollPane.setViewport(new StripedViewport(table)); 226 scrollPane.getViewport().setView(table); 227 scrollPane.setBorder(BorderFactory.createEmptyBorder()); 228// scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, 229// createCornerComponent(table)); 230 return scrollPane; 231 } 232 233 /** 234 * Creates a component that paints the header background for use in a 235 * JScrollPane corner. 236 */ 237 private static JComponent createCornerComponent(final JTable table) { 238 return new JComponent() { 239 @Override 240 protected void paintComponent(Graphics g) { 241 paintHeader(g, table, 0, getWidth()); 242 } 243 }; 244 } 245}