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