001    /*
002     * $Id: ColumnHeaderRenderer.java,v 1.11 2006/03/10 15:25:07 kleopatra Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx.table;
023    
024    import java.awt.Color;
025    import java.awt.Component;
026    import java.awt.Font;
027    
028    import javax.swing.Icon;
029    import javax.swing.JComponent;
030    import javax.swing.JLabel;
031    import javax.swing.JTable;
032    import javax.swing.UIManager;
033    import javax.swing.border.Border;
034    import javax.swing.border.CompoundBorder;
035    import javax.swing.plaf.UIResource;
036    import javax.swing.table.JTableHeader;
037    import javax.swing.table.TableCellRenderer;
038    
039    import org.jdesktop.swingx.JXTable;
040    import org.jdesktop.swingx.LabelProperties;
041    import org.jdesktop.swingx.border.IconBorder;
042    import org.jdesktop.swingx.decorator.SortOrder;
043    import org.jdesktop.swingx.icon.SortArrowIcon;
044    import org.jdesktop.swingx.plaf.ColumnHeaderRendererAddon;
045    import org.jdesktop.swingx.plaf.LookAndFeelAddons;
046    
047    /**
048     * Header renderer class which renders column sort feedback (arrows).
049     * 
050     * PENDING: #25, #169 - Header doesn't look right in winXP/mac
051     * 
052     * 
053     * @author Amy Fowler
054     * @author Ramesh Gupta
055     * @author Jeanette Winzenburg
056     */
057    public class ColumnHeaderRenderer extends JComponent implements TableCellRenderer {
058        // the inheritance is only to make sure we are updated on LF change
059        public static final String UP_ICON_KEY = "ColumnHeaderRenderer.upIcon";
060        public static final String DOWN_ICON_KEY = "ColumnHeaderRenderer.downIcon";
061    
062        static {
063            LookAndFeelAddons.contribute(new ColumnHeaderRendererAddon());
064        }
065        private static TableCellRenderer sharedInstance = null;
066    
067        private static Icon defaultDownIcon = new SortArrowIcon(false);
068    
069        private static Icon defaultUpIcon = new SortArrowIcon(true);
070    
071        private Icon downIcon = defaultDownIcon;
072    
073        private Icon upIcon = defaultUpIcon;
074    
075        private IconBorder iconBorder = new IconBorder();
076        private boolean antiAliasedText = false;
077    
078        private TableCellRenderer delegateRenderer;
079    
080        private LabelProperties label;
081    
082        public static TableCellRenderer getSharedInstance() {
083            if (sharedInstance == null) {
084                sharedInstance = new ColumnHeaderRenderer();
085            }
086            return sharedInstance;
087        }
088    
089        public static ColumnHeaderRenderer createColumnHeaderRenderer() {
090            return new ColumnHeaderRenderer();
091        }
092    
093        /*
094         * JW: a story ...
095         *
096         * latest: don't use a custom component and don't add the original
097         * and the arrow - use the original only and compound a border with 
098         * arrow icon. How does it look in XP/Mac?
099         * 
100         * 
101         * ----------------- below is the comment as of ColumnHeaderRenderer
102         * Original used a Label to show the typical text/icon part and another
103         * Label to show the up/down arrows, added both to this and configured both
104         * directly in getTableCellRendererComponent.
105         * 
106         * My first shot to solve the issues was to delegate the text/icon part to
107         * the defaultRenderer as returned by the JTableHeader: replace the first
108         * label with the rendererComponent of the renderer. In
109         * getTableCellRendererComponent let the renderer configure the comp and
110         * "move" the border from the delegateComp to this - so it's bordering both
111         * the comp and the arrow.
112         * 
113         * Besides not working (WinXP style headers are still not shown :-( it has
114         * issues with opaqueness: different combinations of this.opaque and
115         * delegate.opaque all have issues 
116         *  1. if the delegate is not explicitly set to false the border looks wrong 
117         *  2. if this is set to true we can have custom background 
118         *     per cell but no setting the header background has no
119         *     effect - and changing LF doesn't take up the LF default background ...
120         *  3. if this is set to false we can't have custom cell background
121         * 
122         * Any ideas?
123         * 
124         * 
125         */
126    
127        private ColumnHeaderRenderer() {
128            label = new LabelProperties();
129            initDelegate();
130            
131        }
132    
133    
134        private void initDelegate() {
135            JTableHeader header = new JTableHeader();
136            delegateRenderer = header.getDefaultRenderer();
137    
138        }
139    
140        public Component getTableCellRendererComponent(JTable table, Object value,
141                boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) {
142            Component comp = configureDelegate(table, value, isSelected, hasFocus, rowIndex,
143                    columnIndex);
144    
145            if ((table instanceof JXTable) && (comp instanceof JComponent)) {
146                
147                SortOrder sortOrder = ((JXTable) table).getSortOrder(columnIndex);
148    
149                Border border = UIManager.getBorder("TableHeader.cellBorder");
150                if (sortOrder.isSorted()) {
151                    iconBorder.setIcon(sortOrder.isAscending() ? upIcon : downIcon);
152                    Border origBorder = ((JComponent) comp).getBorder();
153                    border = new CompoundBorder(origBorder, iconBorder);
154                    ((JComponent) comp).setBorder(border);
155                }
156            }
157            adjustComponentOrientation(comp);
158            return comp;
159        }
160    
161        /**
162         * adjusts the Component's orientation to JXTable's CO if appropriate.
163         * Here: always.
164         * 
165         * @param stamp
166         */
167        protected void adjustComponentOrientation(Component stamp) {
168            if (stamp.getComponentOrientation().equals(getComponentOrientation())) return;
169            stamp.applyComponentOrientation(getComponentOrientation());
170        }
171    
172        private Component configureDelegate(JTable table, Object value,
173                boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) {
174            Component comp = delegateRenderer.getTableCellRendererComponent(table,
175                    value, isSelected, hasFocus, rowIndex, columnIndex);
176    
177            applyLabelProperties(comp);
178            return comp;
179        }
180    
181        private void applyLabelProperties(Component delegateRendererComponent) {
182            if (delegateRendererComponent instanceof JLabel) {
183                label.applyPropertiesTo((JLabel) delegateRendererComponent);
184            } else {
185                label.applyPropertiesTo(delegateRenderer);
186            }
187        }
188    
189        public void setAntiAliasedText(boolean antiAlias) {
190            this.antiAliasedText = antiAlias;
191        }
192    
193        public boolean getAntiAliasedText() {
194            return antiAliasedText;
195        }
196    
197        public void setBackground(Color background) {
198            // this is called somewhere along initialization of super?
199            if (label != null) {
200                label.setBackground(background);
201            }
202        }
203    
204        public void setForeground(Color foreground) {
205             if (label != null) {
206                label.setForeground(foreground);
207            }
208        }
209    
210        public void setFont(Font font) {
211            if (label != null) {
212                label.setFont(font);
213            }
214        }
215    
216        public void setDownIcon(Icon icon) {
217            this.downIcon = icon;
218        }
219    
220        public Icon getDownIcon() {
221            return downIcon;
222        }
223    
224        public void setUpIcon(Icon icon) {
225            this.upIcon = icon;
226        }
227    
228        public Icon getUpIcon() {
229            return upIcon;
230        }
231    
232        public void setHorizontalAlignment(int alignment) {
233            label.setHorizontalAlignment(alignment);
234        }
235    
236        public int getHorizontalAlignment() {
237            return label.getHorizontalAlignment();
238        }
239    
240        public void setHorizontalTextPosition(int textPosition) {
241            label.setHorizontalTextPosition(textPosition);
242        }
243    
244        public int getHorizontalTextPosition() {
245            return label.getHorizontalTextPosition();
246        }
247    
248        public void setIcon(Icon icon) {
249            label.setIcon(icon);
250        }
251    
252        public Icon getIcon() {
253            return label.getIcon();
254        }
255    
256        public void setIconTextGap(int iconTextGap) {
257            label.setIconTextGap(iconTextGap);
258        }
259    
260        public int getIconTextGap() {
261            return label.getIconTextGap();
262        }
263    
264        public void setVerticalAlignment(int alignment) {
265            label.setVerticalAlignment(alignment);
266        }
267    
268        public int getVerticalAlignment() {
269            return label.getVerticalAlignment();
270        }
271    
272        public void setVerticalTextPosition(int textPosition) {
273            label.setVerticalTextPosition(textPosition);
274        }
275    
276        public int getVerticalTextPosition() {
277            return label.getVerticalTextPosition();
278        }
279    
280        public void updateUI() {
281            super.updateUI();
282            initDelegate();
283            updateIconUI();
284        }
285    
286        private void updateIconUI() {
287            if (getUpIcon() instanceof UIResource) {
288                Icon icon = UIManager.getIcon(UP_ICON_KEY);
289                setUpIcon(icon != null ? icon : defaultUpIcon);
290                
291            }
292            if (getDownIcon() instanceof UIResource) {
293                Icon icon = UIManager.getIcon(DOWN_ICON_KEY);
294                setDownIcon(icon != null ? icon : defaultDownIcon);
295                
296            }
297        }
298    }