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 }