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 }