1 /*
2  * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package javax.swing.plaf.metal;
27 
28 import sun.swing.SwingUtilities2;
29 import sun.awt.SunToolkit;
30 import java.awt.*;
31 import java.awt.event.*;
32 import java.beans.*;
33 import javax.swing.*;
34 import javax.swing.border.*;
35 import javax.swing.event.InternalFrameEvent;
36 import javax.swing.plaf.*;
37 import javax.swing.plaf.basic.*;
38 import java.util.Locale;
39 import javax.accessibility.*;
40 
41 
42 /**
43  * Class that manages a JLF awt.Window-descendant class's title bar.
44  * <p>
45  * This class assumes it will be created with a particular window
46  * decoration style, and that if the style changes, a new one will
47  * be created.
48  *
49  * @author Terry Kellerman
50  * @since 1.4
51  */
52 @SuppressWarnings("serial") // Superclass is not serializable across versions
53 class MetalTitlePane extends JComponent {
54     private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0);
55     private static final int IMAGE_HEIGHT = 16;
56     private static final int IMAGE_WIDTH = 16;
57 
58     /**
59      * PropertyChangeListener added to the JRootPane.
60      */
61     private PropertyChangeListener propertyChangeListener;
62 
63     /**
64      * JMenuBar, typically renders the system menu items.
65      */
66     private JMenuBar menuBar;
67     /**
68      * Action used to close the Window.
69      */
70     private Action closeAction;
71 
72     /**
73      * Action used to iconify the Frame.
74      */
75     private Action iconifyAction;
76 
77     /**
78      * Action to restore the Frame size.
79      */
80     private Action restoreAction;
81 
82     /**
83      * Action to restore the Frame size.
84      */
85     private Action maximizeAction;
86 
87     /**
88      * Button used to maximize or restore the Frame.
89      */
90     private JButton toggleButton;
91 
92     /**
93      * Button used to maximize or restore the Frame.
94      */
95     private JButton iconifyButton;
96 
97     /**
98      * Button used to maximize or restore the Frame.
99      */
100     private JButton closeButton;
101 
102     /**
103      * Icon used for toggleButton when window is normal size.
104      */
105     private Icon maximizeIcon;
106 
107     /**
108      * Icon used for toggleButton when window is maximized.
109      */
110     private Icon minimizeIcon;
111 
112     /**
113      * Image used for the system menu icon
114      */
115     private Image systemIcon;
116 
117     /**
118      * Listens for changes in the state of the Window listener to update
119      * the state of the widgets.
120      */
121     private WindowListener windowListener;
122 
123     /**
124      * Window we're currently in.
125      */
126     private Window window;
127 
128     /**
129      * JRootPane rendering for.
130      */
131     private JRootPane rootPane;
132 
133     /**
134      * Room remaining in title for bumps.
135      */
136     private int buttonsWidth;
137 
138     /**
139      * Buffered Frame.state property. As state isn't bound, this is kept
140      * to determine when to avoid updating widgets.
141      */
142     private int state;
143 
144     /**
145      * MetalRootPaneUI that created us.
146      */
147     private MetalRootPaneUI rootPaneUI;
148 
149 
150     // Colors
151     private Color inactiveBackground = UIManager.getColor("inactiveCaption");
152     private Color inactiveForeground = UIManager.getColor("inactiveCaptionText");
153     private Color inactiveShadow = UIManager.getColor("inactiveCaptionBorder");
154     private Color activeBumpsHighlight = MetalLookAndFeel.getPrimaryControlHighlight();
155     private Color activeBumpsShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
156     private Color activeBackground = null;
157     private Color activeForeground = null;
158     private Color activeShadow = null;
159 
160     // Bumps
161     private MetalBumps activeBumps
162         = new MetalBumps( 0, 0,
163                           activeBumpsHighlight,
164                           activeBumpsShadow,
165                           MetalLookAndFeel.getPrimaryControl() );
166     private MetalBumps inactiveBumps
167         = new MetalBumps( 0, 0,
168                           MetalLookAndFeel.getControlHighlight(),
169                           MetalLookAndFeel.getControlDarkShadow(),
170                           MetalLookAndFeel.getControl() );
171 
172 
MetalTitlePane(JRootPane root, MetalRootPaneUI ui)173     public MetalTitlePane(JRootPane root, MetalRootPaneUI ui) {
174         this.rootPane = root;
175         rootPaneUI = ui;
176 
177         state = -1;
178 
179         installSubcomponents();
180         determineColors();
181         installDefaults();
182 
183         setLayout(createLayout());
184     }
185 
186     /**
187      * Uninstalls the necessary state.
188      */
uninstall()189     private void uninstall() {
190         uninstallListeners();
191         window = null;
192         removeAll();
193     }
194 
195     /**
196      * Installs the necessary listeners.
197      */
installListeners()198     private void installListeners() {
199         if (window != null) {
200             windowListener = createWindowListener();
201             window.addWindowListener(windowListener);
202             propertyChangeListener = createWindowPropertyChangeListener();
203             window.addPropertyChangeListener(propertyChangeListener);
204         }
205     }
206 
207     /**
208      * Uninstalls the necessary listeners.
209      */
uninstallListeners()210     private void uninstallListeners() {
211         if (window != null) {
212             window.removeWindowListener(windowListener);
213             window.removePropertyChangeListener(propertyChangeListener);
214         }
215     }
216 
217     /**
218      * Returns the <code>WindowListener</code> to add to the
219      * <code>Window</code>.
220      */
createWindowListener()221     private WindowListener createWindowListener() {
222         return new WindowHandler();
223     }
224 
225     /**
226      * Returns the <code>PropertyChangeListener</code> to install on
227      * the <code>Window</code>.
228      */
createWindowPropertyChangeListener()229     private PropertyChangeListener createWindowPropertyChangeListener() {
230         return new PropertyChangeHandler();
231     }
232 
233     /**
234      * Returns the <code>JRootPane</code> this was created for.
235      */
getRootPane()236     public JRootPane getRootPane() {
237         return rootPane;
238     }
239 
240     /**
241      * Returns the decoration style of the <code>JRootPane</code>.
242      */
getWindowDecorationStyle()243     private int getWindowDecorationStyle() {
244         return getRootPane().getWindowDecorationStyle();
245     }
246 
addNotify()247     public void addNotify() {
248         super.addNotify();
249 
250         uninstallListeners();
251 
252         window = SwingUtilities.getWindowAncestor(this);
253         if (window != null) {
254             if (window instanceof Frame) {
255                 setState(((Frame)window).getExtendedState());
256             }
257             else {
258                 setState(0);
259             }
260             setActive(window.isActive());
261             installListeners();
262             updateSystemIcon();
263         }
264     }
265 
removeNotify()266     public void removeNotify() {
267         super.removeNotify();
268 
269         uninstallListeners();
270         window = null;
271     }
272 
273     /**
274      * Adds any sub-Components contained in the <code>MetalTitlePane</code>.
275      */
installSubcomponents()276     private void installSubcomponents() {
277         int decorationStyle = getWindowDecorationStyle();
278         if (decorationStyle == JRootPane.FRAME) {
279             createActions();
280             menuBar = createMenuBar();
281             add(menuBar);
282             createButtons();
283             add(iconifyButton);
284             add(toggleButton);
285             add(closeButton);
286         } else if (decorationStyle == JRootPane.PLAIN_DIALOG ||
287                 decorationStyle == JRootPane.INFORMATION_DIALOG ||
288                 decorationStyle == JRootPane.ERROR_DIALOG ||
289                 decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG ||
290                 decorationStyle == JRootPane.FILE_CHOOSER_DIALOG ||
291                 decorationStyle == JRootPane.QUESTION_DIALOG ||
292                 decorationStyle == JRootPane.WARNING_DIALOG) {
293             createActions();
294             createButtons();
295             add(closeButton);
296         }
297     }
298 
299     /**
300      * Determines the Colors to draw with.
301      */
determineColors()302     private void determineColors() {
303         switch (getWindowDecorationStyle()) {
304         case JRootPane.FRAME:
305             activeBackground = UIManager.getColor("activeCaption");
306             activeForeground = UIManager.getColor("activeCaptionText");
307             activeShadow = UIManager.getColor("activeCaptionBorder");
308             break;
309         case JRootPane.ERROR_DIALOG:
310             activeBackground = UIManager.getColor(
311                 "OptionPane.errorDialog.titlePane.background");
312             activeForeground = UIManager.getColor(
313                 "OptionPane.errorDialog.titlePane.foreground");
314             activeShadow = UIManager.getColor(
315                 "OptionPane.errorDialog.titlePane.shadow");
316             break;
317         case JRootPane.QUESTION_DIALOG:
318         case JRootPane.COLOR_CHOOSER_DIALOG:
319         case JRootPane.FILE_CHOOSER_DIALOG:
320             activeBackground = UIManager.getColor(
321                 "OptionPane.questionDialog.titlePane.background");
322             activeForeground = UIManager.getColor(
323                 "OptionPane.questionDialog.titlePane.foreground");
324             activeShadow = UIManager.getColor(
325                 "OptionPane.questionDialog.titlePane.shadow");
326             break;
327         case JRootPane.WARNING_DIALOG:
328             activeBackground = UIManager.getColor(
329                 "OptionPane.warningDialog.titlePane.background");
330             activeForeground = UIManager.getColor(
331                 "OptionPane.warningDialog.titlePane.foreground");
332             activeShadow = UIManager.getColor(
333                 "OptionPane.warningDialog.titlePane.shadow");
334             break;
335         case JRootPane.PLAIN_DIALOG:
336         case JRootPane.INFORMATION_DIALOG:
337         default:
338             activeBackground = UIManager.getColor("activeCaption");
339             activeForeground = UIManager.getColor("activeCaptionText");
340             activeShadow = UIManager.getColor("activeCaptionBorder");
341             break;
342         }
343         activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow,
344                                   activeBackground);
345     }
346 
347     /**
348      * Installs the fonts and necessary properties on the MetalTitlePane.
349      */
installDefaults()350     private void installDefaults() {
351         setFont(UIManager.getFont("InternalFrame.titleFont", getLocale()));
352     }
353 
354     /**
355      * Uninstalls any previously installed UI values.
356      */
uninstallDefaults()357     private void uninstallDefaults() {
358     }
359 
360     /**
361      * Returns the <code>JMenuBar</code> displaying the appropriate
362      * system menu items.
363      */
createMenuBar()364     protected JMenuBar createMenuBar() {
365         menuBar = new SystemMenuBar();
366         menuBar.setFocusable(false);
367         menuBar.setBorderPainted(true);
368         menuBar.add(createMenu());
369         return menuBar;
370     }
371 
372     /**
373      * Closes the Window.
374      */
close()375     private void close() {
376         Window window = getWindow();
377 
378         if (window != null) {
379             window.dispatchEvent(new WindowEvent(
380                                  window, WindowEvent.WINDOW_CLOSING));
381         }
382     }
383 
384     /**
385      * Iconifies the Frame.
386      */
iconify()387     private void iconify() {
388         Frame frame = getFrame();
389         if (frame != null) {
390             frame.setExtendedState(state | Frame.ICONIFIED);
391         }
392     }
393 
394     /**
395      * Maximizes the Frame.
396      */
maximize()397     private void maximize() {
398         Frame frame = getFrame();
399         if (frame != null) {
400             frame.setExtendedState(state | Frame.MAXIMIZED_BOTH);
401         }
402     }
403 
404     /**
405      * Restores the Frame size.
406      */
restore()407     private void restore() {
408         Frame frame = getFrame();
409 
410         if (frame == null) {
411             return;
412         }
413 
414         if ((state & Frame.ICONIFIED) != 0) {
415             frame.setExtendedState(state & ~Frame.ICONIFIED);
416         } else {
417             frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
418         }
419     }
420 
421     /**
422      * Create the <code>Action</code>s that get associated with the
423      * buttons and menu items.
424      */
createActions()425     private void createActions() {
426         closeAction = new CloseAction();
427         if (getWindowDecorationStyle() == JRootPane.FRAME) {
428             iconifyAction = new IconifyAction();
429             restoreAction = new RestoreAction();
430             maximizeAction = new MaximizeAction();
431         }
432     }
433 
434     /**
435      * Returns the <code>JMenu</code> displaying the appropriate menu items
436      * for manipulating the Frame.
437      */
createMenu()438     private JMenu createMenu() {
439         JMenu menu = new JMenu("");
440         if (getWindowDecorationStyle() == JRootPane.FRAME) {
441             addMenuItems(menu);
442         }
443         return menu;
444     }
445 
446     /**
447      * Adds the necessary <code>JMenuItem</code>s to the passed in menu.
448      */
addMenuItems(JMenu menu)449     private void addMenuItems(JMenu menu) {
450         Locale locale = getRootPane().getLocale();
451         JMenuItem mi = menu.add(restoreAction);
452         int mnemonic = MetalUtils.getInt("MetalTitlePane.restoreMnemonic", -1);
453 
454         if (mnemonic != -1) {
455             mi.setMnemonic(mnemonic);
456         }
457 
458         mi = menu.add(iconifyAction);
459         mnemonic = MetalUtils.getInt("MetalTitlePane.iconifyMnemonic", -1);
460         if (mnemonic != -1) {
461             mi.setMnemonic(mnemonic);
462         }
463 
464         if (Toolkit.getDefaultToolkit().isFrameStateSupported(
465                 Frame.MAXIMIZED_BOTH)) {
466             mi = menu.add(maximizeAction);
467             mnemonic =
468                 MetalUtils.getInt("MetalTitlePane.maximizeMnemonic", -1);
469             if (mnemonic != -1) {
470                 mi.setMnemonic(mnemonic);
471             }
472         }
473 
474         menu.add(new JSeparator());
475 
476         mi = menu.add(closeAction);
477         mnemonic = MetalUtils.getInt("MetalTitlePane.closeMnemonic", -1);
478         if (mnemonic != -1) {
479             mi.setMnemonic(mnemonic);
480         }
481     }
482 
483     /**
484      * Returns a <code>JButton</code> appropriate for placement on the
485      * TitlePane.
486      */
createTitleButton()487     private JButton createTitleButton() {
488         JButton button = new JButton();
489 
490         button.setFocusPainted(false);
491         button.setFocusable(false);
492         button.setOpaque(true);
493         return button;
494     }
495 
496     /**
497      * Creates the Buttons that will be placed on the TitlePane.
498      */
createButtons()499     private void createButtons() {
500         closeButton = createTitleButton();
501         closeButton.setAction(closeAction);
502         closeButton.setText(null);
503         closeButton.putClientProperty("paintActive", Boolean.TRUE);
504         closeButton.setBorder(handyEmptyBorder);
505         closeButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
506                                       "Close");
507         closeButton.setIcon(UIManager.getIcon("InternalFrame.closeIcon"));
508 
509         if (getWindowDecorationStyle() == JRootPane.FRAME) {
510             maximizeIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
511             minimizeIcon = UIManager.getIcon("InternalFrame.minimizeIcon");
512 
513             iconifyButton = createTitleButton();
514             iconifyButton.setAction(iconifyAction);
515             iconifyButton.setText(null);
516             iconifyButton.putClientProperty("paintActive", Boolean.TRUE);
517             iconifyButton.setBorder(handyEmptyBorder);
518             iconifyButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
519                                             "Iconify");
520             iconifyButton.setIcon(UIManager.getIcon("InternalFrame.iconifyIcon"));
521 
522             toggleButton = createTitleButton();
523             toggleButton.setAction(restoreAction);
524             toggleButton.putClientProperty("paintActive", Boolean.TRUE);
525             toggleButton.setBorder(handyEmptyBorder);
526             toggleButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
527                                            "Maximize");
528             toggleButton.setIcon(maximizeIcon);
529         }
530     }
531 
532     /**
533      * Returns the <code>LayoutManager</code> that should be installed on
534      * the <code>MetalTitlePane</code>.
535      */
createLayout()536     private LayoutManager createLayout() {
537         return new TitlePaneLayout();
538     }
539 
540     /**
541      * Updates state dependent upon the Window's active state.
542      */
setActive(boolean isActive)543     private void setActive(boolean isActive) {
544         Boolean activeB = isActive ? Boolean.TRUE : Boolean.FALSE;
545 
546         closeButton.putClientProperty("paintActive", activeB);
547         if (getWindowDecorationStyle() == JRootPane.FRAME) {
548             iconifyButton.putClientProperty("paintActive", activeB);
549             toggleButton.putClientProperty("paintActive", activeB);
550         }
551         // Repaint the whole thing as the Borders that are used have
552         // different colors for active vs inactive
553         getRootPane().repaint();
554     }
555 
556     /**
557      * Sets the state of the Window.
558      */
setState(int state)559     private void setState(int state) {
560         setState(state, false);
561     }
562 
563     /**
564      * Sets the state of the window. If <code>updateRegardless</code> is
565      * true and the state has not changed, this will update anyway.
566      */
setState(int state, boolean updateRegardless)567     private void setState(int state, boolean updateRegardless) {
568         Window w = getWindow();
569 
570         if (w != null && getWindowDecorationStyle() == JRootPane.FRAME) {
571             if (this.state == state && !updateRegardless) {
572                 return;
573             }
574             Frame frame = getFrame();
575 
576             if (frame != null) {
577                 JRootPane rootPane = getRootPane();
578 
579                 if (((state & Frame.MAXIMIZED_BOTH) != 0) &&
580                         (rootPane.getBorder() == null ||
581                         (rootPane.getBorder() instanceof UIResource)) &&
582                             frame.isShowing()) {
583                     rootPane.setBorder(null);
584                 }
585                 else if ((state & Frame.MAXIMIZED_BOTH) == 0) {
586                     // This is a croak, if state becomes bound, this can
587                     // be nuked.
588                     rootPaneUI.installBorder(rootPane);
589                 }
590                 if (frame.isResizable()) {
591                     if ((state & Frame.MAXIMIZED_BOTH) != 0) {
592                         updateToggleButton(restoreAction, minimizeIcon);
593                         maximizeAction.setEnabled(false);
594                         restoreAction.setEnabled(true);
595                     }
596                     else {
597                         updateToggleButton(maximizeAction, maximizeIcon);
598                         maximizeAction.setEnabled(true);
599                         restoreAction.setEnabled(false);
600                     }
601                     if (toggleButton.getParent() == null ||
602                         iconifyButton.getParent() == null) {
603                         add(toggleButton);
604                         add(iconifyButton);
605                         revalidate();
606                         repaint();
607                     }
608                     toggleButton.setText(null);
609                 }
610                 else {
611                     maximizeAction.setEnabled(false);
612                     restoreAction.setEnabled(false);
613                     if (toggleButton.getParent() != null) {
614                         remove(toggleButton);
615                         revalidate();
616                         repaint();
617                     }
618                 }
619             }
620             else {
621                 // Not contained in a Frame
622                 maximizeAction.setEnabled(false);
623                 restoreAction.setEnabled(false);
624                 iconifyAction.setEnabled(false);
625                 remove(toggleButton);
626                 remove(iconifyButton);
627                 revalidate();
628                 repaint();
629             }
630             closeAction.setEnabled(true);
631             this.state = state;
632         }
633     }
634 
635     /**
636      * Updates the toggle button to contain the Icon <code>icon</code>, and
637      * Action <code>action</code>.
638      */
updateToggleButton(Action action, Icon icon)639     private void updateToggleButton(Action action, Icon icon) {
640         toggleButton.setAction(action);
641         toggleButton.setIcon(icon);
642         toggleButton.setText(null);
643     }
644 
645     /**
646      * Returns the Frame rendering in. This will return null if the
647      * <code>JRootPane</code> is not contained in a <code>Frame</code>.
648      */
getFrame()649     private Frame getFrame() {
650         Window window = getWindow();
651 
652         if (window instanceof Frame) {
653             return (Frame)window;
654         }
655         return null;
656     }
657 
658     /**
659      * Returns the <code>Window</code> the <code>JRootPane</code> is
660      * contained in. This will return null if there is no parent ancestor
661      * of the <code>JRootPane</code>.
662      */
getWindow()663     private Window getWindow() {
664         return window;
665     }
666 
667     /**
668      * Returns the String to display as the title.
669      */
getTitle()670     private String getTitle() {
671         Window w = getWindow();
672 
673         if (w instanceof Frame) {
674             return ((Frame)w).getTitle();
675         }
676         else if (w instanceof Dialog) {
677             return ((Dialog)w).getTitle();
678         }
679         return null;
680     }
681 
682     /**
683      * Renders the TitlePane.
684      */
paintComponent(Graphics g)685     public void paintComponent(Graphics g)  {
686         // As state isn't bound, we need a convenience place to check
687         // if it has changed. Changing the state typically changes the
688         if (getFrame() != null) {
689             setState(getFrame().getExtendedState());
690         }
691         JRootPane rootPane = getRootPane();
692         Window window = getWindow();
693         boolean leftToRight = (window == null) ?
694                                rootPane.getComponentOrientation().isLeftToRight() :
695                                window.getComponentOrientation().isLeftToRight();
696         boolean isSelected = (window == null) ? true : window.isActive();
697         int width = getWidth();
698         int height = getHeight();
699 
700         Color background;
701         Color foreground;
702         Color darkShadow;
703 
704         MetalBumps bumps;
705 
706         if (isSelected) {
707             background = activeBackground;
708             foreground = activeForeground;
709             darkShadow = activeShadow;
710             bumps = activeBumps;
711         } else {
712             background = inactiveBackground;
713             foreground = inactiveForeground;
714             darkShadow = inactiveShadow;
715             bumps = inactiveBumps;
716         }
717 
718         g.setColor(background);
719         g.fillRect(0, 0, width, height);
720 
721         g.setColor( darkShadow );
722         g.drawLine ( 0, height - 1, width, height -1);
723         g.drawLine ( 0, 0, 0 ,0);
724         g.drawLine ( width - 1, 0 , width -1, 0);
725 
726         int xOffset = leftToRight ? 5 : width - 5;
727 
728         if (getWindowDecorationStyle() == JRootPane.FRAME) {
729             xOffset += leftToRight ? IMAGE_WIDTH + 5 : - IMAGE_WIDTH - 5;
730         }
731 
732         String theTitle = getTitle();
733         if (theTitle != null) {
734             FontMetrics fm = SwingUtilities2.getFontMetrics(rootPane, g);
735 
736             g.setColor(foreground);
737 
738             int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
739 
740             Rectangle rect = new Rectangle(0, 0, 0, 0);
741             if (iconifyButton != null && iconifyButton.getParent() != null) {
742                 rect = iconifyButton.getBounds();
743             }
744             int titleW;
745 
746             if( leftToRight ) {
747                 if (rect.x == 0) {
748                     rect.x = window.getWidth() - window.getInsets().right-2;
749                 }
750                 titleW = rect.x - xOffset - 4;
751                 theTitle = SwingUtilities2.clipStringIfNecessary(
752                                 rootPane, fm, theTitle, titleW);
753             } else {
754                 titleW = xOffset - rect.x - rect.width - 4;
755                 theTitle = SwingUtilities2.clipStringIfNecessary(
756                                 rootPane, fm, theTitle, titleW);
757                 xOffset -= SwingUtilities2.stringWidth(rootPane, fm,
758                                                        theTitle);
759             }
760             int titleLength = SwingUtilities2.stringWidth(rootPane, fm,
761                                                           theTitle);
762             SwingUtilities2.drawString(rootPane, g, theTitle, xOffset,
763                                        yOffset );
764             xOffset += leftToRight ? titleLength + 5  : -5;
765         }
766 
767         int bumpXOffset;
768         int bumpLength;
769         if( leftToRight ) {
770             bumpLength = width - buttonsWidth - xOffset - 5;
771             bumpXOffset = xOffset;
772         } else {
773             bumpLength = xOffset - buttonsWidth - 5;
774             bumpXOffset = buttonsWidth + 5;
775         }
776         int bumpYOffset = 3;
777         int bumpHeight = getHeight() - (2 * bumpYOffset);
778         bumps.setBumpArea( bumpLength, bumpHeight );
779         bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
780     }
781 
782     /**
783      * Actions used to <code>close</code> the <code>Window</code>.
784      */
785     @SuppressWarnings("serial") // Superclass is not serializable across versions
786     private class CloseAction extends AbstractAction {
CloseAction()787         public CloseAction() {
788             super(UIManager.getString("MetalTitlePane.closeTitle",
789                                       getLocale()));
790         }
791 
actionPerformed(ActionEvent e)792         public void actionPerformed(ActionEvent e) {
793             close();
794         }
795     }
796 
797 
798     /**
799      * Actions used to <code>iconfiy</code> the <code>Frame</code>.
800      */
801     @SuppressWarnings("serial") // Superclass is not serializable across versions
802     private class IconifyAction extends AbstractAction {
IconifyAction()803         public IconifyAction() {
804             super(UIManager.getString("MetalTitlePane.iconifyTitle",
805                                       getLocale()));
806         }
807 
actionPerformed(ActionEvent e)808         public void actionPerformed(ActionEvent e) {
809             iconify();
810         }
811     }
812 
813 
814     /**
815      * Actions used to <code>restore</code> the <code>Frame</code>.
816      */
817     @SuppressWarnings("serial") // Superclass is not serializable across versions
818     private class RestoreAction extends AbstractAction {
RestoreAction()819         public RestoreAction() {
820             super(UIManager.getString
821                   ("MetalTitlePane.restoreTitle", getLocale()));
822         }
823 
actionPerformed(ActionEvent e)824         public void actionPerformed(ActionEvent e) {
825             restore();
826         }
827     }
828 
829 
830     /**
831      * Actions used to <code>restore</code> the <code>Frame</code>.
832      */
833     @SuppressWarnings("serial") // Superclass is not serializable across versions
834     private class MaximizeAction extends AbstractAction {
MaximizeAction()835         public MaximizeAction() {
836             super(UIManager.getString("MetalTitlePane.maximizeTitle",
837                                       getLocale()));
838         }
839 
actionPerformed(ActionEvent e)840         public void actionPerformed(ActionEvent e) {
841             maximize();
842         }
843     }
844 
845 
846     /**
847      * Class responsible for drawing the system menu. Looks up the
848      * image to draw from the Frame associated with the
849      * <code>JRootPane</code>.
850      */
851     @SuppressWarnings("serial") // Superclass is not serializable across versions
852     private class SystemMenuBar extends JMenuBar {
paint(Graphics g)853         public void paint(Graphics g) {
854             if (isOpaque()) {
855                 g.setColor(getBackground());
856                 g.fillRect(0, 0, getWidth(), getHeight());
857             }
858 
859             if (systemIcon != null) {
860                 g.drawImage(systemIcon, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
861             } else {
862                 Icon icon = UIManager.getIcon("InternalFrame.icon");
863 
864                 if (icon != null) {
865                     icon.paintIcon(this, g, 0, 0);
866                 }
867             }
868         }
getMinimumSize()869         public Dimension getMinimumSize() {
870             return getPreferredSize();
871         }
getPreferredSize()872         public Dimension getPreferredSize() {
873             Dimension size = super.getPreferredSize();
874 
875             return new Dimension(Math.max(IMAGE_WIDTH, size.width),
876                                  Math.max(size.height, IMAGE_HEIGHT));
877         }
878     }
879 
880     private class TitlePaneLayout implements LayoutManager {
addLayoutComponent(String name, Component c)881         public void addLayoutComponent(String name, Component c) {}
removeLayoutComponent(Component c)882         public void removeLayoutComponent(Component c) {}
preferredLayoutSize(Container c)883         public Dimension preferredLayoutSize(Container c)  {
884             int height = computeHeight();
885             return new Dimension(height, height);
886         }
887 
minimumLayoutSize(Container c)888         public Dimension minimumLayoutSize(Container c) {
889             return preferredLayoutSize(c);
890         }
891 
computeHeight()892         private int computeHeight() {
893             FontMetrics fm = rootPane.getFontMetrics(getFont());
894             int fontHeight = fm.getHeight();
895             fontHeight += 7;
896             int iconHeight = 0;
897             if (getWindowDecorationStyle() == JRootPane.FRAME) {
898                 iconHeight = IMAGE_HEIGHT;
899             }
900 
901             int finalHeight = Math.max( fontHeight, iconHeight );
902             return finalHeight;
903         }
904 
layoutContainer(Container c)905         public void layoutContainer(Container c) {
906             boolean leftToRight = (window == null) ?
907                 getRootPane().getComponentOrientation().isLeftToRight() :
908                 window.getComponentOrientation().isLeftToRight();
909 
910             int w = getWidth();
911             int x;
912             int y = 3;
913             int spacing;
914             int buttonHeight;
915             int buttonWidth;
916 
917             if (closeButton != null && closeButton.getIcon() != null) {
918                 buttonHeight = closeButton.getIcon().getIconHeight();
919                 buttonWidth = closeButton.getIcon().getIconWidth();
920             }
921             else {
922                 buttonHeight = IMAGE_HEIGHT;
923                 buttonWidth = IMAGE_WIDTH;
924             }
925 
926             // assumes all buttons have the same dimensions
927             // these dimensions include the borders
928 
929             x = leftToRight ? w : 0;
930 
931             spacing = 5;
932             x = leftToRight ? spacing : w - buttonWidth - spacing;
933             if (menuBar != null) {
934                 menuBar.setBounds(x, y, buttonWidth, buttonHeight);
935             }
936 
937             x = leftToRight ? w : 0;
938             spacing = 4;
939             x += leftToRight ? -spacing -buttonWidth : spacing;
940             if (closeButton != null) {
941                 closeButton.setBounds(x, y, buttonWidth, buttonHeight);
942             }
943 
944             if( !leftToRight ) x += buttonWidth;
945 
946             if (getWindowDecorationStyle() == JRootPane.FRAME) {
947                 if (Toolkit.getDefaultToolkit().isFrameStateSupported(
948                         Frame.MAXIMIZED_BOTH)) {
949                     if (toggleButton.getParent() != null) {
950                         spacing = 10;
951                         x += leftToRight ? -spacing -buttonWidth : spacing;
952                         toggleButton.setBounds(x, y, buttonWidth, buttonHeight);
953                         if (!leftToRight) {
954                             x += buttonWidth;
955                         }
956                     }
957                 }
958 
959                 if (iconifyButton != null && iconifyButton.getParent() != null) {
960                     spacing = 2;
961                     x += leftToRight ? -spacing -buttonWidth : spacing;
962                     iconifyButton.setBounds(x, y, buttonWidth, buttonHeight);
963                     if (!leftToRight) {
964                         x += buttonWidth;
965                     }
966                 }
967             }
968             buttonsWidth = leftToRight ? w - x : x;
969         }
970     }
971 
972 
973 
974     /**
975      * PropertyChangeListener installed on the Window. Updates the necessary
976      * state as the state of the Window changes.
977      */
978     private class PropertyChangeHandler implements PropertyChangeListener {
propertyChange(PropertyChangeEvent pce)979         public void propertyChange(PropertyChangeEvent pce) {
980             String name = pce.getPropertyName();
981 
982             // Frame.state isn't currently bound.
983             if ("resizable".equals(name) || "state".equals(name)) {
984                 Frame frame = getFrame();
985 
986                 if (frame != null) {
987                     setState(frame.getExtendedState(), true);
988                 }
989                 if ("resizable".equals(name)) {
990                     getRootPane().repaint();
991                 }
992             }
993             else if ("title".equals(name)) {
994                 repaint();
995             }
996             else if ("componentOrientation" == name) {
997                 revalidate();
998                 repaint();
999             }
1000             else if ("iconImage" == name) {
1001                 updateSystemIcon();
1002                 revalidate();
1003                 repaint();
1004             }
1005         }
1006     }
1007 
1008     /**
1009      * Update the image used for the system icon
1010      */
updateSystemIcon()1011     private void updateSystemIcon() {
1012         Window window = getWindow();
1013         if (window == null) {
1014             systemIcon = null;
1015             return;
1016         }
1017         java.util.List<Image> icons = window.getIconImages();
1018         assert icons != null;
1019 
1020         if (icons.size() == 0) {
1021             systemIcon = null;
1022         }
1023         else if (icons.size() == 1) {
1024             systemIcon = icons.get(0);
1025         }
1026         else {
1027             systemIcon = SunToolkit.getScaledIconImage(icons,
1028                                                        IMAGE_WIDTH,
1029                                                        IMAGE_HEIGHT);
1030         }
1031     }
1032 
1033 
1034     /**
1035      * WindowListener installed on the Window, updates the state as necessary.
1036      */
1037     private class WindowHandler extends WindowAdapter {
windowActivated(WindowEvent ev)1038         public void windowActivated(WindowEvent ev) {
1039             setActive(true);
1040         }
1041 
windowDeactivated(WindowEvent ev)1042         public void windowDeactivated(WindowEvent ev) {
1043             setActive(false);
1044         }
1045     }
1046 }
1047