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 }