    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}