001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it
010     * under the terms of the GNU Lesser General Public License as published by
011     * the Free Software Foundation; either version 2.1 of the License, or
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022     * USA.
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * -----------------------
028     * SortButtonRenderer.java
029     * -----------------------
030     * (C) Copyright 2000-2004, by Nobuo Tamemasa and Contributors.
031     *
032     * Original Author:  Nobuo Tamemasa;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *                   Gareth Davis;
035     *
036     * $Id: SortButtonRenderer.java,v 1.7 2008/09/10 09:26:11 mungady Exp $
037     *
038     * Changes (from 26-Oct-2001)
039     * --------------------------
040     * 26-Oct-2001 : Changed package to com.jrefinery.ui.* (DG);
041     * 26-Jun-2002 : Removed unnecessary import (DG);
042     * 14-Oct-2002 : Fixed errors reported by Checkstyle (DG);
043     *
044     */
045    
046    package org.jfree.ui;
047    
048    import java.awt.Component;
049    import java.awt.Insets;
050    import javax.swing.JButton;
051    import javax.swing.JComponent;
052    import javax.swing.JLabel;
053    import javax.swing.JTable;
054    import javax.swing.SwingConstants;
055    import javax.swing.UIManager;
056    import javax.swing.border.Border;
057    import javax.swing.table.JTableHeader;
058    import javax.swing.table.TableCellRenderer;
059    
060    /**
061     * A table cell renderer for table headings - uses one of three JButton instances to indicate the
062     * sort order for the table column.
063     * <P>
064     * This class (and also BevelArrowIcon) is adapted from original code by Nobuo Tamemasa (version
065     * 1.0, 26-Feb-1999) posted on www.codeguru.com.
066     *
067     * @author David Gilbert
068     */
069    public class SortButtonRenderer implements TableCellRenderer {
070    
071        /**
072         * Useful constant indicating NO sorting.
073         */
074        public static final int NONE = 0;
075    
076        /**
077         * Useful constant indicating ASCENDING (that is, arrow pointing down) sorting in the table.
078         */
079        public static final int DOWN = 1;
080    
081        /**
082         * Useful constant indicating DESCENDING (that is, arrow pointing up) sorting in the table.
083         */
084        public static final int UP = 2;
085    
086        /**
087         * The current pressed column (-1 for no column).
088         */
089        private int pressedColumn = -1;
090    
091        /**
092         * The three buttons that are used to render the table header cells.
093         */
094        private JButton normalButton;
095    
096        /**
097         * The three buttons that are used to render the table header cells.
098         */
099        private JButton ascendingButton;
100    
101        /**
102         * The three buttons that are used to render the table header cells.
103         */
104        private JButton descendingButton;
105    
106        /**
107         * Used to allow the class to work out whether to use the buttuns
108         * or labels. Labels are required when using the aqua look and feel cos the
109         * buttons won't fit.
110         */
111        private boolean useLabels;
112    
113        /**
114         * The normal label (only used with MacOSX).
115         */
116        private JLabel normalLabel;
117    
118        /**
119         * The ascending label (only used with MacOSX).
120         */
121        private JLabel ascendingLabel;
122    
123        /**
124         * The descending label (only used with MacOSX).
125         */
126        private JLabel descendingLabel;
127    
128        /**
129         * Creates a new button renderer.
130         */
131        public SortButtonRenderer() {
132    
133            this.pressedColumn = -1;
134            this.useLabels = UIManager.getLookAndFeel().getID().equals("Aqua");
135    
136            final Border border = UIManager.getBorder("TableHeader.cellBorder");
137    
138            if (this.useLabels) {
139                this.normalLabel = new JLabel();
140                this.normalLabel.setHorizontalAlignment(SwingConstants.LEADING);
141    
142                this.ascendingLabel = new JLabel();
143                this.ascendingLabel.setHorizontalAlignment(SwingConstants.LEADING);
144                this.ascendingLabel.setHorizontalTextPosition(SwingConstants.LEFT);
145                this.ascendingLabel.setIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
146    
147                this.descendingLabel = new JLabel();
148                this.descendingLabel.setHorizontalAlignment(SwingConstants.LEADING);
149                this.descendingLabel.setHorizontalTextPosition(SwingConstants.LEFT);
150                this.descendingLabel.setIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, false));
151    
152                this.normalLabel.setBorder(border);
153                this.ascendingLabel.setBorder(border);
154                this.descendingLabel.setBorder(border);
155            }
156            else {
157                this.normalButton = new JButton();
158                this.normalButton.setMargin(new Insets(0, 0, 0, 0));
159                this.normalButton.setHorizontalAlignment(SwingConstants.LEADING);
160    
161                this.ascendingButton = new JButton();
162                this.ascendingButton.setMargin(new Insets(0, 0, 0, 0));
163                this.ascendingButton.setHorizontalAlignment(SwingConstants.LEADING);
164                this.ascendingButton.setHorizontalTextPosition(SwingConstants.LEFT);
165                this.ascendingButton.setIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
166                this.ascendingButton.setPressedIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, true));
167    
168                this.descendingButton = new JButton();
169                this.descendingButton.setMargin(new Insets(0, 0, 0, 0));
170                this.descendingButton.setHorizontalAlignment(SwingConstants.LEADING);
171                this.descendingButton.setHorizontalTextPosition(SwingConstants.LEFT);
172                this.descendingButton.setIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, false));
173                this.descendingButton.setPressedIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, true));
174    
175                this.normalButton.setBorder(border);
176                this.ascendingButton.setBorder(border);
177                this.descendingButton.setBorder(border);
178    
179            }
180    
181        }
182    
183        /**
184         * Returns the renderer component.
185         *
186         * @param table      the table.
187         * @param value      the value.
188         * @param isSelected selected?
189         * @param hasFocus   focussed?
190         * @param row        the row.
191         * @param column     the column.
192         * @return the renderer.
193         */
194        public Component getTableCellRendererComponent(final JTable table,
195                                                       final Object value,
196                                                       final boolean isSelected,
197                                                       final boolean hasFocus,
198                                                       final int row, final int column) {
199    
200            if (table == null) {
201                throw new NullPointerException("Table must not be null.");
202            }
203    
204            final JComponent component;
205            final SortableTableModel model = (SortableTableModel) table.getModel();
206            final int cc = table.convertColumnIndexToModel(column);
207            final boolean isSorting = (model.getSortingColumn() == cc);
208            final boolean isAscending = model.isAscending();
209    
210            final JTableHeader header = table.getTableHeader();
211            final boolean isPressed = (cc == this.pressedColumn);
212    
213            if (this.useLabels) {
214                final JLabel label = getRendererLabel(isSorting, isAscending);
215                label.setText((value == null) ? "" : value.toString());
216                component = label;
217            }
218            else {
219                final JButton button = getRendererButton(isSorting, isAscending);
220                button.setText((value == null) ? "" : value.toString());
221                button.getModel().setPressed(isPressed);
222                button.getModel().setArmed(isPressed);
223                component = button;
224            }
225    
226            if (header != null) {
227                component.setForeground(header.getForeground());
228                component.setBackground(header.getBackground());
229                component.setFont(header.getFont());
230            }
231            return component;
232        }
233    
234        /**
235         * Returns the correct button component.
236         *
237         * @param isSorting whether the render component represents the sort column.
238         * @param isAscending whether the model is ascending.
239         * @return either the ascending, descending or normal button.
240         */
241        private JButton getRendererButton(final boolean isSorting, final boolean isAscending) {
242            if (isSorting) {
243                if (isAscending) {
244                    return this.ascendingButton;
245                }
246                else {
247                    return this.descendingButton;
248                }
249            }
250            else {
251                return this.normalButton;
252            }
253        }
254    
255        /**
256         * Returns the correct label component.
257         *
258         * @param isSorting whether the render component represents the sort column.
259         * @param isAscending whether the model is ascending.
260         * @return either the ascending, descending or normal label.
261         */
262        private JLabel getRendererLabel(final boolean isSorting, final boolean isAscending) {
263            if (isSorting) {
264                if (isAscending) {
265                    return this.ascendingLabel;
266                }
267                else {
268                    return this.descendingLabel;
269                }
270            }
271            else {
272                return this.normalLabel;
273            }
274        }
275    
276        /**
277         * Sets the pressed column.
278         *
279         * @param column the column.
280         */
281        public void setPressedColumn(final int column) {
282            this.pressedColumn = column;
283        }
284    
285    }