gt:         if (titleLabel != null) updateHeader();
context: // BlogBridge -- RSS feed reader, manager, and web based service
  // Copyright (C) 2002-2006 by R. Pito Salas
  //
  // This program is free software; you can redistribute it and/or modify it under
  // the terms of the GNU General Public License as published by the Free Software Foundation;
  // either version 2 of the License, or (at your option) any later version.
  //
  // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  // See the GNU General Public License for more details.
  //
  // You should have received a copy of the GNU General Public License along with this program;
  // if not, write to the Free Software Foundation, Inc., 59 Temple Place,
  // Suite 330, Boston, MA 02111-1307 USA
  //
  // Contact: R. Pito Salas
  // mailto:pitosalas@users.sourceforge.net
  // More information: about BlogBridge
  // http://www.blogbridge.com
  // http://sourceforge.net/projects/blogbridge
  //
  // $Id: CoolInternalFrame.java,v 1.25 2006/01/08 05:07:31 kyank Exp $
  // 
  
  package com.salas.bb.utils.uif;
  
  import com.jgoodies.forms.layout.CellConstraints;
  import com.jgoodies.forms.layout.FormLayout;
  import com.jgoodies.uif.util.SystemUtils;
  
  import javax.swing.*;
  import java.awt.*;
  
  /**
   * A header and a body in a nice shaded frame.
   * 
   * The body is any component.
   * The header is constructed from a title, subtitle and header control component, which are
   * laid out in two rows. The Title is in the top left of the header, and has a bigger font. The
   * control component is in the top right. And the subtitle spans the whole bottom of the header,
   * in a smaller font.
   */
  public class CoolInternalFrame extends JPanel
  {
      private static final int TITLE_FONT_SIZE            = 12;
      private static final int TITLE_FONT_STYLE           = Font.BOLD;
      private static final String TITLE_FONT_FACE_MAC     = "Franklin Gothic Medium";
      private static final String TITLE_FONT_FACE         = "Sans Serif";
  
      private static final Color COLOR_BORDER             = Color.decode("#797979");
      private static final Color COLOR_GRADIENT_TOP       = Color.decode("#0c98c2");
      private static final Color COLOR_GRADIENT_BOTTOM    = Color.decode("#086091");
      private static final Color COLOR_TITLE_SHADOW       = Color.decode("#000040");
  
      private ShadowLabel     titleLabel;
      private JLabel          subtitleLabel;
      private JPanel          headerPanel;
      private JComponent      headerCtrlComp;
      private boolean         isSelected;
  
      // Instance Creation ****************************************************
  
      /**
       * Constructs a <code>CoolInternalPane</code> with the specified title.
       *
       * @param title the initial title
       */
      public CoolInternalFrame(String title)
      {
          this(title, null, null, null);
      }
  
      /**
       * Constructs a <code>CoolInternalPane</code> with the specified title, tool bar, and content
       * panel.
       *
       * @param title       initial title.
       * @param subtitle    initial subtitle.
       * @param hdrControls initial header controls component.
       * @param content     initial content pane.
       */
      public CoolInternalFrame(String title, String subtitle, JComponent hdrControls,
          JComponent content)
      {
          this(null, title, subtitle, hdrControls, content);
      }
  
      /**
       * Constructs a <code>CoolInternalPane</code> with the specified parameters.
       *
       * @param icon        initial icon.
       * @param title       initial title.
       * @param subtitle    initial subtitle.
       * @param hdrControls initial header controls component.
       * @param content     initial content pane.
       */
      public CoolInternalFrame(Icon icon, String title, String subtitle, JComponent hdrControls,
          JComponent content)
      {
          super(new BorderLayout());
          isSelected = false;
  
          // Build the labels
          titleLabel = new ShadowLabel(title, icon, SwingConstants.LEADING, COLOR_TITLE_SHADOW);
          subtitleLabel = new JLabel(subtitle);
          setupLabels();
  
          // Build the header and add it to the whole frame
          headerCtrlComp = hdrControls;
          JPanel header = buildHeader();
          add(header, BorderLayout.NORTH);
          
          // If there is a content Component, then add it
          if (content != null) setContent(content);
  
          // Final steps
          setBorder(BorderFactory.createLineBorder(COLOR_BORDER));
          setSelected(true);
          updateHeader();
      }
  
      // Public API ***********************************************************
  
      /**
       * Returns the frame's icon.
       *
       * @return the frame's icon.
       */
      public Icon getFrameIcon()
      {
          return titleLabel.getIcon();
      }
  
      /**
       * Sets a new frame icon.
       *
       * @param newIcon the icon to be set.
       */
      public void setFrameIcon(Icon newIcon)
      {
          Icon oldIcon = getFrameIcon();
          titleLabel.setIcon(newIcon);
          firePropertyChange("frameIcon", oldIcon, newIcon);
      }
  
      /**
       * Returns the frame's title text.
       *
       * @return String the current title text.
       */
      public String getTitle()
      {
          return titleLabel.getText();
      }
  
      /**
       * Sets a new title text.
       *
       * @param newText the title text to be set.
       */
      public void setTitle(String newText)
      {
          String oldText = getTitle();
          titleLabel.setText(newText);
          firePropertyChange("title", oldText, newText);
      }
  
      /**
       * Sets the subtitle text.
       *
       * @param newText title text to be set.
       */
      public void setSubtitle(String newText)
      {
          String oldText = getSubTitle();
          subtitleLabel.setText(newText);
          firePropertyChange("title", oldText, newText);
      }
  
      /**
       * Returns subtitle text.
       *
       * @return text.
       */
      private String getSubTitle()
      {
          return subtitleLabel.getText();
      }
  
      /**
       * Returns the current header controls component, null if none has been set before.
       *
       * @return the current header control component (if any).
       */
      public JComponent getHeaderCtrlComp()
      {
          return headerCtrlComp;
      }
  
      /**
       * Sets a new header control component.
       *
       * @param newhdrContrlComp the header control component to be set in the header.
       */
      public void setHeaderControl(JComponent newhdrContrlComp)
      {
          JComponent oldhdrContrlComp = getHeaderCtrlComp();
          if (oldhdrContrlComp == newhdrContrlComp) return;
  
          if (oldhdrContrlComp != null)
          {
              headerPanel.remove(oldhdrContrlComp);
          }
  
          if (newhdrContrlComp != null)
          {
              newhdrContrlComp.setBorder(null);
              CellConstraints cc = new CellConstraints();
                  headerPanel.add(newhdrContrlComp, cc.xywh(5, 2, 1, 3, "r, c"));
          }
          headerCtrlComp = newhdrContrlComp;
          updateHeader();
          firePropertyChange("toolBar", oldhdrContrlComp, newhdrContrlComp);
      }
  
      /**
       * Returns the content or null, if none has been set.
       *
       * @return the current content.
       */
      public Component getContent()
      {
          return hasContent() ? getComponent(1) : null;
      }
  
      /**
       * Sets a new panel content; replaces any existing content, if existing.
       *
       * @param newContent the panel's new content.
       */
      public void setContent(Component newContent)
      {
          Component oldContent = getContent();
          if (hasContent()) remove(oldContent);
          add(newContent, BorderLayout.CENTER);
          firePropertyChange("content", oldContent, newContent);
      }
  
      /**
       * Answers if the panel is currently selected (or in other words active) or not. In the selected
       * state, the header background will be rendered differently.
       *
       * @return boolean a boolean, where true means the frame is selected (currently active) and
       *         false means it is not.
       */
      public boolean isSelected()
      {
          return isSelected;
      }
  
      /**
       * This panel draws its title bar differently if it is selected, which may be used to indicate
       * to the user that this panel has the focus, or should get more attention than other simple
       * internal frames.
       *
       * @param newValue a boolean, where true means the frame is selected (currently active) and
       *                 false means it is not.
       */
      public void setSelected(boolean newValue)
      {
          boolean oldValue = isSelected();
          isSelected = newValue;
          updateHeader();
          firePropertyChange("selected", oldValue, newValue);
      }
  
      // Building *************************************************************
  
      /**
       * Creates the appropriately laied out Header for the CoolInternalFrame.
       *
       * @return the header.
       */
      private JPanel buildHeader()
      {
          FormLayout layout = new FormLayout(
              "11px, pref, min:grow, 2px, pref, 11px",
              "6px, pref, 1px, pref, 8px");
          headerPanel = new GradientPanel(layout);
  
          CellConstraints cc = new CellConstraints();
          headerPanel.add(titleLabel, cc.xy(2, 2));
          headerPanel.add(subtitleLabel, cc.xyw(2, 4, 3));
          if (headerCtrlComp != null) headerPanel.add(headerCtrlComp, cc.xywh(5, 2, 1, 3, "r, c"));
  
          headerPanel.setOpaque(SystemUtils.IS_OS_MAC);
          return headerPanel;
      }
  
      /**
       * Set up the desired forns and opacities for the two labels.
       */
      private void setupLabels()
      {
          titleLabel.setOpaque(false);
          titleLabel.setFont(new Font(TITLE_FONT_FACE, TITLE_FONT_STYLE, TITLE_FONT_SIZE));
  
          subtitleLabel.setOpaque(false);
          subtitleLabel.setFont(subtitleLabel.getFont().deriveFont(9.0f));
      }
  
      /**
       * Updates the header.
       */
      private void updateHeader()
      {
          headerPanel.setOpaque(isSelected());
          titleLabel.setForeground(getTextForeground(isSelected()));
          subtitleLabel.setForeground(getSubTitleForeground(isSelected()));
  
          if (headerCtrlComp != null)
          {
              headerCtrlComp.setOpaque(false);
              headerCtrlComp.setBackground(Color.RED);
          }
  
          headerPanel.repaint();
      }
  
      /**
       * Returns sub-title foreground color.
       *
       * @param selected if the sub-title is selected.
       *
       * @return color.
       */
      protected Color getSubTitleForeground(boolean selected)
      {
          return getTextForeground(selected);
      }
  
      /**
       * Updates the UI. In addition to the superclass behavior, we need to update the header
       * component.
       */
      public void updateUI()
      {
          super.updateUI();
  