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