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