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("iconifiable"))
 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:     
 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 isFocusTransversable()
 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 = "Close";
 524: 
 525:   /** The action command for the Minimize action. */
 526:   protected static final String ICONIFY_CMD = "Minimize";
 527: 
 528:   /** The action command for the Maximize action. */
 529:   protected static final String MAXIMIZE_CMD = "Maximize";
 530: 
 531:   /** The action command for the Move action. */
 532:   protected static final String MOVE_CMD = "Move";
 533: 
 534:   /** The action command for the Restore action. */
 535:   protected static final String RESTORE_CMD = "Restore";
 536: 
 537:   /** The action command for the Size action. */
 538:   protected static final String SIZE_CMD = "Size";
 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:   /**
 619:    * Creates a new BasicInternalFrameTitlePane object that is used in the
 620:    * given JInternalFrame.
 621:    *
 622:    * @param f The JInternalFrame this BasicInternalFrameTitlePane will be used
 623:    *        in.
 624:    */
 625:   public BasicInternalFrameTitlePane(JInternalFrame f)
 626:   {
 627:     frame = f;
 628:     setLayout(createLayout());
 629:     title = new JLabel();
 630:     title.setHorizontalAlignment(SwingConstants.LEFT);
 631:     title.setHorizontalTextPosition(SwingConstants.LEFT);
 632:     title.setOpaque(false);
 633:     setOpaque(true);
 634: 
 635:     setBackground(Color.LIGHT_GRAY);
 636:     setOpaque(true);
 637: 
 638:     installTitlePane();
 639:   }
 640: 
 641:   /**
 642:    * This method installs the TitlePane onto the JInternalFrameTitlePane. It
 643:    * also creates any children components that need to be created and adds
 644:    * listeners to the appropriate components.
 645:    */
 646:   protected void installTitlePane()
 647:   {
 648:     installDefaults();
 649:     installListeners();
 650:     createActions();
 651: 
 652:     assembleSystemMenu();
 653: 
 654:     createButtons();
 655:     setButtonIcons();
 656:     addSubComponents();
 657:     enableActions();
 658:   }
 659: 
 660:   /**
 661:    * This method adds the sub components to the TitlePane.
 662:    */
 663:   protected void addSubComponents()
 664:   {
 665:     add(menuBar);
 666: 
 667:     add(closeButton);
 668:     add(iconButton);
 669:     add(maxButton);
 670:   }
 671: 
 672:   /**
 673:    * This method creates the actions that are used to manipulate the
 674:    * JInternalFrame.
 675:    */
 676:   protected void createActions()
 677:   {
 678:     closeAction = new CloseAction();
 679:     closeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, CLOSE_CMD);
 680: 
 681:     iconifyAction = new IconifyAction();
 682:     iconifyAction.putValue(AbstractAction.ACTION_COMMAND_KEY, ICONIFY_CMD);
 683: 
 684:     maximizeAction = new MaximizeAction();
 685:     maximizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MAXIMIZE_CMD);
 686: 
 687:     sizeAction = new SizeAction();
 688:     sizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, SIZE_CMD);
 689: 
 690:     restoreAction = new RestoreAction();
 691:     restoreAction.putValue(AbstractAction.ACTION_COMMAND_KEY, RESTORE_CMD);
 692: 
 693:     moveAction = new MoveAction();
 694:     moveAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MOVE_CMD);
 695:   }
 696: 
 697:   /**
 698:    * This method is used to install the listeners.
 699:    */
 700:   protected void installListeners()
 701:   {
 702:     propertyChangeListener = createPropertyChangeListener();
 703:     frame.addPropertyChangeListener(propertyChangeListener);
 704:   }
 705: 
 706:   /**
 707:    * This method is used to uninstall the listeners.
 708:    */
 709:   protected void uninstallListeners()
 710:   {
 711:     frame.removePropertyChangeListener(propertyChangeListener);
 712:     propertyChangeListener = null;
 713:   }
 714: 
 715:   /**
 716:    * This method installs the defaults determined by the look and feel.
 717:    */
 718:   protected void installDefaults()
 719:   {
 720:     title.setFont(UIManager.getFont("InternalFrame.titleFont"));
 721:     selectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
 722:     selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
 723:     notSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
 724:     notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
 725:   
 726:     closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
 727:     iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
 728:     maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
 729:   }
 730: 
 731:   /**
 732:    * This method uninstalls the defaults.
 733:    */
 734:   protected void uninstallDefaults()
 735:   {
 736:     setFont(null);
 737:     selectedTextColor = null;
 738:     selectedTitleColor = null;
 739:     notSelectedTextColor = null;
 740:     notSelectedTitleColor = null;
 741:     
 742:     closeIcon = null;
 743:     iconIcon = null;
 744:     maxIcon = null;
 745:   }
 746: 
 747:   /**
 748:    * This method creates the buttons used in the TitlePane.
 749:    */
 750:   protected void createButtons()
 751:   {
 752:     closeButton = new PaneButton(closeAction);
 753:     closeButton.setText(null);
 754:     if (!frame.isClosable())
 755:       closeButton.setVisible(false);
 756:     iconButton = new PaneButton(iconifyAction);
 757:     iconButton.setText(null);
 758:     if (!frame.isIconifiable())
 759:       iconButton.setVisible(false);
 760:     maxButton = new PaneButton(maximizeAction);
 761:     maxButton.setText(null);
 762:     if (!frame.isMaximizable())
 763:       maxButton.setVisible(false);
 764:   }
 765: 
 766:   /**
 767:    * Set icons for the minimize-, maximize- and close-buttons.
 768:    */
 769:   protected void setButtonIcons()
 770:   {
 771:     if (closeIcon != null && closeButton != null)
 772:       closeButton.setIcon(closeIcon);
 773:     if (iconIcon != null && iconButton != null)
 774:       iconButton.setIcon(iconIcon);
 775:     if (maxIcon != null && maxButton != null)
 776:       maxButton.setIcon(maxIcon);
 777:   }
 778: 
 779:   /**
 780:    * This method creates the MenuBar used in the TitlePane.
 781:    */
 782:   protected void assembleSystemMenu()
 783:   {
 784:     menuBar = createSystemMenuBar();
 785:     windowMenu = createSystemMenu();
 786: 
 787:     menuBar.add(windowMenu);
 788: 
 789:     addSystemMenuItems(windowMenu);
 790:     enableActions();
 791:   }
 792: 
 793:   /**
 794:    * This method adds the MenuItems to the given JMenu.
 795:    *
 796:    * @param systemMenu The JMenu to add MenuItems to.
 797:    */
 798:   protected void addSystemMenuItems(JMenu systemMenu)
 799:   {
 800:     JMenuItem tmp;
 801: 
 802:     tmp = new JMenuItem(RESTORE_CMD);
 803:     tmp.addActionListener(restoreAction);
 804:     tmp.setMnemonic(KeyEvent.VK_R);
 805:     systemMenu.add(tmp);
 806: 
 807:     tmp = new JMenuItem(MOVE_CMD);
 808:     tmp.addActionListener(moveAction);
 809:     tmp.setMnemonic(KeyEvent.VK_M);
 810:     systemMenu.add(tmp);
 811: 
 812:     tmp = new JMenuItem(SIZE_CMD);
 813:     tmp.addActionListener(sizeAction);
 814:     tmp.setMnemonic(KeyEvent.VK_S);
 815:     systemMenu.add(tmp);
 816: 
 817:     tmp = new JMenuItem(ICONIFY_CMD);
 818:     tmp.addActionListener(iconifyAction);
 819:     tmp.setMnemonic(KeyEvent.VK_N);
 820:     systemMenu.add(tmp);
 821: 
 822:     tmp = new JMenuItem(MAXIMIZE_CMD);
 823:     tmp.addActionListener(maximizeAction);
 824:     tmp.setMnemonic(KeyEvent.VK_X);
 825:     systemMenu.add(tmp);
 826: 
 827:     systemMenu.addSeparator();
 828: 
 829:     tmp = new JMenuItem(CLOSE_CMD);
 830:     tmp.addActionListener(closeAction);
 831:     tmp.setMnemonic(KeyEvent.VK_C);
 832:     systemMenu.add(tmp);
 833:   }
 834: 
 835:   /**
 836:    * This method creates a new JMenubar.
 837:    *
 838:    * @return A new JMenuBar.
 839:    */
 840:   protected JMenuBar createSystemMenuBar()
 841:   {
 842:     if (menuBar == null)
 843:       menuBar = new SystemMenuBar();
 844:     menuBar.removeAll();
 845:     return menuBar;
 846:   }
 847: 
 848:   /**
 849:    * This method creates a new JMenu.
 850:    *
 851:    * @return A new JMenu.
 852:    */
 853:   protected JMenu createSystemMenu()
 854:   {
 855:     if (windowMenu == null)
 856:       windowMenu = new JMenu();
 857:     windowMenu.removeAll();
 858:     return windowMenu;
 859:   }
 860: 
 861:   /**
 862:    * This method programmatically shows the JMenu.
 863:    */
 864:   protected void showSystemMenu()
 865:   {
 866:     // FIXME: Untested as KeyEvents are not hooked up.
 867:     menuBar.getMenu(1).getPopupMenu().show();
 868:   }
 869: 
 870:   /**
 871:    * This method paints the TitlePane.
 872:    *
 873:    * @param g The Graphics object to paint with.
 874:    */
 875:   public void paintComponent(Graphics g)
 876:   {
 877:     paintTitleBackground(g);
 878:     if (frame.getTitle() != null && title != null)
 879:       {
 880:     Color saved = g.getColor();
 881:         Font f = title.getFont();
 882:         g.setFont(f);
 883:         FontMetrics fm = g.getFontMetrics(f);
 884:     if (frame.isSelected())
 885:       g.setColor(selectedTextColor);
 886:     else
 887:       g.setColor(notSelectedTextColor);
 888:     title.setText(getTitle(frame.getTitle(), fm, title.getBounds().width));
 889:     SwingUtilities.paintComponent(g, title, null, title.getBounds());
 890:     g.setColor(saved);
 891:       }
 892:   }
 893: 
 894:   /**
 895:    * This method paints the TitlePane's background.
 896:    *
 897:    * @param g The Graphics object to paint with.
 898:    */
 899:   protected void paintTitleBackground(Graphics g)
 900:   {
 901:     if (!isOpaque())
 902:       return;
 903: 
 904:     Color saved = g.getColor();
 905:     Dimension dims = getSize();
 906: 
 907:     Color bg = getBackground();
 908:     if (frame.isSelected())
 909:       bg = selectedTitleColor;
 910:     else
 911:       bg = notSelectedTitleColor;
 912:     g.setColor(bg);
 913:     g.fillRect(0, 0, dims.width, dims.height);
 914:     g.setColor(saved);
 915:   }
 916: 
 917:   /**
 918:    * This method returns the title string based on the available width and the
 919:    * font metrics.
 920:    *
 921:    * @param text The desired title.
 922:    * @param fm The FontMetrics of the font used.
 923:    * @param availableWidth The available width.
 924:    *
 925:    * @return The allowable string.
 926:    */
 927:   protected String getTitle(String text, FontMetrics fm, int availableWidth)
 928:   {
 929:     Rectangle vr = new Rectangle(0, 0, availableWidth, fm.getHeight());
 930:     Rectangle ir = new Rectangle();
 931:     Rectangle tr = new Rectangle();
 932:     String value = SwingUtilities.layoutCompoundLabel(this, fm, text, null,
 933:                                                       SwingConstants.CENTER,
 934:                                                       SwingConstants.LEFT,
 935:                                                       SwingConstants.CENTER,
 936:                                                       SwingConstants.LEFT, vr,
 937:                                                       ir, tr, 0);
 938:     return value;
 939:   }
 940: 
 941:   /**
 942:    * This method fires something similar to a WINDOW_CLOSING event.
 943:    *
 944:    * @param frame The JInternalFrame that is being closed.
 945:    */
 946:   protected void postClosingEvent(JInternalFrame frame)
 947:   {
 948:     // FIXME: Implement postClosingEvent when I figure out what
 949:     // it's supposed to do.
 950:     // It says that this fires an WINDOW_CLOSING like event. 
 951:     // So the closest thing is some kind of InternalFrameEvent.
 952:     // But none is fired.
 953:     // Can't see it called or anything.
 954:   }
 955: 
 956:   /**
 957:    * This method enables the actions for the TitlePane given the frame's
 958:    * properties.
 959:    */
 960:   protected void enableActions()
 961:   {
 962:     closeAction.setEnabled(frame.isClosable());
 963: 
 964:     iconifyAction.setEnabled(frame.isIconifiable());
 965:     // The maximize action is responsible for restoring it
 966:     // as well, if clicked from the button
 967:     maximizeAction.setEnabled(frame.isMaximizable());
 968: 
 969:     // The restoring action is only active when selected
 970:     // from the menu.
 971:     restoreAction.setEnabled(frame.isMaximum());
 972: 
 973:     sizeAction.setEnabled(frame.isResizable());
 974: 
 975:     // FIXME: Tie MoveAction enabled status to a variable.
 976:     moveAction.setEnabled(false);
 977:   }
 978: 
 979:   /**
 980:    * This method creates a new PropertyChangeListener.
 981:    *
 982:    * @return A new PropertyChangeListener.
 983:    */
 984:   protected PropertyChangeListener createPropertyChangeListener()
 985:   {
 986:     return new PropertyChangeHandler();
 987:   }
 988: 
 989:   /**
 990:    * This method creates a new LayoutManager for the TitlePane.
 991:    *
 992:    * @return A new LayoutManager.
 993:    */
 994:   protected LayoutManager createLayout()
 995:   {
 996:     return new TitlePaneLayout();
 997:   }
 998: }