Source for javax.swing.plaf.basic.BasicInternalFrameTitlePane

   1: /* BasicInternalFrameTitlePane.java --
   2:    Copyright (C) 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Container;
  44: import java.awt.Dimension;
  45: import java.awt.Font;
  46: import java.awt.FontMetrics;
  47: import java.awt.Graphics;
  48: import java.awt.Insets;
  49: import java.awt.LayoutManager;
  50: import java.awt.Rectangle;
  51: import java.awt.event.ActionEvent;
  52: import java.awt.event.KeyEvent;
  53: import java.beans.PropertyChangeEvent;
  54: import java.beans.PropertyChangeListener;
  55: import java.beans.PropertyVetoException;
  56: 
  57: import javax.swing.AbstractAction;
  58: import javax.swing.Action;
  59: import javax.swing.Icon;
  60: import javax.swing.JButton;
  61: import javax.swing.JComponent;
  62: import javax.swing.JInternalFrame;
  63: import javax.swing.JLabel;
  64: import javax.swing.JMenu;
  65: import javax.swing.JMenuBar;
  66: import javax.swing.JMenuItem;
  67: import javax.swing.SwingConstants;
  68: import javax.swing.SwingUtilities;
  69: import javax.swing.UIManager;
  70: 
  71: /**
  72:  * This class acts as a titlebar for JInternalFrames.
  73:  */
  74: public class BasicInternalFrameTitlePane extends JComponent
  75: {
  76:   /**
  77:    * The Action responsible for closing the JInternalFrame.
  78:    *
  79:    * @specnote Apparently this class was intended to be protected,
  80:    *           but was made public by a compiler bug and is now
  81:    *           public for compatibility.
  82:    */
  83:   public class CloseAction extends AbstractAction
  84:   {
  85:     /**
  86:      * Creates a new action.
  87:      */
  88:     public CloseAction()
  89:     {
  90:       super("Close");
  91:     }
  92:     
  93:     /**
  94:      * This method is called when something closes the JInternalFrame.
  95:      *
  96:      * @param e The ActionEvent.
  97:      */
  98:     public void actionPerformed(ActionEvent e)
  99:     {
 100:       if (frame.isClosable())
 101:         {
 102:           try
 103:             {
 104:               frame.setClosed(true);
 105:             }
 106:           catch (PropertyVetoException pve)
 107:             {
 108:               // We do nothing if the attempt has been vetoed.
 109:             }
 110:         }
 111:     }
 112:   }
 113: 
 114:   /**
 115:    * This Action is responsible for iconifying the JInternalFrame.
 116:    *
 117:    * @specnote Apparently this class was intended to be protected,
 118:    *           but was made public by a compiler bug and is now
 119:    *           public for compatibility.
 120:    */
 121:   public class IconifyAction extends AbstractAction
 122:   {
 123:     /**
 124:      * Creates a new action.
 125:      */
 126:     public IconifyAction()
 127:     {
 128:       super("Minimize");
 129:     }
 130: 
 131:     /**
 132:      * This method is called when the user wants to iconify the
 133:      * JInternalFrame.
 134:      *
 135:      * @param e The ActionEvent.
 136:      */
 137:     public void actionPerformed(ActionEvent e)
 138:     {
 139:       if (frame.isIconifiable() && ! frame.isIcon())
 140:         {
 141:           try
 142:             {
 143:               frame.setIcon(true);
 144:             }
 145:           catch (PropertyVetoException pve)
 146:             {
 147:               // We do nothing if the attempt has been vetoed.
 148:             }
 149:         }
 150:     }
 151:   }
 152: 
 153:   /**
 154:    * This Action is responsible for maximizing the JInternalFrame.
 155:    *
 156:    * @specnote Apparently this class was intended to be protected,
 157:    *           but was made public by a compiler bug and is now
 158:    *           public for compatibility.
 159:    */
 160:   public class MaximizeAction extends AbstractAction
 161:   {
 162:     /**
 163:      * Creates a new action.
 164:      */
 165:     public MaximizeAction()
 166:     {
 167:       super("Maximize");
 168:     }
 169:     /**
 170:      * This method is called when the user wants to maximize the
 171:      * JInternalFrame.
 172:      *
 173:      * @param e The ActionEvent.
 174:      */
 175:     public void actionPerformed(ActionEvent e)
 176:     {
 177:       try
 178:         {
 179:           if (frame.isMaximizable() && ! frame.isMaximum())
 180:             frame.setMaximum(true);
 181:           else if (frame.isMaximum())
 182:             frame.setMaximum(false);
 183:         }
 184:       catch (PropertyVetoException pve)
 185:         {
 186:           // We do nothing if the attempt has been vetoed.
 187:         }
 188:     }
 189:   }
 190: 
 191:   /**
 192:    * This Action is responsible for dragging the JInternalFrame.
 193:    *
 194:    * @specnote Apparently this class was intended to be protected,
 195:    *           but was made public by a compiler bug and is now
 196:    *           public for compatibility.
 197:    */
 198:   public class MoveAction extends AbstractAction
 199:   {
 200:     /**
 201:      * Creates a new action.
 202:      */
 203:     public MoveAction()
 204:     {
 205:       super("Move");
 206:     }
 207:     /**
 208:      * This method is called when the user wants to drag the JInternalFrame.
 209:      *
 210:      * @param e The ActionEvent.
 211:      */
 212:     public void actionPerformed(ActionEvent e)
 213:     {
 214:       // FIXME: Implement keyboard driven? move actions.
 215:     }
 216:   }
 217: 
 218:   /**
 219:    * This Action is responsible for restoring the JInternalFrame. Restoring
 220:    * the JInternalFrame is the same as setting the maximum property to false.
 221:    *
 222:    * @specnote Apparently this class was intended to be protected,
 223:    *           but was made public by a compiler bug and is now
 224:    *           public for compatibility.
 225:    */
 226:   public class RestoreAction extends AbstractAction
 227:   {
 228:     /**
 229:      * Creates a new action.
 230:      */
 231:     public RestoreAction()
 232:     {
 233:       super("Restore");
 234:     }
 235:     /**
 236:      * This method is called when the user wants to restore the
 237:      * JInternalFrame.
 238:      *
 239:      * @param e The ActionEvent.
 240:      */
 241:     public void actionPerformed(ActionEvent e)
 242:     {
 243:       if (frame.isMaximum())
 244:         {
 245:           try
 246:             {
 247:               frame.setMaximum(false);
 248:             }
 249:           catch (PropertyVetoException pve)
 250:             {
 251:               // We do nothing if the attempt has been vetoed.
 252:             }
 253:         }
 254:     }
 255:   }
 256: 
 257:   /**
 258:    * This action is responsible for sizing the JInternalFrame.
 259:    *
 260:    * @specnote Apparently this class was intended to be protected,
 261:    *           but was made public by a compiler bug and is now
 262:    *           public for compatibility.
 263:    */
 264:   public class SizeAction extends AbstractAction
 265:   {
 266:     /**
 267:      * Creates a new action.
 268:      */
 269:     public SizeAction()
 270:     {
 271:       super("Size");
 272:     }
 273:     /**
 274:      * This method is called when the user wants to resize the JInternalFrame.
 275:      *
 276:      * @param e The ActionEvent.
 277:      */
 278:     public void actionPerformed(ActionEvent e)
 279:     {
 280:       // FIXME: Not sure how size actions should be handled.
 281:     }
 282:   }
 283: 
 284:   /**
 285:    * This class is responsible for handling property change events from the
 286:    * JInternalFrame and adjusting the Title Pane as necessary.
 287:    *
 288:    * @specnote Apparently this class was intended to be protected,
 289:    *           but was made public by a compiler bug and is now
 290:    *           public for compatibility.
 291:    */
 292:   public class PropertyChangeHandler implements PropertyChangeListener
 293:   {
 294:     /**
 295:      * This method is called when a PropertyChangeEvent is received by the
 296:      * Title Pane.
 297:      *
 298:      * @param evt The PropertyChangeEvent.
 299:      */
 300:     public void propertyChange(PropertyChangeEvent evt)
 301:     {
 302:       String propName = evt.getPropertyName();
 303:       if (propName.equals("closable"))
 304:         {
 305:           if (evt.getNewValue().equals(Boolean.TRUE))
 306:             closeButton.setVisible(true);
 307:           else
 308:             closeButton.setVisible(false);
 309:         }
 310:       else if (propName.equals("iconable"))
 311:         {
 312:           if (evt.getNewValue().equals(Boolean.TRUE))
 313:             iconButton.setVisible(true);
 314:           else
 315:             iconButton.setVisible(false);
 316:         }
 317:       else if (propName.equals("maximizable"))
 318:         {
 319:           if (evt.getNewValue().equals(Boolean.TRUE))
 320:             maxButton.setVisible(true);
 321:           else
 322:             maxButton.setVisible(false);
 323:         }
 324:       enableActions();
 325:     }
 326:   }
 327: 
 328:   /**
 329:    * This class acts as the MenuBar for the TitlePane. Clicking on the Frame
 330:    * Icon in the top left corner will activate it.
 331:    *
 332:    * @specnote Apparently this class was intended to be protected,
 333:    *           but was made public by a compiler bug and is now
 334:    *           public for compatibility.
 335:    */
 336:   public class SystemMenuBar extends JMenuBar
 337:   {
 338:     /**
 339:      * This method returns true if it can receive focus.
 340:      *
 341:      * @return True if this Component can receive focus.
 342:      */
 343:     public boolean isFocusTraversable()
 344:     {
 345:       return true;
 346:     }
 347: 
 348:     /**
 349:      * This method returns true if this Component is expected to paint all of
 350:      * itself.
 351:      *
 352:      * @return True if this Component is expect to paint all of itself.
 353:      */
 354:     public boolean isOpaque()
 355:     {
 356:       return true;
 357:     }
 358: 
 359:     /**
 360:      * This method paints this Component.
 361:      *
 362:      * @param g The Graphics object to paint with.
 363:      */
 364:     public void paint(Graphics g)
 365:     {
 366:       Icon frameIcon = frame.getFrameIcon();
 367:       if (frameIcon == null)
 368:     frameIcon = BasicDesktopIconUI.defaultIcon;
 369:       frameIcon.paintIcon(this, g, 0, 0);
 370:     }
 371: 
 372:     /**
 373:      * This method requests that focus be given to this Component.
 374:      */
 375:     public void requestFocus()
 376:     {
 377:       super.requestFocus();
 378:     }
 379:   }
 380: 
 381:   /**
 382:    * This class acts as the Layout Manager for the TitlePane.
 383:    *
 384:    * @specnote Apparently this class was intended to be protected,
 385:    *           but was made public by a compiler bug and is now
 386:    *           public for compatibility.
 387:    */
 388:   public class TitlePaneLayout implements LayoutManager
 389:   {
 390:     /**
 391:      * Creates a new <code>TitlePaneLayout</code> object.
 392:      */
 393:     public TitlePaneLayout()
 394:     {
 395:       // Do nothing.
 396:     }
 397: 
 398:     /**
 399:      * This method is called when adding a Component to the Container.
 400:      *
 401:      * @param name The name to reference the added Component by.
 402:      * @param c The Component to add.
 403:      */
 404:     public void addLayoutComponent(String name, Component c)
 405:     {
 406:       // Do nothing.
 407:     }
 408: 
 409:     /**
 410:      * This method is called to lay out the children of the Title Pane.
 411:      *
 412:      * @param c The Container to lay out.
 413:      */
 414:     public void layoutContainer(Container c)
 415:     {
 416:       Dimension size = c.getSize();
 417:       Insets insets = c.getInsets();
 418:       int width = size.width - insets.left - insets.right;
 419:       int height = size.height - insets.top - insets.bottom;
 420: 
 421:       // MenuBar is always present and located at the top left corner.
 422:       Dimension menupref = menuBar.getPreferredSize();
 423:       menuBar.setBounds(insets.left, insets.top, menupref.width, height);
 424: 
 425:       int loc = width + insets.left - 1;
 426:       int top = insets.top + 1;
 427:       int buttonHeight = height - 4;
 428:       if (closeButton.isVisible())
 429:         {
 430:           int buttonWidth = closeIcon.getIconWidth();
 431:           loc -= buttonWidth + 2;
 432:           closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
 433:         }
 434: 
 435:       if (maxButton.isVisible())
 436:         {
 437:           int buttonWidth = maxIcon.getIconWidth();
 438:           loc -= buttonWidth + 2;
 439:           maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
 440:         }
 441: 
 442:       if (iconButton.isVisible())
 443:         {
 444:           int buttonWidth = iconIcon.getIconWidth();
 445:           loc -= buttonWidth + 2;
 446:           iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
 447:         }
 448: 
 449:       if (title != null)
 450:     title.setBounds(insets.left + menupref.width, insets.top,
 451:                     loc - menupref.width - insets.left, height);
 452:     }
 453: 
 454:     /**
 455:      * This method returns the minimum size of the given Container given the
 456:      * children that it has.
 457:      *
 458:      * @param c The Container to get a minimum size for.
 459:      *
 460:      * @return The minimum size of the Container.
 461:      */
 462:     public Dimension minimumLayoutSize(Container c)
 463:     {
 464:       return preferredLayoutSize(c);
 465:     }
 466: 
 467:     /**
 468:      * This method returns the preferred size of the given Container taking
 469:      * into account the children that it has.
 470:      *
 471:      * @param c The Container to lay out.
 472:      *
 473:      * @return The preferred size of the Container.
 474:      */
 475:     public Dimension preferredLayoutSize(Container c)
 476:     {
 477:       return new Dimension(22, 18);
 478:     }
 479: 
 480:     /**
 481:      * This method is called when removing a Component from the Container.
 482:      *
 483:      * @param c The Component to remove.
 484:      */
 485:     public void removeLayoutComponent(Component c)
 486:     {
 487:       // Nothing to do here.
 488:     }
 489:   }
 490: 
 491:   /**
 492:    * This helper class is used to create the minimize, maximize and close
 493:    * buttons in the top right corner of the Title Pane. These buttons are
 494:    * special since they cannot be given focus and have no border.
 495:    */
 496:   private class PaneButton extends JButton
 497:   {
 498:     /**
 499:      * Creates a new PaneButton object with the given Action.
 500:      *
 501:      * @param a The Action that the button uses.
 502:      */
 503:     public PaneButton(Action a)
 504:     {
 505:       super(a);
 506:       setMargin(new Insets(0, 0, 0, 0));
 507:     }
 508: 
 509:     /**
 510:      * This method returns true if the Component can be focused.
 511:      *
 512:      * @return false.
 513:      */
 514:     public boolean isFocusable()
 515:     {
 516:       // These buttons cannot be given focus.
 517:       return false;
 518:     }
 519: 
 520:   }
 521: 
 522:   /** The action command for the Close action. */
 523:   protected static final String CLOSE_CMD;
 524: 
 525:   /** The action command for the Minimize action. */
 526:   protected static final String ICONIFY_CMD;
 527: 
 528:   /** The action command for the Maximize action. */
 529:   protected static final String MAXIMIZE_CMD;
 530: 
 531:   /** The action command for the Move action. */
 532:   protected static final String MOVE_CMD;
 533: 
 534:   /** The action command for the Restore action. */
 535:   protected static final String RESTORE_CMD;
 536: 
 537:   /** The action command for the Size action. */
 538:   protected static final String SIZE_CMD;
 539: 
 540:   /** The action associated with closing the JInternalFrame. */
 541:   protected Action closeAction;
 542: 
 543:   /** The action associated with iconifying the JInternalFrame. */
 544:   protected Action iconifyAction;
 545: 
 546:   /** The action associated with maximizing the JInternalFrame. */
 547:   protected Action maximizeAction;
 548: 
 549:   /** The action associated with moving the JInternalFrame. */
 550:   protected Action moveAction;
 551: 
 552:   /** The action associated with restoring the JInternalFrame. */
 553:   protected Action restoreAction;
 554: 
 555:   /** The action associated with resizing the JInternalFrame. */
 556:   protected Action sizeAction;
 557: 
 558:   /** The button that closes the JInternalFrame. */
 559:   protected JButton closeButton;
 560: 
 561:   /** The button that iconifies the JInternalFrame. */
 562:   protected JButton iconButton;
 563: 
 564:   /** The button that maximizes the JInternalFrame. */
 565:   protected JButton maxButton;
 566: 
 567:   /** The icon displayed in the restore button. */
 568:   protected Icon minIcon = BasicIconFactory.createEmptyFrameIcon();
 569: 
 570:   /** The icon displayed in the maximize button. */
 571:   protected Icon maxIcon = BasicIconFactory.createEmptyFrameIcon();
 572: 
 573:   /** The icon displayed in the iconify button. */
 574:   protected Icon iconIcon = BasicIconFactory.createEmptyFrameIcon();
 575: 
 576:   /** The icon displayed in the close button. */
 577:   protected Icon closeIcon;
 578:   
 579:   /** The JInternalFrame that this TitlePane is used in. */
 580:   protected JInternalFrame frame;
 581: 
 582:   /** The JMenuBar that is located at the top left of the Title Pane. */
 583:   protected JMenuBar menuBar;
 584: 
 585:   /** The JMenu inside the menuBar. */
 586:   protected JMenu windowMenu;
 587: 
 588:   /**
 589:    * The text color of the TitlePane when the JInternalFrame is not selected.
 590:    */
 591:   protected Color notSelectedTextColor;
 592: 
 593:   /**
 594:    * The background color of the TitlePane when the JInternalFrame is not
 595:    * selected.
 596:    */
 597:   protected Color notSelectedTitleColor;
 598: 
 599:   /** The text color of the titlePane when the JInternalFrame is selected. */
 600:   protected Color selectedTextColor;
 601: 
 602:   /**
 603:    * The background color of the TitlePane when the JInternalFrame is
 604:    * selected.
 605:    */
 606:   protected Color selectedTitleColor;
 607: 
 608:   /** The Property Change listener that listens to the JInternalFrame. */
 609:   protected PropertyChangeListener propertyChangeListener;
 610: 
 611:   /**
 612:    * The label used to display the title. This label is not added to the
 613:    * TitlePane.
 614:    * This is package-private to avoid an accessor method.
 615:    */
 616:   transient JLabel title;
 617:   
 618:   static
 619:     {
 620:       // not constants in JDK
 621:       CLOSE_CMD = "Close";
 622:       ICONIFY_CMD = "Minimize";
 623:       MAXIMIZE_CMD = "Maximize";
 624:       MOVE_CMD = "Move";
 625:       RESTORE_CMD = "Restore";
 626:       SIZE_CMD = "Size";
 627:     }
 628: 
 629:   /**
 630:    * Creates a new BasicInternalFrameTitlePane object that is used in the
 631:    * given JInternalFrame.
 632:    *
 633:    * @param f The JInternalFrame this BasicInternalFrameTitlePane will be used
 634:    *        in.
 635:    */
 636:   public BasicInternalFrameTitlePane(JInternalFrame f)
 637:   {
 638:     frame = f;
 639:     setLayout(createLayout());
 640:     title = new JLabel();
 641:     title.setHorizontalAlignment(SwingConstants.LEFT);
 642:     title.setHorizontalTextPosition(SwingConstants.LEFT);
 643:     title.setOpaque(false);
 644:     setOpaque(true);
 645: 
 646:     setBackground(Color.LIGHT_GRAY);
 647:     setOpaque(true);
 648: 
 649:     installTitlePane();
 650:   }
 651: 
 652:   /**
 653:    * This method installs the TitlePane onto the JInternalFrameTitlePane. It
 654:    * also creates any children components that need to be created and adds
 655:    * listeners to the appropriate components.
 656:    */
 657:   protected void installTitlePane()
 658:   {
 659:     installDefaults();
 660:     installListeners();
 661:     createActions();
 662: 
 663:     assembleSystemMenu();
 664: 
 665:     createButtons();
 666:     setButtonIcons();
 667:     addSubComponents();
 668:     enableActions();
 669:   }
 670: 
 671:   /**
 672:    * This method adds the sub components to the TitlePane.
 673:    */
 674:   protected void addSubComponents()
 675:   {
 676:     add(menuBar);
 677: 
 678:     add(closeButton);
 679:     add(iconButton);
 680:     add(maxButton);
 681:   }
 682: 
 683:   /**
 684:    * This method creates the actions that are used to manipulate the
 685:    * JInternalFrame.
 686:    */
 687:   protected void createActions()
 688:   {
 689:     closeAction = new CloseAction();
 690:     closeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, CLOSE_CMD);
 691: 
 692:     iconifyAction = new IconifyAction();
 693:     iconifyAction.putValue(AbstractAction.ACTION_COMMAND_KEY, ICONIFY_CMD);
 694: 
 695:     maximizeAction = new MaximizeAction();
 696:     maximizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MAXIMIZE_CMD);
 697: 
 698:     sizeAction = new SizeAction();
 699:     sizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, SIZE_CMD);
 700: 
 701:     restoreAction = new RestoreAction();
 702:     restoreAction.putValue(AbstractAction.ACTION_COMMAND_KEY, RESTORE_CMD);
 703: 
 704:     moveAction = new MoveAction();
 705:     moveAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MOVE_CMD);
 706:   }
 707: 
 708:   /**
 709:    * This method is used to install the listeners.
 710:    */
 711:   protected void installListeners()
 712:   {
 713:     propertyChangeListener = createPropertyChangeListener();
 714:     frame.addPropertyChangeListener(propertyChangeListener);
 715:   }
 716: 
 717:   /**
 718:    * This method is used to uninstall the listeners.
 719:    */
 720:   protected void uninstallListeners()
 721:   {
 722:     frame.removePropertyChangeListener(propertyChangeListener);
 723:     propertyChangeListener = null;
 724:   }
 725: 
 726:   /**
 727:    * This method installs the defaults determined by the look and feel.
 728:    */
 729:   protected void installDefaults()
 730:   {
 731:     title.setFont(UIManager.getFont("InternalFrame.titleFont"));
 732:     selectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
 733:     selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
 734:     notSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
 735:     notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
 736:   
 737:     closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
 738:     iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
 739:     maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
 740:   }
 741: 
 742:   /**
 743:    * This method uninstalls the defaults.
 744:    */
 745:   protected void uninstallDefaults()
 746:   {
 747:     setFont(null);
 748:     selectedTextColor = null;
 749:     selectedTitleColor = null;
 750:     notSelectedTextColor = null;
 751:     notSelectedTitleColor = null;
 752:     
 753:     closeIcon = null;
 754:     iconIcon = null;
 755:     maxIcon = null;
 756:   }
 757: 
 758:   /**
 759:    * This method creates the buttons used in the TitlePane.
 760:    */
 761:   protected void createButtons()
 762:   {
 763:     closeButton = new PaneButton(closeAction);
 764:     closeButton.setText(null);
 765:     if (!frame.isClosable())
 766:       closeButton.setVisible(false);
 767:     iconButton = new PaneButton(iconifyAction);
 768:     iconButton.setText(null);
 769:     if (!frame.isIconifiable())
 770:       iconButton.setVisible(false);
 771:     maxButton = new PaneButton(maximizeAction);
 772:     maxButton.setText(null);
 773:     if (!frame.isMaximizable())
 774:       maxButton.setVisible(false);
 775:   }
 776: 
 777:   /**
 778:    * Set icons for the minimize-, maximize- and close-buttons.
 779:    */
 780:   protected void setButtonIcons()
 781:   {
 782:     if (closeIcon != null && closeButton != null)
 783:       closeButton.setIcon(closeIcon);
 784:     if (iconIcon != null && iconButton != null)
 785:       iconButton.setIcon(iconIcon);
 786:     if (maxIcon != null && maxButton != null)
 787:       maxButton.setIcon(maxIcon);
 788:   }
 789: 
 790:   /**
 791:    * This method creates the MenuBar used in the TitlePane.
 792:    */
 793:   protected void assembleSystemMenu()
 794:   {
 795:     menuBar = createSystemMenuBar();
 796:     windowMenu = createSystemMenu();
 797: 
 798:     menuBar.add(windowMenu);
 799: 
 800:     addSystemMenuItems(windowMenu);
 801:     enableActions();
 802:   }
 803: 
 804:   /**
 805:    * This method adds the MenuItems to the given JMenu.
 806:    *
 807:    * @param systemMenu The JMenu to add MenuItems to.
 808:    */
 809:   protected void addSystemMenuItems(JMenu systemMenu)
 810:   {
 811:     JMenuItem tmp;
 812: 
 813:     tmp = new JMenuItem(RESTORE_CMD);
 814:     tmp.addActionListener(restoreAction);
 815:     tmp.setMnemonic(KeyEvent.VK_R);
 816:     systemMenu.add(tmp);
 817: 
 818:     tmp = new JMenuItem(MOVE_CMD);
 819:     tmp.addActionListener(moveAction);
 820:     tmp.setMnemonic(KeyEvent.VK_M);
 821:     systemMenu.add(tmp);
 822: 
 823:     tmp = new JMenuItem(SIZE_CMD);
 824:     tmp.addActionListener(sizeAction);
 825:     tmp.setMnemonic(KeyEvent.VK_S);
 826:     systemMenu.add(tmp);
 827: 
 828:     tmp = new JMenuItem(ICONIFY_CMD);
 829:     tmp.addActionListener(iconifyAction);
 830:     tmp.setMnemonic(KeyEvent.VK_N);
 831:     systemMenu.add(tmp);
 832: 
 833:     tmp = new JMenuItem(MAXIMIZE_CMD);
 834:     tmp.addActionListener(maximizeAction);
 835:     tmp.setMnemonic(KeyEvent.VK_X);
 836:     systemMenu.add(tmp);
 837: 
 838:     systemMenu.addSeparator();
 839: 
 840:     tmp = new JMenuItem(CLOSE_CMD);
 841:     tmp.addActionListener(closeAction);
 842:     tmp.setMnemonic(KeyEvent.VK_C);
 843:     systemMenu.add(tmp);
 844:   }
 845: 
 846:   /**
 847:    * This method creates a new JMenubar.
 848:    *
 849:    * @return A new JMenuBar.
 850:    */
 851:   protected JMenuBar createSystemMenuBar()
 852:   {
 853:     if (menuBar == null)
 854:       menuBar = new SystemMenuBar();
 855:     menuBar.removeAll();
 856:     return menuBar;
 857:   }
 858: 
 859:   /**
 860:    * This method creates a new JMenu.
 861:    *
 862:    * @return A new JMenu.
 863:    */
 864:   protected JMenu createSystemMenu()
 865:   {
 866:     if (windowMenu == null)
 867:       windowMenu = new JMenu();
 868:     windowMenu.removeAll();
 869:     return windowMenu;
 870:   }
 871: 
 872:   /**
 873:    * This method programmatically shows the JMenu.
 874:    */
 875:   protected void showSystemMenu()
 876:   {
 877:     // FIXME: Untested as KeyEvents are not hooked up.
 878:     menuBar.getMenu(1).getPopupMenu().show();
 879:   }
 880: 
 881:   /**
 882:    * This method paints the TitlePane.
 883:    *
 884:    * @param g The Graphics object to paint with.
 885:    */
 886:   public void paintComponent(Graphics g)
 887:   {
 888:     paintTitleBackground(g);
 889:     if (frame.getTitle() != null && title != null)
 890:       {
 891:     Color saved = g.getColor();
 892:         Font f = title.getFont();
 893:         g.setFont(f);
 894:         FontMetrics fm = g.getFontMetrics(f);
 895:     if (frame.isSelected())
 896:       g.setColor(selectedTextColor);
 897:     else
 898:       g.setColor(notSelectedTextColor);
 899:     title.setText(getTitle(frame.getTitle(), fm, title.getBounds().width));
 900:     SwingUtilities.paintComponent(g, title, null, title.getBounds());
 901:     g.setColor(saved);
 902:       }
 903:   }
 904: 
 905:   /**
 906:    * This method paints the TitlePane's background.
 907:    *
 908:    * @param g The Graphics object to paint with.
 909:    */
 910:   protected void paintTitleBackground(Graphics g)
 911:   {
 912:     if (!isOpaque())
 913:       return;
 914: 
 915:     Color saved = g.getColor();
 916:     Dimension dims = getSize();
 917: 
 918:     Color bg = getBackground();
 919:     if (frame.isSelected())
 920:       bg = selectedTitleColor;
 921:     else
 922:       bg = notSelectedTitleColor;
 923:     g.setColor(bg);
 924:     g.fillRect(0, 0, dims.width, dims.height);
 925:     g.setColor(saved);
 926:   }
 927: 
 928:   /**
 929:    * This method returns the title string based on the available width and the
 930:    * font metrics.
 931:    *
 932:    * @param text The desired title.
 933:    * @param fm The FontMetrics of the font used.
 934:    * @param availableWidth The available width.
 935:    *
 936:    * @return The allowable string.
 937:    */
 938:   protected String getTitle(String text, FontMetrics fm, int availableWidth)
 939:   {
 940:     Rectangle vr = new Rectangle(0, 0, availableWidth, fm.getHeight());
 941:     Rectangle ir = new Rectangle();
 942:     Rectangle tr = new Rectangle();
 943:     String value = SwingUtilities.layoutCompoundLabel(this, fm, text, null,
 944:                                                       SwingConstants.CENTER,
 945:                                                       SwingConstants.LEFT,
 946:                                                       SwingConstants.CENTER,
 947:                                                       SwingConstants.LEFT, vr,
 948:                                                       ir, tr, 0);
 949:     return value;
 950:   }
 951: 
 952:   /**
 953:    * This method fires something similar to a WINDOW_CLOSING event.
 954:    *
 955:    * @param frame The JInternalFrame that is being closed.
 956:    */
 957:   protected void postClosingEvent(JInternalFrame frame)
 958:   {
 959:     // FIXME: Implement postClosingEvent when I figure out what
 960:     // it's supposed to do.
 961:     // It says that this fires an WINDOW_CLOSING like event. 
 962:     // So the closest thing is some kind of InternalFrameEvent.
 963:     // But none is fired.
 964:     // Can't see it called or anything.
 965:   }
 966: 
 967:   /**
 968:    * This method enables the actions for the TitlePane given the frame's
 969:    * properties.
 970:    */
 971:   protected void enableActions()
 972:   {
 973:     closeAction.setEnabled(frame.isClosable());
 974: 
 975:     iconifyAction.setEnabled(frame.isIconifiable());
 976:     // The maximize action is responsible for restoring it
 977:     // as well, if clicked from the button
 978:     maximizeAction.setEnabled(frame.isMaximizable());
 979: 
 980:     // The restoring action is only active when selected
 981:     // from the menu.
 982:     restoreAction.setEnabled(frame.isMaximum());
 983: 
 984:     sizeAction.setEnabled(frame.isResizable());
 985: 
 986:     // FIXME: Tie MoveAction enabled status to a variable.
 987:     moveAction.setEnabled(false);
 988:   }
 989: 
 990:   /**
 991:    * This method creates a new PropertyChangeListener.
 992:    *
 993:    * @return A new PropertyChangeListener.
 994:    */
 995:   protected PropertyChangeListener createPropertyChangeListener()
 996:   {
 997:     return new PropertyChangeHandler();
 998:   }
 999: 
1000:   /**
1001:    * This method creates a new LayoutManager for the TitlePane.
1002:    *
1003:    * @return A new LayoutManager.
1004:    */
1005:   protected LayoutManager createLayout()
1006:   {
1007:     return new TitlePaneLayout();
1008:   }
1009: }