/* * $Id: ColumnHeaderRenderer.java,v 1.24 2009/02/01 15:01:06 rah003 Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.table; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.util.logging.Logger; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.plaf.UIResource; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.border.IconBorder; import org.jdesktop.swingx.decorator.SortOrder; import org.jdesktop.swingx.icon.SortArrowIcon; import org.jdesktop.swingx.plaf.ColumnHeaderRendererAddon; import org.jdesktop.swingx.plaf.LookAndFeelAddons; /** * Header renderer class which renders column sort feedback (arrows). * <p> * Additionally, it allows to customize renderer properties like <code>Font</code>, * <code>Alignment</code> and similar. This part needs to go somewhere else * when we switch to Mustang. * <p> * * Note: #169-jdnc, #193-swingx - Header doesn't look right in winXP/mac seem - to be * fixed, but could be brittle. Won't do more about it, Mustang will take care once * SwingLabs is switched over to 1.6. * * @status.target jdk15-glue * * @author Amy Fowler * @author Ramesh Gupta * @author Jeanette Winzenburg */ public class ColumnHeaderRenderer extends JComponent implements TableCellRenderer , UIResource { @SuppressWarnings("unused") private static final Logger LOG = Logger .getLogger(ColumnHeaderRenderer.class.getName()); // the inheritance is only to make sure we are updated on LF change public static final String UP_ICON_KEY = "ColumnHeaderRenderer.upIcon"; public static final String DOWN_ICON_KEY = "ColumnHeaderRenderer.downIcon"; public static final String VISTA_BORDER_HACK = "ColumnHeaderRenderer.vistaBorderHack"; public static final String METAL_BORDER_HACK = "ColumnHeaderRenderer.metalBorderHack"; static { LookAndFeelAddons.contribute(new ColumnHeaderRendererAddon()); } private static TableCellRenderer sharedInstance = null; private static Icon defaultDownIcon = new SortArrowIcon(false); private static Icon defaultUpIcon = new SortArrowIcon(true); private Icon downIcon = defaultDownIcon; private Icon upIcon = defaultUpIcon; private IconBorder iconBorder = new IconBorder(); private boolean antiAliasedText = false; private TableCellRenderer delegateRenderer; private LabelProperties label; /** * Returns the shared ColumnHeaderRenderer. <p> * @return the shared header renderer. * */ public static TableCellRenderer getSharedInstance() { if (sharedInstance == null) { sharedInstance = new ColumnHeaderRenderer(); } return sharedInstance; } /** * * @return a <code>ColumnHeaderRenderer</code> */ public static ColumnHeaderRenderer createColumnHeaderRenderer() { return new ColumnHeaderRenderer(); } /* * JW: a story ... ordered from latest to older. * * Yet another chapter: The problem with some LFs is that they * install a LF specific renderer in uidelegate.installUI, * unconditionally overwriting the renderer created and installed * by the tableHeader.createDefaultRenderer. Looks like the * ui install is needed to support rollover (maybe other?) "live" * effects. If so, there are some implications: * * - the default renderer must not be shared across tableHeaders. * - the ColumnHeaderRenderer's delegateRenderer must be updated * to the last ui-installed default * - the tableHeader must reset its default to the delegateRenderer * before calling super.updateUI (to give the uidelegate the * possibility to cleanup) * * ------------------------------------------- * latest: don't use a custom component and don't add the original * and the arrow - use the original only and compound a border with * arrow icon. How does it look in XP/Mac? * * * ----------------- below is the comment as of ColumnHeaderRenderer * Original used a Label to show the typical text/icon part and another * Label to show the up/down arrows, added both to this and configured both * directly in getTableCellRendererComponent. * * My first shot to solve the issues was to delegate the text/icon part to * the defaultRenderer as returned by the JTableHeader: replace the first * label with the rendererComponent of the renderer. In * getTableCellRendererComponent let the renderer configure the comp and * "move" the border from the delegateComp to this - so it's bordering both * the comp and the arrow. * * Besides not working (WinXP style headers are still not shown :-( it has * issues with opaqueness: different combinations of this.opaque and * delegate.opaque all have issues * 1. if the delegate is not explicitly set to false the border looks wrong * 2. if this is set to true we can have custom background * per cell but no setting the header background has no * effect - and changing LF doesn't take up the LF default background ... * 3. if this is set to false we can't have custom cell background * * * */ public ColumnHeaderRenderer() { label = new LabelProperties(); initDelegate(); updateIconUI(); } public ColumnHeaderRenderer(JTableHeader header) { label = new LabelProperties(); updateUI(header); } private void initDelegate() { // JW: hacking around core problems with standalone table header // so we create a core table and use its default header // PENDING: sure about not introducing memory issues? // JTable table = new JTable(); JTableHeader header = null; //table.getTableHeader(); if (header == null) { header = new JTableHeader(); } delegateRenderer = header.getDefaultRenderer(); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) { Component comp = configureDelegate(table, value, isSelected, hasFocus, rowIndex, columnIndex); if ((table instanceof JXTable) && (comp instanceof JComponent)) { // work-around core issues hackBorder((JComponent) comp); SortOrder sortOrder = ((JXTable) table).getSortOrder(columnIndex); // Border border = UIManager.getBorder("TableHeader.cellBorder"); // LOG.info("in renderer: " + UIManager.getBorder("TableHeader.cellBorder")); if (sortOrder.isSorted()) { iconBorder.setIcon(sortOrder.isAscending() ? upIcon : downIcon); Border origBorder = ((JComponent) comp).getBorder(); Border border = new CompoundBorder(origBorder, iconBorder); ((JComponent) comp).setBorder(border); } } adjustComponentOrientation(comp); return comp; } /** * * @param component */ private void hackBorder(JComponent component) { if (hackBorder(component, VISTA_BORDER_HACK)) return; hackBorder(component, METAL_BORDER_HACK); } /** * * @param component */ private boolean hackBorder(JComponent component, Object key) { Border hackBorder = UIManager.getBorder(key); if (hackBorder == null) return false; component.setBorder(hackBorder); return true; } /** * Adjusts the Component's orientation to JXTable's CO if appropriate. * Here: always. * * @param stamp the component to adjust. */ protected void adjustComponentOrientation(Component stamp) { if (stamp.getComponentOrientation().equals(getComponentOrientation())) return; stamp.applyComponentOrientation(getComponentOrientation()); } private Component configureDelegate(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) { Component comp = delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, rowIndex, columnIndex); applyLabelProperties(comp); return comp; } private void applyLabelProperties(Component delegateRendererComponent) { if (delegateRendererComponent instanceof JLabel) { label.applyPropertiesTo((JLabel) delegateRendererComponent); } else { label.applyPropertiesTo(delegateRenderer); } } public void setAntiAliasedText(boolean antiAlias) { this.antiAliasedText = antiAlias; } public boolean getAntiAliasedText() { return antiAliasedText; } @Override public void setBackground(Color background) { // this is called somewhere along initialization of super? if (label != null) { Color old = getBackground(); label.setBackground(background); firePropertyChange("background", old, getBackground()); } } @Override public Color getBackground() { return label.getBackground(); } @Override public void setForeground(Color foreground) { if (label != null) { Color old = getForeground(); label.setForeground(foreground); firePropertyChange("foreground", old, getForeground()); } } @Override public Color getForeground() { return label.getForeground(); } @Override public void setFont(Font font) { if (label != null) { Font old = getFont(); label.setFont(font); firePropertyChange("font", old, getFont()); } } @Override public Font getFont() { return label.getFont(); } public void setDownIcon(Icon icon) { Icon old = getDownIcon(); this.downIcon = icon; firePropertyChange("downIcon", old, getDownIcon()); } public Icon getDownIcon() { return downIcon; } public void setUpIcon(Icon icon) { Icon old = getUpIcon(); this.upIcon = icon; firePropertyChange("upIcon", old, getUpIcon()); } public Icon getUpIcon() { return upIcon; } public void setHorizontalAlignment(int alignment) { int old = getHorizontalAlignment(); label.setHorizontalAlignment(alignment); firePropertyChange("horizontalAlignment", old, getHorizontalAlignment()); } public int getHorizontalAlignment() { return label.getHorizontalAlignment(); } public void setHorizontalTextPosition(int textPosition) { int old = getHorizontalTextPosition(); label.setHorizontalTextPosition(textPosition); firePropertyChange("horizontalTextPosition", old, getHorizontalTextPosition()); } public int getHorizontalTextPosition() { return label.getHorizontalTextPosition(); } public void setIcon(Icon icon) { Icon old = getIcon(); label.setIcon(icon); firePropertyChange("icon", old, getIcon()); } public Icon getIcon() { return label.getIcon(); } public void setIconTextGap(int iconTextGap) { int old = getIconTextGap(); label.setIconTextGap(iconTextGap); firePropertyChange("iconTextGap", old, getIconTextGap()); } public int getIconTextGap() { return label.getIconTextGap(); } public void setVerticalAlignment(int alignment) { int old = getVerticalAlignment(); label.setVerticalAlignment(alignment); firePropertyChange("verticalAlignment", old, getVerticalAlignment()); } public int getVerticalAlignment() { return label.getVerticalAlignment(); } public void setVerticalTextPosition(int textPosition) { int old = getVerticalTextPosition(); label.setVerticalTextPosition(textPosition); firePropertyChange("verticalTextPosition", old, getVerticalTextPosition()); } public int getVerticalTextPosition() { return label.getVerticalTextPosition(); } /** * @return the delegate renderer. */ public TableCellRenderer getDelegateRenderer() { return delegateRenderer; } @Override public void updateUI() { super.updateUI(); initDelegate(); updateIconUI(); } public void updateUI(JTableHeader header) { updateIconUI(); if (header.getDefaultRenderer() != this) { delegateRenderer = header.getDefaultRenderer(); } } private void updateIconUI() { if (getUpIcon() instanceof UIResource) { Icon icon = UIManager.getIcon(UP_ICON_KEY); setUpIcon(icon != null ? icon : defaultUpIcon); } if (getDownIcon() instanceof UIResource) { Icon icon = UIManager.getIcon(DOWN_ICON_KEY); setDownIcon(icon != null ? icon : defaultDownIcon); } } }