001    /*
002     * $Id: BasicTitledPanelUI.java,v 1.14 2006/04/11 20:51:31 rbair 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.plaf.basic;
023    
024    import java.awt.BorderLayout;
025    import java.awt.Color;
026    import java.awt.Container;
027    import java.awt.Font;
028    import java.awt.GradientPaint;
029    import java.awt.Graphics;
030    import java.awt.Graphics2D;
031    import java.awt.GridBagConstraints;
032    import java.awt.GridBagLayout;
033    import java.awt.Image;
034    import java.awt.Insets;
035    import java.beans.BeanInfo;
036    import java.beans.Introspector;
037    import java.beans.PropertyChangeEvent;
038    import java.beans.PropertyChangeListener;
039    import java.beans.PropertyDescriptor;
040    import java.lang.reflect.Method;
041    import java.util.logging.Level;
042    import java.util.logging.Logger;
043    
044    import javax.swing.BorderFactory;
045    import javax.swing.ImageIcon;
046    import javax.swing.JComponent;
047    import javax.swing.JLabel;
048    import javax.swing.UIManager;
049    import javax.swing.plaf.UIResource;
050    
051    import org.jdesktop.swingx.JXPanel;
052    import org.jdesktop.swingx.JXTitledPanel;
053    import org.jdesktop.swingx.painter.Painter;
054    import org.jdesktop.swingx.plaf.TitledPanelUI;
055    
056    
057    /**
058     * All TitledPanels contain a title section and a content section. The default
059     * implementation for the title section relies on a Gradient background. All
060     * title sections can have components embedded to the "left" or
061     * "right" of the Title.
062     *
063     * @author Richard Bair
064     * @author Jeanette Winzenburg
065     *
066     */
067    public abstract class BasicTitledPanelUI extends TitledPanelUI {
068        private static final Logger LOG = Logger.getLogger(BasicTitledPanelUI.class
069                .getName());
070        
071        /**
072         * JLabel used for the title in the Title section of the JTitledPanel.
073         */
074        private JLabel caption;
075        /**
076         * The Title section panel.
077         */
078        private JXPanel topPanel;
079        /**
080         * Listens to changes in the title of the JXTitledPanel component
081         */
082        private PropertyChangeListener titleChangeListener;
083        /**
084         * The JXTitledPanel peered with this UI
085         */
086        protected JXTitledPanel titledPanel;
087        private JComponent left;
088        private JComponent right;
089        
090        /** Creates a new instance of BasicTitledPanelUI */
091        public BasicTitledPanelUI() {
092        }
093        
094        /**
095         * Configures the specified component appropriate for the look and feel.
096         * This method is invoked when the <code>ComponentUI</code> instance is being installed
097         * as the UI delegate on the specified component.  This method should
098         * completely configure the component for the look and feel,
099         * including the following:
100         * <ol>
101         * <li>Install any default property values for color, fonts, borders,
102         *     icons, opacity, etc. on the component.  Whenever possible,
103         *     property values initialized by the client program should <i>not</i>
104         *     be overridden.
105         * <li>Install a <code>LayoutManager</code> on the component if necessary.
106         * <li>Create/add any required sub-components to the component.
107         * <li>Create/install event listeners on the component.
108         * <li>Create/install a <code>PropertyChangeListener</code> on the component in order
109         *     to detect and respond to component property changes appropriately.
110         * <li>Install keyboard UI (mnemonics, traversal, etc.) on the component.
111         * <li>Initialize any appropriate instance data.
112         * </ol>
113         * @param c the component where this UI delegate is being installed
114         *
115         * @see #uninstallUI
116         * @see javax.swing.JComponent#setUI
117         * @see javax.swing.JComponent#updateUI
118         */
119        public void installUI(JComponent c) {
120            assert c instanceof JXTitledPanel;
121            titledPanel = (JXTitledPanel)c;
122            
123            installProperty(titledPanel, "titlePainter", (Painter)UIManager.get("JXTitledPanel.title.painter"));
124            installProperty(titledPanel, "titleForeground", UIManager.getColor("JXTitledPanel.title.foreground"));
125            installProperty(titledPanel, "titleFont", UIManager.getFont("JXTitledPanel.title.font"));
126            
127            
128            caption = createAndConfigureCaption(titledPanel);
129            topPanel = createAndConfigureTopPanel(titledPanel);
130            fillTopPanel();
131            titledPanel.setLayout(new BorderLayout());
132            titledPanel.add(topPanel, BorderLayout.NORTH);
133            titledPanel.setBorder(BorderFactory.createRaisedBevelBorder());
134            titledPanel.setOpaque(false);
135            
136            installListeners();
137            
138        }
139        
140        private void fillTopPanel() {
141            topPanel.add(caption, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(4, 12, 4, 12), 0, 0));
142            if (titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION) instanceof JComponent) {
143                setRightDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION));
144            }
145            if (titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION) instanceof JComponent) {
146                setLeftDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION));
147            }
148        }
149        
150        private JXPanel createAndConfigureTopPanel(JXTitledPanel titledPanel) {
151            JXPanel topPanel = new JXPanel();
152            topPanel.setBackgroundPainter(titledPanel.getTitlePainter());
153            topPanel.setBorder(BorderFactory.createEmptyBorder());
154            topPanel.setLayout(new GridBagLayout());
155            return topPanel;
156        }
157        
158        private JLabel createAndConfigureCaption(JXTitledPanel titledPanel) {
159            JLabel caption = new JLabel(titledPanel.getTitle());
160            caption.setFont(titledPanel.getTitleFont());
161            caption.setForeground(titledPanel.getTitleForeground());
162            return caption;
163        }
164        /**
165         * Reverses configuration which was done on the specified component during
166         * <code>installUI</code>.  This method is invoked when this
167         * <code>UIComponent</code> instance is being removed as the UI delegate
168         * for the specified component.  This method should undo the
169         * configuration performed in <code>installUI</code>, being careful to
170         * leave the <code>JComponent</code> instance in a clean state (no
171         * extraneous listeners, look-and-feel-specific property objects, etc.).
172         * This should include the following:
173         * <ol>
174         * <li>Remove any UI-set borders from the component.
175         * <li>Remove any UI-set layout managers on the component.
176         * <li>Remove any UI-added sub-components from the component.
177         * <li>Remove any UI-added event/property listeners from the component.
178         * <li>Remove any UI-installed keyboard UI from the component.
179         * <li>Nullify any allocated instance data objects to allow for GC.
180         * </ol>
181         * @param c the component from which this UI delegate is being removed;
182         *          this argument is often ignored,
183         *          but might be used if the UI object is stateless
184         *          and shared by multiple components
185         *
186         * @see #installUI
187         * @see javax.swing.JComponent#updateUI
188         */
189        public void uninstallUI(JComponent c) {
190            assert c instanceof JXTitledPanel;
191            uninstallListeners(titledPanel);
192            // JW: this is needed to make the gradient paint work correctly...
193            // LF changes will remove the left/right components...
194            topPanel.removeAll();
195            titledPanel.remove(topPanel);
196            titledPanel.putClientProperty(JXTitledPanel.LEFT_DECORATION, left);
197            titledPanel.putClientProperty(JXTitledPanel.RIGHT_DECORATION, right);
198            caption =  null;
199            topPanel = null;
200            titledPanel = null;
201            left = null;
202            right = null;
203        }
204        
205        
206        protected void installListeners() {
207            titleChangeListener = new PropertyChangeListener() {
208                public void propertyChange(PropertyChangeEvent evt) {
209                    if (evt.getPropertyName().equals("title")) {
210                        caption.setText((String)evt.getNewValue());
211                    } else if (evt.getPropertyName().equals("titleForeground")) {
212                        caption.setForeground((Color)evt.getNewValue());
213                    } else if (evt.getPropertyName().equals("titleFont")) {
214                        caption.setFont((Font)evt.getNewValue());
215                    } else if ("titlePainter".equals(evt.getPropertyName())) {
216                        topPanel.setBackgroundPainter(titledPanel.getTitlePainter());
217                        topPanel.repaint();
218                    }
219                }
220            };
221            titledPanel.addPropertyChangeListener(titleChangeListener);
222        }
223        
224        protected void uninstallListeners(JXTitledPanel panel) {
225            titledPanel.removePropertyChangeListener(titleChangeListener);
226        }
227        
228        protected void installProperty(JComponent c, String propName, Object value) {
229            try {
230                BeanInfo bi = Introspector.getBeanInfo(c.getClass());
231                for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
232                    if (pd.getName().equals(propName)) {
233                        Method m = pd.getReadMethod();
234                        Object oldVal = m.invoke(c);
235                        if (oldVal == null || oldVal instanceof UIResource) {
236                            m = pd.getWriteMethod();
237                            m.invoke(c, value);
238                        }
239                    }
240                }
241            } catch (Exception e) {
242                LOG.log(Level.FINE, "Failed to install property " + propName, e);
243            }
244        }
245        
246        
247        /**
248         * Paints the specified component appropriate for the look and feel.
249         * This method is invoked from the <code>ComponentUI.update</code> method when
250         * the specified component is being painted.  Subclasses should override
251         * this method and use the specified <code>Graphics</code> object to
252         * render the content of the component.
253         *
254         * @param g the <code>Graphics</code> context in which to paint
255         * @param c the component being painted;
256         *          this argument is often ignored,
257         *          but might be used if the UI object is stateless
258         *          and shared by multiple components
259         *
260         * @see #update
261         */
262        public void paint(Graphics g, JComponent c) {
263            super.paint(g, c);
264        }
265        
266        /**
267         * Adds the given JComponent as a decoration on the right of the title
268         * @param decoration
269         */
270        public void setRightDecoration(JComponent decoration) {
271            if (right != null) topPanel.remove(right);
272            right = decoration;
273            if (right != null) {
274                topPanel.add(decoration, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
275                
276            }
277        }
278        
279        public JComponent getRightDecoration() {
280            return right;
281        }
282        
283        /**
284         * Adds the given JComponent as a decoration on the left of the title
285         * @param decoration
286         */
287        public void setLeftDecoration(JComponent decoration) {
288            if (left != null) topPanel.remove(left);
289            left = decoration;
290            if (left != null) {
291                topPanel.add(left, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
292            }
293        }
294        
295        public JComponent getLeftDecoration() {
296            return left;
297        }
298        
299        /**
300         * @return the Container acting as the title bar for this component
301         */
302        public Container getTitleBar() {
303            return topPanel;
304        }
305    }