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