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 }