001    /////////////////////////////////////////////////
002    //This file is part of Sears project.
003    //Subtitle Editor And Re-Synch
004    //A tool to easily modify and resynch movies subtitles.
005    /////////////////////////////////////////////////
006    //This program is free software; 
007    //you can redistribute it and/or modify it under the terms 
008    //of the GNU General Public License 
009    //as published by the Free Software Foundation; 
010    //either version 2 of the License, or (at your option) any later version.
011    /////////////////////////////////////////////////
012    //Sears project is available under sourceforge
013    //at adress: http://sourceforge.net/projects/sears/
014    //Copyright (C) 2005 Booba Skaya
015    //Mail: booba.skaya@gmail.com
016    /////////////////////////////////////////////////
017    
018    //some suggestions about this class: floriaen@gmail.com
019    
020    package sears.search.gui;
021    
022    import java.awt.Component;
023    import java.awt.Rectangle;
024    
025    import javax.swing.JScrollPane;
026    import javax.swing.JTable;
027    import javax.swing.SwingUtilities;
028    
029    /**
030     * Gives method to manipulate table view
031     * [...]
032     * <p>
033     * Scroll to visible a table cell with a better style than
034     * the {@link JTable#scrollRectToVisible(Rectangle)} method
035     *</p>
036     */
037    public class JTableManipulation {
038            
039            private static final int SIZE_DECAY = 20;
040    
041            private JTable table;
042            private JScrollPane scrollPane;
043    
044            private int lastRow;
045            private int lastColumn;
046    
047            /**
048             * Constructs a new <code>JTableManipulation</code> object
049             * @param scrollPane
050             * @throws NullPointerException
051             * @throws IllegalArgumentException
052             */
053            public JTableManipulation(JScrollPane scrollPane) {
054                    if( scrollPane == null ) {
055                            throw new NullPointerException("JScrollPane object cannot be null");
056                    }
057                    // try to get back view
058                    Component view = scrollPane.getViewport().getView();
059                    if( view != null && view instanceof JTable ) {
060                            table = (JTable) view;
061                    } else {
062                            throw new IllegalArgumentException("the JScrollPane object is not associated to a JTable object or this object is null");
063                    }
064                    this.scrollPane = scrollPane;
065                    resetColumn();
066            }
067    
068            /**
069             * Gets the previous row of <tt>row</tt>
070             * <br>Ensure loop coherence
071             * @param row   the row
072             * @return              the previous row
073             */
074            private int getPreviousRow(int row) {
075                    if( --row < 0 ) {
076                            row = table.getRowCount();
077                    }
078                    return row;
079            }
080            
081            /**
082             * Gets the next row of <tt>row</tt>
083             * <br>Ensure loop coherence
084             * @param row   the row
085             * @return              the next row
086             */
087            private int getNextRow(int row) {
088                    if( ++row > table.getRowCount() ) {
089                            row = 0;
090                    }
091                    return row;
092            }
093            
094            /**
095             * Reset <tt>lastColumn</tt>
096             */
097            public void resetColumn() {
098                    lastColumn = -1;
099            }
100            
101            /**
102             * Scroll to visible a table cell with a better style than
103             * the {@link JTable#scrollRectToVisible(Rectangle)} method.
104             * <br><tt>column</tt> must be coherent and must be the same 
105             * that the previous given in parameters of this method.
106             * <br>call {@link #resetColumn()} before ...
107             * @param row           the row to display in view
108             * @param column        the column
109             * @param backward      the direction 
110             * @throws IllegalArgumentException if <tt>column</tt> is not the same than the last used <tt>column</tt>
111             */
112            public void scrollCellToVisible(int row, int column, boolean backward) {
113                    if( lastColumn >= 0 && column != lastColumn ) {
114                            throw new IllegalArgumentException("Given column " + column + " must be equal to " + lastColumn);
115                    }
116                    if( row >= 0 && column >= 0 ) {
117                            int rowCount = table.getRowCount();
118                            if( row < rowCount && column < table.getColumnCount() ) {
119                                    Rectangle cell = table.getCellRect(row, column, false);
120                                    Rectangle convertedCell = SwingUtilities.convertRectangle(scrollPane.getViewport().getView(), cell, scrollPane.getViewport());
121                                    Rectangle bounds = scrollPane.getBounds();
122                                    // if previous or next cell is not on view
123                                    if( !bounds.contains(convertedCell) ) {
124                                            int size = cell.height;
125                                            int viewSize = scrollPane.getViewport().getHeight();
126                                            // condition to loop
127                                            boolean loop = ( backward && row > lastRow ) || ( !backward && row < lastRow );
128                                            while( !loop && size < viewSize - SIZE_DECAY ) {
129                                                    if( backward && ( row-- < getPreviousRow(row) ) ) {
130                                                            loop = true;
131                                                    } 
132                                                    if( !backward && ( row++ > getNextRow(row) ) ) {
133                                                            loop = true;
134                                                    }
135                                                    cell = table.getCellRect(row, column, true);
136                                                    size += cell.height;
137                                            }
138                                            // display cell in view
139                                            table.scrollRectToVisible(cell);
140                                    }
141                            }                       
142                    }
143                    lastRow = row;
144                    lastColumn = column;
145            }
146    }