001: /*
002: * Copyright (c) 2000-2006 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:
031: package com.jgoodies.uif_lite.panel;
032:
033: import java.awt.*;
034:
035: import javax.swing.*;
036: import javax.swing.border.AbstractBorder;
037:
038: /**
039: * A <code>JPanel</code> subclass that has a drop shadow border and
040: * that provides a header with icon, title and tool bar.<p>
041: *
042: * This class can be used to replace the <code>JInternalFrame</code>,
043: * for use outside of a <code>JDesktopPane</code>.
044: * The <code>SimpleInternalFrame</code> is less flexible but often
045: * more usable; it avoids overlapping windows and scales well
046: * up to IDE size.
047: * Several customers have reported that they and their clients feel
048: * much better with both the appearance and the UI feel.<p>
049: *
050: * The SimpleInternalFrame provides the following bound properties:
051: * <i>frameIcon, title, toolBar, content, selected.</i><p>
052: *
053: * By default the SimpleInternalFrame is in <i>selected</i> state.
054: * If you don't do anything, multiple simple internal frames will
055: * be displayed as selected.
056: *
057: * @author Karsten Lentzsch
058: * @version $Revision: 1.3 $
059: *
060: * @see javax.swing.JInternalFrame
061: * @see javax.swing.JDesktopPane
062: */
063:
064: public class SimpleInternalFrame extends JPanel {
065:
066: private JLabel titleLabel;
067: private GradientPanel gradientPanel;
068: private JPanel headerPanel;
069: private boolean selected;
070:
071: // Instance Creation ****************************************************
072:
073: /**
074: * Constructs a SimpleInternalFrame with the specified title.
075: * The title is intended to be non-blank, or in other words
076: * should contain non-space characters.
077: *
078: * @param title the initial title
079: */
080: public SimpleInternalFrame(String title) {
081: this (null, title, null, null);
082: }
083:
084: /**
085: * Constructs a SimpleInternalFrame with the specified
086: * icon, and title.
087: *
088: * @param icon the initial icon
089: * @param title the initial title
090: */
091: public SimpleInternalFrame(Icon icon, String title) {
092: this (icon, title, null, null);
093: }
094:
095: /**
096: * Constructs a SimpleInternalFrame with the specified
097: * title, tool bar, and content panel.
098: *
099: * @param title the initial title
100: * @param bar the initial tool bar
101: * @param content the initial content pane
102: */
103: public SimpleInternalFrame(String title, JToolBar bar,
104: JComponent content) {
105: this (null, title, bar, content);
106: }
107:
108: /**
109: * Constructs a SimpleInternalFrame with the specified
110: * icon, title, tool bar, and content panel.
111: *
112: * @param icon the initial icon
113: * @param title the initial title
114: * @param bar the initial tool bar
115: * @param content the initial content pane
116: */
117: public SimpleInternalFrame(Icon icon, String title, JToolBar bar,
118: JComponent content) {
119: super (new BorderLayout());
120: this .selected = false;
121: this .titleLabel = new JLabel(title, icon,
122: SwingConstants.LEADING);
123: JPanel top = buildHeader(titleLabel, bar);
124:
125: add(top, BorderLayout.NORTH);
126: if (content != null) {
127: setContent(content);
128: }
129: setBorder(new ShadowBorder());
130: setSelected(true);
131: updateHeader();
132: }
133:
134: // Public API ***********************************************************
135:
136: /**
137: * Returns the frame's icon.
138: *
139: * @return the frame's icon
140: */
141: public Icon getFrameIcon() {
142: return titleLabel.getIcon();
143: }
144:
145: /**
146: * Sets a new frame icon.
147: *
148: * @param newIcon the icon to be set
149: */
150: public void setFrameIcon(Icon newIcon) {
151: Icon oldIcon = getFrameIcon();
152: titleLabel.setIcon(newIcon);
153: firePropertyChange("frameIcon", oldIcon, newIcon);
154: }
155:
156: /**
157: * Returns the frame's title text.
158: *
159: * @return String the current title text
160: */
161: public String getTitle() {
162: return titleLabel.getText();
163: }
164:
165: /**
166: * Sets a new title text.
167: *
168: * @param newText the title text tp be set
169: */
170: public void setTitle(String newText) {
171: String oldText = getTitle();
172: titleLabel.setText(newText);
173: firePropertyChange("title", oldText, newText);
174: }
175:
176: /**
177: * Returns the current toolbar, null if none has been set before.
178: *
179: * @return the current toolbar - if any
180: */
181: public JToolBar getToolBar() {
182: return headerPanel.getComponentCount() > 1 ? (JToolBar) headerPanel
183: .getComponent(1)
184: : null;
185: }
186:
187: /**
188: * Sets a new tool bar in the header.
189: *
190: * @param newToolBar the tool bar to be set in the header
191: */
192: public void setToolBar(JToolBar newToolBar) {
193: JToolBar oldToolBar = getToolBar();
194: if (oldToolBar == newToolBar) {
195: return;
196: }
197: if (oldToolBar != null) {
198: headerPanel.remove(oldToolBar);
199: }
200: if (newToolBar != null) {
201: newToolBar.setBorder(BorderFactory.createEmptyBorder(0, 0,
202: 0, 0));
203: headerPanel.add(newToolBar, BorderLayout.EAST);
204: }
205: updateHeader();
206: firePropertyChange("toolBar", oldToolBar, newToolBar);
207: }
208:
209: /**
210: * Returns the content - null, if none has been set.
211: *
212: * @return the current content
213: */
214: public Component getContent() {
215: return hasContent() ? getComponent(1) : null;
216: }
217:
218: /**
219: * Sets a new panel content; replaces any existing content, if existing.
220: *
221: * @param newContent the panel's new content
222: */
223: public void setContent(Component newContent) {
224: Component oldContent = getContent();
225: if (hasContent()) {
226: remove(oldContent);
227: }
228: add(newContent, BorderLayout.CENTER);
229: firePropertyChange("content", oldContent, newContent);
230: }
231:
232: /**
233: * Answers if the panel is currently selected (or in other words active)
234: * or not. In the selected state, the header background will be
235: * rendered differently.
236: *
237: * @return boolean a boolean, where true means the frame is selected
238: * (currently active) and false means it is not
239: */
240: public boolean isSelected() {
241: return selected;
242: }
243:
244: /**
245: * This panel draws its title bar differently if it is selected,
246: * which may be used to indicate to the user that this panel
247: * has the focus, or should get more attention than other
248: * simple internal frames.
249: *
250: * @param newValue a boolean, where true means the frame is selected
251: * (currently active) and false means it is not
252: */
253: public void setSelected(boolean newValue) {
254: boolean oldValue = isSelected();
255: selected = newValue;
256: updateHeader();
257: firePropertyChange("selected", oldValue, newValue);
258: }
259:
260: // Building *************************************************************
261:
262: /**
263: * Creates and answers the header panel, that consists of:
264: * an icon, a title label, a tool bar, and a gradient background.
265: *
266: * @param label the label to paint the icon and text
267: * @param bar the panel's tool bar
268: * @return the panel's built header area
269: */
270: private JPanel buildHeader(JLabel label, JToolBar bar) {
271: gradientPanel = new GradientPanel(new BorderLayout(),
272: getHeaderBackground());
273: label.setOpaque(false);
274:
275: gradientPanel.add(label, BorderLayout.WEST);
276: gradientPanel.setBorder(BorderFactory.createEmptyBorder(3, 4,
277: 3, 1));
278:
279: headerPanel = new JPanel(new BorderLayout());
280: headerPanel.add(gradientPanel, BorderLayout.CENTER);
281: setToolBar(bar);
282: headerPanel.setBorder(new RaisedHeaderBorder());
283: headerPanel.setOpaque(false);
284: return headerPanel;
285: }
286:
287: /**
288: * Updates the header.
289: */
290: private void updateHeader() {
291: gradientPanel.setBackground(getHeaderBackground());
292: gradientPanel.setOpaque(isSelected());
293: titleLabel.setForeground(getTextForeground(isSelected()));
294: headerPanel.repaint();
295: }
296:
297: /**
298: * Updates the UI. In addition to the superclass behavior, we need
299: * to update the header component.
300: */
301: public void updateUI() {
302: super .updateUI();
303: if (titleLabel != null) {
304: updateHeader();
305: }
306: }
307:
308: // Helper Code **********************************************************
309:
310: /**
311: * Checks and answers if the panel has a content component set.
312: *
313: * @return true if the panel has a content, false if it's empty
314: */
315: private boolean hasContent() {
316: return getComponentCount() > 1;
317: }
318:
319: /**
320: * Determines and answers the header's text foreground color.
321: * Tries to lookup a special color from the L&F.
322: * In case it is absent, it uses the standard internal frame forground.
323: *
324: * @param isSelected true to lookup the active color, false for the inactive
325: * @return the color of the foreground text
326: */
327: protected Color getTextForeground(boolean isSelected) {
328: Color c = UIManager
329: .getColor(isSelected ? "SimpleInternalFrame.activeTitleForeground"
330: : "SimpleInternalFrame.inactiveTitleForeground");
331: if (c != null) {
332: return c;
333: }
334: return UIManager
335: .getColor(isSelected ? "InternalFrame.activeTitleForeground"
336: : "Label.foreground");
337:
338: }
339:
340: /**
341: * Determines and answers the header's background color.
342: * Tries to lookup a special color from the L&F.
343: * In case it is absent, it uses the standard internal frame background.
344: *
345: * @return the color of the header's background
346: */
347: protected Color getHeaderBackground() {
348: Color c = UIManager
349: .getColor("SimpleInternalFrame.activeTitleBackground");
350: return c != null ? c : UIManager
351: .getColor("InternalFrame.activeTitleBackground");
352: }
353:
354: // Helper Classes *******************************************************
355:
356: // A custom border for the raised header pseudo 3D effect.
357: private static class RaisedHeaderBorder extends AbstractBorder {
358:
359: private static final Insets INSETS = new Insets(1, 1, 1, 0);
360:
361: public Insets getBorderInsets(Component c) {
362: return INSETS;
363: }
364:
365: public void paintBorder(Component c, Graphics g, int x, int y,
366: int w, int h) {
367:
368: g.translate(x, y);
369: g.setColor(UIManager.getColor("controlLtHighlight"));
370: g.fillRect(0, 0, w, 1);
371: g.fillRect(0, 1, 1, h - 1);
372: g.setColor(UIManager.getColor("controlShadow"));
373: g.fillRect(0, h - 1, w, 1);
374: g.translate(-x, -y);
375: }
376: }
377:
378: // A custom border that has a shadow on the right and lower sides.
379: private static class ShadowBorder extends AbstractBorder {
380:
381: private static final Insets INSETS = new Insets(1, 1, 3, 3);
382:
383: public Insets getBorderInsets(Component c) {
384: return INSETS;
385: }
386:
387: public void paintBorder(Component c, Graphics g, int x, int y,
388: int w, int h) {
389:
390: Color shadow = UIManager.getColor("controlShadow");
391: if (shadow == null) {
392: shadow = Color.GRAY;
393: }
394: Color lightShadow = new Color(shadow.getRed(), shadow
395: .getGreen(), shadow.getBlue(), 170);
396: Color lighterShadow = new Color(shadow.getRed(), shadow
397: .getGreen(), shadow.getBlue(), 70);
398: g.translate(x, y);
399:
400: g.setColor(shadow);
401: g.fillRect(0, 0, w - 3, 1);
402: g.fillRect(0, 0, 1, h - 3);
403: g.fillRect(w - 3, 1, 1, h - 3);
404: g.fillRect(1, h - 3, w - 3, 1);
405: // Shadow line 1
406: g.setColor(lightShadow);
407: g.fillRect(w - 3, 0, 1, 1);
408: g.fillRect(0, h - 3, 1, 1);
409: g.fillRect(w - 2, 1, 1, h - 3);
410: g.fillRect(1, h - 2, w - 3, 1);
411: // Shadow line2
412: g.setColor(lighterShadow);
413: g.fillRect(w - 2, 0, 1, 1);
414: g.fillRect(0, h - 2, 1, 1);
415: g.fillRect(w - 2, h - 2, 1, 1);
416: g.fillRect(w - 1, 1, 1, h - 2);
417: g.fillRect(1, h - 1, w - 2, 1);
418: g.translate(-x, -y);
419: }
420: }
421:
422: /**
423: * A panel with a horizontal gradient background.
424: */
425: private static final class GradientPanel extends JPanel {
426:
427: private GradientPanel(LayoutManager lm, Color background) {
428: super (lm);
429: setBackground(background);
430: }
431:
432: public void paintComponent(Graphics g) {
433: super .paintComponent(g);
434: if (!isOpaque()) {
435: return;
436: }
437: Color control = UIManager.getColor("control");
438: int width = getWidth();
439: int height = getHeight();
440:
441: Graphics2D g2 = (Graphics2D) g;
442: Paint storedPaint = g2.getPaint();
443: g2.setPaint(new GradientPaint(0, 0, getBackground(), width,
444: 0, control));
445: g2.fillRect(0, 0, width, height);
446: g2.setPaint(storedPaint);
447: }
448: }
449:
450: }
|