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    }