001 /*
002 * Copyright (c) 2003 JGoodies Karsten Lentzsch. All Rights Reserved.
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 * o Redistributions of source code must retain the above copyright notice,
008 * this list of conditions and the following disclaimer.
009 *
010 * o Redistributions in binary form must reproduce the above copyright notice,
011 * this list of conditions and the following disclaimer in the documentation
012 * and/or other materials provided with the distribution.
013 *
014 * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015 * its contributors may be used to endorse or promote products derived
016 * from this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030 package org.springframework.richclient.components;
031
032 import java.awt.BorderLayout;
033 import java.awt.Color;
034 import java.awt.Component;
035 import java.awt.GradientPaint;
036 import java.awt.Graphics;
037 import java.awt.Graphics2D;
038 import java.awt.Insets;
039 import java.awt.LayoutManager;
040 import java.awt.Paint;
041
042 import javax.swing.BorderFactory;
043 import javax.swing.Icon;
044 import javax.swing.JComponent;
045 import javax.swing.JLabel;
046 import javax.swing.JPanel;
047 import javax.swing.JToolBar;
048 import javax.swing.SwingConstants;
049 import javax.swing.UIManager;
050 import javax.swing.border.AbstractBorder;
051
052 /**
053 * A <code>JPanel</code> subclass that has a drop shadow border and that
054 * provides a header with icon, title and tool bar.
055 * <p>
056 *
057 * This class can be used to replace the <code>JInternalFrame</code>, for use
058 * outside of a <code>JDesktopPane</code>. The
059 * <code>SimpleInternalFrame</code> is less flexible but often more usable; it
060 * avoids overlapping windows and scales well up to IDE size. Several customers
061 * have reported that they and their clients feel much better with both the
062 * appearance and the UI feel.
063 * <p>
064 *
065 * The SimpleInternalFrame provides the following bound properties:
066 * <i>frameIcon, title, toolBar, content, selected. </i>
067 * <p>
068 *
069 * By default the SimpleInternalFrame is in <i>selected </i> state. If you don't
070 * do anything, multiple simple internal frames will be displayed as selected.
071 *
072 * @author Karsten Lentzsch
073 * @version $Revision: 2105 $
074 *
075 * @see javax.swing.JInternalFrame
076 * @see javax.swing.JDesktopPane
077 */
078
079 public class SimpleInternalFrame extends JPanel {
080
081 private static final long serialVersionUID = 663704388460231166L;
082
083 private JLabel titleLabel;
084
085 private GradientPanel gradientPanel;
086
087 private JPanel headerPanel;
088
089 private boolean isSelected;
090
091 // Instance Creation ****************************************************
092
093 public SimpleInternalFrame() {
094 this("");
095 }
096
097 /**
098 * Constructs a <code>SimpleInternalFrame</code> with the specified title.
099 *
100 * @param title
101 * the initial title
102 */
103 public SimpleInternalFrame(String title) {
104 this(null, title, null, null);
105 }
106
107 /**
108 * Constructs a <code>SimpleInternalFrame</code> with the specified icon,
109 * and title.
110 *
111 * @param icon
112 * the initial icon
113 * @param title
114 * the initial title
115 */
116 public SimpleInternalFrame(Icon icon, String title) {
117 this(icon, title, null, null);
118 }
119
120 /**
121 * Constructs a <code>SimpleInternalFrame</code> with the specified title,
122 * tool bar, and content panel.
123 *
124 * @param title
125 * the initial title
126 * @param bar
127 * the initial tool bar
128 * @param content
129 * the initial content pane
130 */
131 public SimpleInternalFrame(String title, JToolBar bar, JComponent content) {
132 this(null, title, bar, content);
133 }
134
135 /**
136 * Constructs a <code>SimpleInternalFrame</code> with the specified icon,
137 * title, tool bar, and content panel.
138 *
139 * @param icon
140 * the initial icon
141 * @param title
142 * the initial title
143 * @param bar
144 * the initial tool bar
145 * @param content
146 * the initial content pane
147 */
148 public SimpleInternalFrame(Icon icon, String title, JToolBar bar, JComponent content) {
149 super(new BorderLayout());
150 this.isSelected = false;
151 this.titleLabel = new JLabel(title, icon, SwingConstants.LEADING);
152 JPanel top = buildHeader(titleLabel, bar);
153
154 add(top, BorderLayout.NORTH);
155 if (content != null) {
156 setContent(content);
157 }
158 setBorder(new ShadowBorder());
159 setSelected(true);
160 updateHeader();
161 }
162
163 // Public API ***********************************************************
164
165 /**
166 * Returns the frame's icon.
167 *
168 * @return the frame's icon
169 */
170 public Icon getFrameIcon() {
171 return titleLabel.getIcon();
172 }
173
174 /**
175 * Sets a new frame icon.
176 *
177 * @param newIcon
178 * the icon to be set
179 */
180 public void setFrameIcon(Icon newIcon) {
181 Icon oldIcon = getFrameIcon();
182 titleLabel.setIcon(newIcon);
183 firePropertyChange("frameIcon", oldIcon, newIcon);
184 }
185
186 /**
187 * Returns the frame's title text.
188 *
189 * @return String the current title text
190 */
191 public String getTitle() {
192 return titleLabel.getText();
193 }
194
195 /**
196 * Sets a new title text.
197 *
198 * @param newText
199 * the title text tp be set
200 */
201 public void setTitle(String newText) {
202 String oldText = getTitle();
203 titleLabel.setText(newText);
204 firePropertyChange("title", oldText, newText);
205 }
206
207 /**
208 * Returns the current toolbar, null if none has been set before.
209 *
210 * @return the current toolbar - if any
211 */
212 public JToolBar getToolBar() {
213 return headerPanel.getComponentCount() > 1 ? (JToolBar)headerPanel.getComponent(1) : null;
214 }
215
216 /**
217 * Sets a new tool bar in the header.
218 *
219 * @param newToolBar
220 * the tool bar to be set in the header
221 */
222 public void setToolBar(JToolBar newToolBar) {
223 JToolBar oldToolBar = getToolBar();
224 if (oldToolBar == newToolBar) {
225 return;
226 }
227 if (oldToolBar != null) {
228 headerPanel.remove(oldToolBar);
229 }
230 if (newToolBar != null) {
231 newToolBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
232 headerPanel.add(newToolBar, BorderLayout.EAST);
233 }
234 updateHeader();
235 firePropertyChange("toolBar", oldToolBar, newToolBar);
236 }
237
238 /**
239 * Returns the content - null, if none has been set.
240 *
241 * @return the current content
242 */
243 public Component getContent() {
244 return hasContent() ? getComponent(1) : null;
245 }
246
247 /**
248 * Sets a new panel content; replaces any existing content, if existing.
249 *
250 * @param newContent
251 * the panel's new content
252 */
253 public void setContent(Component newContent) {
254 Component oldContent = getContent();
255 if (hasContent()) {
256 remove(oldContent);
257 }
258 add(newContent, BorderLayout.CENTER);
259 firePropertyChange("content", oldContent, newContent);
260 }
261
262 /**
263 * Answers if the panel is currently selected (or in other words active) or
264 * not. In the selected state, the header background will be rendered
265 * differently.
266 *
267 * @return boolean a boolean, where true means the frame is selected
268 * (currently active) and false means it is not
269 */
270 public boolean isSelected() {
271 return isSelected;
272 }
273
274 /**
275 * This panel draws its title bar differently if it is selected, which may
276 * be used to indicate to the user that this panel has the focus, or should
277 * get more attention than other simple internal frames.
278 *
279 * @param newValue
280 * a boolean, where true means the frame is selected (currently
281 * active) and false means it is not
282 */
283 public void setSelected(boolean newValue) {
284 boolean oldValue = isSelected();
285 isSelected = newValue;
286 updateHeader();
287 firePropertyChange("selected", oldValue, newValue);
288 }
289
290 // Building *************************************************************
291
292 /**
293 * Creates and answers the header panel, that consists of: an icon, a title
294 * label, a tool bar, and a gradient background.
295 *
296 * @param label
297 * the label to paint the icon and text
298 * @param bar
299 * the panel's tool bar
300 * @return the panel's built header area
301 */
302 private JPanel buildHeader(JLabel label, JToolBar bar) {
303 gradientPanel = new GradientPanel(new BorderLayout(), getHeaderBackground());
304 label.setOpaque(false);
305
306 gradientPanel.add(label, BorderLayout.WEST);
307 gradientPanel.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 1));
308
309 headerPanel = new JPanel(new BorderLayout());
310 headerPanel.add(gradientPanel, BorderLayout.CENTER);
311 setToolBar(bar);
312 headerPanel.setBorder(new RaisedHeaderBorder());
313 headerPanel.setOpaque(false);
314 return headerPanel;
315 }
316
317 /**
318 * Updates the header.
319 */
320 private void updateHeader() {
321 gradientPanel.setBackground(getHeaderBackground());
322 gradientPanel.setOpaque(isSelected());
323 titleLabel.setForeground(getTextForeground(isSelected()));
324 headerPanel.repaint();
325 }
326
327 /**
328 * Updates the UI. In addition to the superclass behavior, we need to update
329 * the header component.
330 */
331 public void updateUI() {
332 super.updateUI();
333 if (titleLabel != null) {
334 updateHeader();
335 }
336 }
337
338 // Helper Code **********************************************************
339
340 /**
341 * Checks and answers if the panel has a content component set.
342 *
343 * @return true if the panel has a content, false if it's empty
344 */
345 private boolean hasContent() {
346 return getComponentCount() > 1;
347 }
348
349 /**
350 * Determines and answers the header's text foreground color. Tries to
351 * lookup a special color from the L&F. In case it is absent, it uses
352 * the standard internal frame forground.
353 *
354 * @param selected
355 * true to lookup the active color, false for the inactive
356 * @return the color of the foreground text
357 */
358 protected Color getTextForeground(boolean selected) {
359 Color c = UIManager.getColor(selected ? "SimpleInternalFrame.activeTitleForeground"
360 : "SimpleInternalFrame.inactiveTitleForeground");
361 if (c != null) {
362 return c;
363 }
364 return UIManager.getColor(selected ? "InternalFrame.activeTitleForeground" : "Label.foreground");
365
366 }
367
368 /**
369 * Determines and answers the header's background color. Tries to lookup a
370 * special color from the L&F. In case it is absent, it uses the
371 * standard internal frame background.
372 *
373 * @return the color of the header's background
374 */
375 protected Color getHeaderBackground() {
376 Color c = UIManager.getColor("SimpleInternalFrame.activeTitleBackground");
377
378 return c != null ? c : UIManager.getColor("InternalFrame.activeTitleBackground");
379 }
380
381 // Helper Classes *******************************************************
382
383 // A custom border for the raised header pseudo 3D effect.
384 private static class RaisedHeaderBorder extends AbstractBorder {
385
386 private static final Insets INSETS = new Insets(1, 1, 1, 0);
387
388 public Insets getBorderInsets(Component c) {
389 return INSETS;
390 }
391
392 public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
393
394 g.translate(x, y);
395 g.setColor(UIManager.getColor("controlLtHighlight"));
396 g.fillRect(0, 0, w, 1);
397 g.fillRect(0, 1, 1, h - 1);
398 g.setColor(UIManager.getColor("controlShadow"));
399 g.fillRect(0, h - 1, w, 1);
400 g.translate(-x, -y);
401 }
402 }
403
404 // A panel with a horizontal gradient background.
405 private static class GradientPanel extends JPanel {
406
407 private GradientPanel(LayoutManager lm, Color background) {
408 super(lm);
409 setBackground(background);
410 }
411
412 public void paintComponent(Graphics g) {
413 super.paintComponent(g);
414 if (!isOpaque()) {
415 return;
416 }
417 Color control = UIManager.getColor("control");
418 int width = getWidth();
419 int height = getHeight();
420
421 Graphics2D g2 = (Graphics2D)g;
422 Paint storedPaint = g2.getPaint();
423 g2.setPaint(new GradientPaint(0, 0, getBackground(), width, 0, control));
424 g2.fillRect(0, 0, width, height);
425 g2.setPaint(storedPaint);
426 }
427 }
428
429 }