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      */
CloseAction()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      */
actionPerformed(ActionEvent e)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      */
IconifyAction()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      */
actionPerformed(ActionEvent e)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      */
MaximizeAction()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      */
actionPerformed(ActionEvent e)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      */
MoveAction()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      */
actionPerformed(ActionEvent e)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      */
RestoreAction()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      */
actionPerformed(ActionEvent e)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      */
SizeAction()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      */
actionPerformed(ActionEvent e)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      */
propertyChange(PropertyChangeEvent evt)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      */
isFocusTraversable()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      */
isOpaque()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      */
paint(Graphics g)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      */
requestFocus()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      */
TitlePaneLayout()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      */
addLayoutComponent(String name, Component c)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      */
layoutContainer(Container c)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      */
minimumLayoutSize(Container c)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      */
preferredLayoutSize(Container c)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      */
removeLayoutComponent(Component c)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      */
PaneButton(Action a)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      */
isFocusable()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    */
BasicInternalFrameTitlePane(JInternalFrame f)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    */
installTitlePane()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    */
addSubComponents()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    */
createActions()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    */
installListeners()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    */
uninstallListeners()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    */
installDefaults()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    */
uninstallDefaults()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    */
createButtons()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    */
setButtonIcons()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    */
assembleSystemMenu()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    */
addSystemMenuItems(JMenu systemMenu)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    */
createSystemMenuBar()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    */
createSystemMenu()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    */
showSystemMenu()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    */
paintComponent(Graphics g)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    */
paintTitleBackground(Graphics g)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    */
getTitle(String text, FontMetrics fm, int availableWidth)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    */
postClosingEvent(JInternalFrame frame)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    */
enableActions()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    */
createPropertyChangeListener()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    */
createLayout()1011   protected LayoutManager createLayout()
1012   {
1013     return new TitlePaneLayout();
1014   }
1015 }
1016