    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.5 2005/10/18 13:18:34 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 ascendingButton;
    245                }
    246                else {
    247                    return descendingButton;
    248                }
    249            }
    250            else {
    251                return 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 ascendingLabel;
    266                }
    267                else {
    268                    return descendingLabel;
    269                }
    270            }
    271            else {
    272                return 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    }