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}