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 }