001/* 002 * $Id: WrappingIconPanel.java 3927 2011-02-22 16:34:11Z kleopatra $ 003 * 004 * Copyright 2007 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 */ 021package org.jdesktop.swingx.renderer; 022 023import java.awt.BorderLayout; 024import java.awt.Color; 025import java.awt.ComponentOrientation; 026import java.awt.Font; 027import java.awt.Rectangle; 028 029import javax.swing.BorderFactory; 030import javax.swing.Icon; 031import javax.swing.JComponent; 032import javax.swing.JLabel; 033import javax.swing.border.Border; 034 035import org.jdesktop.swingx.JXPanel; 036import org.jdesktop.swingx.painter.Painter; 037 038/** 039 * Compound component for usage in tree renderer. <p> 040 * 041 * Supports setting an icon for the node and a delegate component 042 * which is used to show the text/content of the node. The delegate 043 * component can be shared across renderers. <p> 044 * 045 * This implements the PainterAware by delegating to the delegate component if that 046 * is of type PainterAware. Does nothing if not. 047 */ 048public class WrappingIconPanel extends JXPanel implements PainterAware, IconAware { 049 protected JComponent delegate; 050 JLabel iconLabel; 051 String labelPosition = BorderLayout.CENTER; //2; 052 int iconLabelGap; 053 private Border ltorBorder; 054 private Border rtolBorder; 055 private boolean dropHackEnabled; 056 private boolean extendsComponentOpacity; 057 058 059 /** 060 * Instantiates and configures a WrappingIconPanel with the dropHack 061 * enabled. 062 * 063 */ 064 public WrappingIconPanel() { 065 this(true); 066 } 067 /** 068 * Instantiates and configures a WrappingIconPanel with the dropHack 069 * property set as indicated by the boolean. 070 * 071 * @param dropHackEnabled a boolean indicating whether the drop hack should 072 * be enabled. 073 * 074 * @see #isVisible() 075 */ 076 public WrappingIconPanel(boolean dropHackEnabled) { 077 setOpaque(false); 078 iconLabel = new JRendererLabel(); 079 iconLabelGap = iconLabel.getIconTextGap(); 080 iconLabel.setOpaque(false); 081 updateIconBorder(); 082 setBorder(null); 083 setLayout(new BorderLayout()); 084 add(iconLabel, BorderLayout.LINE_START); 085 setDropHackEnabled(dropHackEnabled); 086 } 087 088 /** 089 * {@inheritDoc} <p> 090 * 091 * Overridden to update the icon position. 092 */ 093 @Override 094 public void setComponentOrientation(ComponentOrientation o) { 095 super.setComponentOrientation(o); 096 updateIconBorder(); 097 } 098 099 /** 100 * Updates the icon position according to ComponentOrientation. 101 */ 102 private void updateIconBorder() { 103 if (ltorBorder == null) { 104 ltorBorder = BorderFactory.createEmptyBorder(0, 0, 0, iconLabelGap); 105 rtolBorder = BorderFactory.createEmptyBorder(0, iconLabelGap, 0, 0); 106 } 107 if (getComponentOrientation().isLeftToRight()) { 108 iconLabel.setBorder(ltorBorder); 109 } else { 110 iconLabel.setBorder(rtolBorder); 111 } 112 } 113 114 /** 115 * Sets the icon. 116 * 117 * @param icon the icon to use. 118 */ 119 @Override 120 public void setIcon(Icon icon) { 121 iconLabel.setIcon(icon); 122 iconLabel.setText(null); 123 validate(); 124 } 125 126 /** 127 * Returns the icon used in this panel, may be null. 128 * 129 * @return the icon used in this panel, may be null. 130 */ 131 @Override 132 public Icon getIcon() { 133 return iconLabel.getIcon(); 134 } 135 136 137 /** 138 * Sets the delegate component. 139 * 140 * @param comp the component to add as delegate. 141 */ 142 public void setComponent(JComponent comp) { 143 JComponent old = getComponent(); 144 if (delegate != null) { 145 remove(delegate); 146 } 147 delegate = comp; 148 if (extendsComponentOpacity) { 149 iconLabel.setOpaque(comp.isOpaque()); 150 } else { 151 iconLabel.setOpaque(false); 152 } 153 add(delegate, labelPosition); 154 validate(); 155 firePropertyChange("component", old, getComponent()); 156 } 157 158 /** 159 * Returns the delegate component. 160 * 161 * @return the delegate component. 162 */ 163 public JComponent getComponent() { 164 return delegate; 165 } 166 167 /** 168 * {@inheritDoc} <p> 169 * 170 * Overridden to set the background of the delegate and icon label as well. 171 */ 172 @Override 173 public void setBackground(Color bg) { 174 super.setBackground(bg); 175 if (iconLabel != null) { 176 iconLabel.setBackground(bg); 177 } 178 if (delegate != null) { 179 delegate.setBackground(bg); 180 } 181 } 182 183 /** 184 * {@inheritDoc} <p> 185 * 186 * Overridden to set the foreground of the delegate and icon label as well. 187 */ 188 @Override 189 public void setForeground(Color bg) { 190 super.setForeground(bg); 191 if (iconLabel != null) { 192 iconLabel.setForeground(bg); 193 } 194 if (delegate != null) { 195 delegate.setForeground(bg); 196 } 197 } 198 199 200 201 202 /** 203 * {@inheritDoc} <p> 204 * 205 * Overridden to set the Font of the delegate as well. 206 */ 207 @Override 208 public void setFont(Font font) { 209 if (delegate != null) { 210 delegate.setFont(font); 211 } 212 super.setFont(font); 213 } 214 215 216 /** 217 * {@inheritDoc} 218 * <p> 219 * 220 * Overridden to hack around #766-swingx: cursor flickering in DnD when 221 * dragging over tree column. This is a core bug (#6700748) related to 222 * painting the rendering component on a CellRendererPane. A trick around is 223 * to let this return false. 224 * <p> 225 * 226 * Some LayoutManagers don't layout an invisible component, so need to make 227 * the hack-enabled configurable. This implementation will return false 228 * if isDropHackEnabled, super.isVisible otherwise. 229 */ 230 @Override 231 public boolean isVisible() { 232 return dropHackEnabled ? false : super.isVisible(); 233 } 234 235 236 /** 237 * {@inheritDoc} 238 * <p> 239 * 240 * Returns the delegate's Painter if it is of type PainterAware or null 241 * otherwise. 242 * 243 * @return the delegate's Painter or null. 244 */ 245 @Override 246 public Painter<?> getPainter() { 247 if (delegate instanceof PainterAware) { 248 return ((PainterAware) delegate).getPainter(); 249 } 250 return null; 251 } 252 253 254 /** 255 * Sets the delegate's Painter if it is of type PainterAware. Does nothing otherwise. 256 * 257 * @param painter the Painter to apply to the delegate. 258 */ 259 @Override 260 public void setPainter(Painter<?> painter) { 261 if (delegate instanceof PainterAware) { 262 ((PainterAware) delegate).setPainter(painter); 263 } 264 265 } 266 267 /** 268 * 269 * Returns the bounds of the delegate component or null if the delegate is null. 270 * 271 * PENDING JW: where do we use it? Maybe it was for testing only? 272 * 273 * @return the bounds of the delegate, or null if the delegate is null. 274 */ 275 public Rectangle getDelegateBounds() { 276 if (delegate == null) return null; 277 return delegate.getBounds(); 278 } 279 280 281 /** 282 * Sets the dropHackEnabled property. <p> 283 * 284 * The default value is true. 285 * 286 * @param dropHackEnabled 287 * 288 * @see #isVisible() 289 */ 290 public void setDropHackEnabled(boolean dropHackEnabled) { 291 this.dropHackEnabled = dropHackEnabled; 292 } 293 294 /** 295 * Sets a boolean indicating whether or not the main component's opacity 296 * should be applied to the Icon region.<p> 297 * 298 * The default value is false. This covers the main use case in a JTree. 299 * 300 * @param extendsComponentOpacity 301 */ 302 public void setExtendsComponentOpacity(boolean extendsComponentOpacity) { 303 this.extendsComponentOpacity = extendsComponentOpacity; 304 305 } 306 /** 307 * @return the extendsComponentOpacity 308 */ 309 public boolean getExtendsComponentOpacity() { 310 return extendsComponentOpacity; 311 } 312 313 314}