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 }