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 }