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
046package org.jfree.ui;
047
048import java.awt.Component;
049import java.awt.Insets;
050import javax.swing.JButton;
051import javax.swing.JComponent;
052import javax.swing.JLabel;
053import javax.swing.JTable;
054import javax.swing.SwingConstants;
055import javax.swing.UIManager;
056import javax.swing.border.Border;
057import javax.swing.table.JTableHeader;
058import 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 */
069public 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}