001    /* BasicInternalFrameTitlePane.java --
002       Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Container;
044    import java.awt.Dimension;
045    import java.awt.Font;
046    import java.awt.FontMetrics;
047    import java.awt.Graphics;
048    import java.awt.Insets;
049    import java.awt.LayoutManager;
050    import java.awt.Rectangle;
051    import java.awt.event.ActionEvent;
052    import java.awt.event.KeyEvent;
053    import java.beans.PropertyChangeEvent;
054    import java.beans.PropertyChangeListener;
055    import java.beans.PropertyVetoException;
056    
057    import javax.swing.AbstractAction;
058    import javax.swing.Action;
059    import javax.swing.Icon;
060    import javax.swing.JButton;
061    import javax.swing.JComponent;
062    import javax.swing.JInternalFrame;
063    import javax.swing.JLabel;
064    import javax.swing.JMenu;
065    import javax.swing.JMenuBar;
066    import javax.swing.JMenuItem;
067    import javax.swing.SwingConstants;
068    import javax.swing.SwingUtilities;
069    import javax.swing.UIManager;
070    
071    /**
072     * This class acts as a titlebar for JInternalFrames.
073     */
074    public 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    }