001 /* MetalRootPaneUI.java
002 Copyright (C) 2005, 2006 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.plaf.metal;
040
041 import java.awt.Color;
042 import java.awt.Component;
043 import java.awt.Container;
044 import java.awt.Dimension;
045 import java.awt.Frame;
046 import java.awt.Graphics;
047 import java.awt.Insets;
048 import java.awt.LayoutManager;
049 import java.awt.LayoutManager2;
050 import java.awt.Point;
051 import java.awt.Rectangle;
052 import java.awt.Window;
053 import java.awt.event.ActionEvent;
054 import java.awt.event.MouseEvent;
055 import java.beans.PropertyChangeEvent;
056
057 import javax.swing.AbstractAction;
058 import javax.swing.Action;
059 import javax.swing.Icon;
060 import javax.swing.JButton;
061 import javax.swing.JComponent;
062 import javax.swing.JDialog;
063 import javax.swing.JFrame;
064 import javax.swing.JLabel;
065 import javax.swing.JLayeredPane;
066 import javax.swing.JMenu;
067 import javax.swing.JMenuBar;
068 import javax.swing.JRootPane;
069 import javax.swing.SwingConstants;
070 import javax.swing.SwingUtilities;
071 import javax.swing.UIManager;
072 import javax.swing.border.AbstractBorder;
073 import javax.swing.event.MouseInputAdapter;
074 import javax.swing.plaf.ComponentUI;
075 import javax.swing.plaf.basic.BasicRootPaneUI;
076
077 /**
078 * A UI delegate for the {@link JRootPane} component. This implementation
079 * supports the JRootPane <code>windowDecorationStyle</code> property.
080 *
081 * @author Roman Kennke (kennke@aicas.com)
082 *
083 * @since 1.4
084 */
085 public class MetalRootPaneUI
086 extends BasicRootPaneUI
087 {
088
089 /**
090 * The border that is used on JRootPane when the windowDecorationStyle
091 * property of the JRootPane is set to a different value than NONE.
092 *
093 * @author Roman Kennke (kennke@aicas.com)
094 */
095 private static class MetalFrameBorder
096 extends AbstractBorder
097 {
098 /**
099 * Returns the border insets.
100 *
101 * @param c the component
102 * @param newInsets the insets to be filled with the return value, may be
103 * <code>null</code> in which case a new object is created
104 *
105 * @return the border insets
106 */
107 public Insets getBorderInsets(Component c, Insets newInsets)
108 {
109 if (newInsets == null)
110 newInsets = new Insets(5, 5, 5, 5);
111 else
112 {
113 newInsets.top = 5;
114 newInsets.left = 5;
115 newInsets.bottom = 5;
116 newInsets.right = 5;
117 }
118 return newInsets;
119 }
120
121 /**
122 * Returns the border insets.
123 *
124 * @param c the component
125 *
126 * @return the border insets
127 */
128 public Insets getBorderInsets(Component c)
129 {
130 return getBorderInsets(c, null);
131 }
132
133 /**
134 * Paints the border for the specified component.
135 *
136 * @param c the component
137 * @param g the graphics device
138 * @param x the x-coordinate
139 * @param y the y-coordinate
140 * @param w the width
141 * @param h the height
142 */
143 public void paintBorder(Component c, Graphics g, int x, int y, int w,
144 int h)
145 {
146 JRootPane f = (JRootPane) c;
147 Window frame = SwingUtilities.getWindowAncestor(f);
148 if (frame.isActive())
149 g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
150 else
151 g.setColor(MetalLookAndFeel.getControlDarkShadow());
152
153 // Fill the border background.
154 g.fillRect(x, y, w, 5);
155 g.fillRect(x, y, 5, h);
156 g.fillRect(x + w - 5, y, 5, h);
157 g.fillRect(x, y + h - 5, w, 5);
158
159 // Draw a dot in each corner.
160 g.setColor(MetalLookAndFeel.getControl());
161 g.fillRect(x, y, 1, 1);
162 g.fillRect(x + w - 1, y, 1, 1);
163 g.fillRect(x + w - 1, y + h - 1, 1, 1);
164 g.fillRect(x, y + h - 1, 1, 1);
165
166 // Draw the lines.
167 g.setColor(MetalLookAndFeel.getBlack());
168 g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
169 g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
170 g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
171 g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
172
173 // Draw the line highlights.
174 if (frame.isActive())
175 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
176 else
177 g.setColor(MetalLookAndFeel.getControlShadow());
178 g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
179 g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
180 g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
181 g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
182 }
183 }
184
185 /**
186 * The component that renders the title bar for frames. This duplicates
187 * most of {@link MetalInternalFrameTitlePane}. It is not reasonably possible
188 * to reuse that class because that is bound to the JInternalFrame and we
189 * need to handle JFrames/JRootPanes here.
190 *
191 * @author Roman Kennke (kennke@aicas.com)
192 */
193 private static class MetalTitlePane extends JComponent
194 {
195
196 /**
197 * Handles dragging of the title pane and moves the window accordingly.
198 */
199 private class MouseHandler
200 extends MouseInputAdapter
201 {
202 /**
203 * The point where the dragging started.
204 */
205 Point lastDragLocation;
206
207 /**
208 * Receives notification when the mouse gets pressed on the title pane.
209 * This updates the lastDragLocation.
210 *
211 * @param ev the mouse event
212 */
213 public void mousePressed(MouseEvent ev)
214 {
215 lastDragLocation = ev.getPoint();
216 }
217
218 /**
219 * Receives notification when the mouse is dragged on the title pane.
220 * This will move the nearest window accordingly.
221 *
222 * @param ev the mouse event
223 */
224 public void mouseDragged(MouseEvent ev)
225 {
226 Point dragLocation = ev.getPoint();
227 int deltaX = dragLocation.x - lastDragLocation.x;
228 int deltaY = dragLocation.y - lastDragLocation.y;
229 Window window = SwingUtilities.getWindowAncestor(rootPane);
230 Point loc = window.getLocation();
231 window.setLocation(loc.x + deltaX, loc.y + deltaY);
232 // Note that we do not update the lastDragLocation. This is because
233 // we move the underlying window while dragging the component, which
234 // results in having the same lastDragLocation under the mouse while
235 // dragging.
236 }
237 }
238
239 /**
240 * The Action responsible for closing the JInternalFrame.
241 */
242 private class CloseAction extends AbstractAction
243 {
244 /**
245 * Creates a new action.
246 */
247 public CloseAction()
248 {
249 super("Close");
250 }
251
252 /**
253 * This method is called when something closes the frame.
254 *
255 * @param e the ActionEvent
256 */
257 public void actionPerformed(ActionEvent e)
258 {
259 Window frame = SwingUtilities.getWindowAncestor(rootPane);
260 if (frame instanceof JFrame)
261 {
262 JFrame jframe = (JFrame) frame;
263 switch (jframe.getDefaultCloseOperation())
264 {
265 case JFrame.EXIT_ON_CLOSE:
266 jframe.setVisible(false);
267 jframe.dispose();
268 System.exit(0);
269 break;
270 case JFrame.DISPOSE_ON_CLOSE:
271 jframe.setVisible(false);
272 jframe.dispose();
273 break;
274 case JFrame.HIDE_ON_CLOSE:
275 jframe.setVisible(false);
276 break;
277 case JFrame.DO_NOTHING_ON_CLOSE:
278 default:
279 break;
280 }
281 }
282 else if (frame instanceof JDialog)
283 {
284 JDialog jdialog = (JDialog) frame;
285 switch (jdialog.getDefaultCloseOperation())
286 {
287 case JFrame.DISPOSE_ON_CLOSE:
288 jdialog.setVisible(false);
289 jdialog.dispose();
290 break;
291 case JFrame.HIDE_ON_CLOSE:
292 jdialog.setVisible(false);
293 break;
294 case JFrame.DO_NOTHING_ON_CLOSE:
295 default:
296 break;
297 }
298 }
299 }
300 }
301
302 /**
303 * This action is performed when the iconify button is pressed.
304 */
305 private class IconifyAction
306 extends AbstractAction
307 {
308
309 public void actionPerformed(ActionEvent event)
310 {
311 Window w = SwingUtilities.getWindowAncestor(rootPane);
312 if (w instanceof Frame)
313 {
314 Frame f = (Frame) w;
315 int state = f.getExtendedState();
316 f.setExtendedState(Frame.ICONIFIED);
317 }
318 }
319
320 }
321
322 /**
323 * This action is performed when the maximize button is pressed.
324 */
325 private class MaximizeAction
326 extends AbstractAction
327 {
328
329 public void actionPerformed(ActionEvent event)
330 {
331 Window w = SwingUtilities.getWindowAncestor(rootPane);
332 if (w instanceof Frame)
333 {
334 Frame f = (Frame) w;
335 int state = f.getExtendedState();
336 f.setExtendedState(Frame.MAXIMIZED_BOTH);
337 }
338 }
339 }
340
341 /**
342 * This helper class is used to create the minimize, maximize and close
343 * buttons in the top right corner of the Title Pane. These buttons are
344 * special since they cannot be given focus and have no border.
345 */
346 private class PaneButton extends JButton
347 {
348 /**
349 * Creates a new PaneButton object with the given Action.
350 *
351 * @param a The Action that the button uses.
352 */
353 public PaneButton(Action a)
354 {
355 super(a);
356 setMargin(new Insets(0, 0, 0, 0));
357 }
358
359 /**
360 * This method returns true if the Component can be focused.
361 *
362 * @return false.
363 */
364 public boolean isFocusable()
365 {
366 // These buttons cannot be given focus.
367 return false;
368 }
369
370 }
371
372 /**
373 * The layout for the JRootPane when the <code>windowDecorationStyle</code>
374 * property is set. In addition to the usual JRootPane.RootLayout behaviour
375 * this lays out the titlePane.
376 *
377 * @author Roman Kennke (kennke@aicas.com)
378 */
379 private class MetalTitlePaneLayout implements LayoutManager
380 {
381 /**
382 * Creates a new <code>TitlePaneLayout</code> object.
383 */
384 public MetalTitlePaneLayout()
385 {
386 // Do nothing.
387 }
388
389 /**
390 * Adds a Component to the Container.
391 *
392 * @param name The name to reference the added Component by.
393 * @param c The Component to add.
394 */
395 public void addLayoutComponent(String name, Component c)
396 {
397 // Do nothing.
398 }
399
400 /**
401 * This method is called to lay out the children of the Title Pane.
402 *
403 * @param c The Container to lay out.
404 */
405 public void layoutContainer(Container c)
406 {
407
408 Dimension size = c.getSize();
409 Insets insets = c.getInsets();
410 int width = size.width - insets.left - insets.right;
411 int height = size.height - insets.top - insets.bottom;
412
413 int loc = width - insets.right - 1;
414 int top = insets.top + 2;
415 int buttonHeight = height - 4;
416 if (closeButton.isVisible())
417 {
418 int buttonWidth = closeIcon.getIconWidth();
419 loc -= buttonWidth + 2;
420 closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
421 loc -= 6;
422 }
423
424 if (maxButton.isVisible())
425 {
426 int buttonWidth = maxIcon.getIconWidth();
427 loc -= buttonWidth + 4;
428 maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
429 }
430
431 if (iconButton.isVisible())
432 {
433 int buttonWidth = minIcon.getIconWidth();
434 loc -= buttonWidth + 4;
435 iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
436 loc -= 2;
437 }
438
439 Dimension titlePreferredSize = title.getPreferredSize();
440 title.setBounds(insets.left + 5, insets.top,
441 Math.min(titlePreferredSize.width, loc - insets.left - 10),
442 height);
443
444 }
445
446 /**
447 * This method returns the minimum size of the given Container given the
448 * children that it has.
449 *
450 * @param c The Container to get a minimum size for.
451 *
452 * @return The minimum size of the Container.
453 */
454 public Dimension minimumLayoutSize(Container c)
455 {
456 return preferredLayoutSize(c);
457 }
458
459 /**
460 * Returns the preferred size of the given Container taking
461 * into account the children that it has.
462 *
463 * @param c The Container to lay out.
464 *
465 * @return The preferred size of the Container.
466 */
467 public Dimension preferredLayoutSize(Container c)
468 {
469 return new Dimension(22, 22);
470 }
471
472 /**
473 * Removes a Component from the Container.
474 *
475 * @param c The Component to remove.
476 */
477 public void removeLayoutComponent(Component c)
478 {
479 // Nothing to do here.
480 }
481 }
482
483 JRootPane rootPane;
484
485 /** The button that closes the JInternalFrame. */
486 JButton closeButton;
487
488 /** The button that iconifies the JInternalFrame. */
489 JButton iconButton;
490
491 /** The button that maximizes the JInternalFrame. */
492 JButton maxButton;
493
494 Icon minIcon;
495
496 /** The icon displayed in the maximize button. */
497 Icon maxIcon;
498
499 /** The icon displayed in the iconify button. */
500 private Icon iconIcon;
501
502 /** The icon displayed in the close button. */
503 Icon closeIcon;
504
505 /**
506 * The background color of the TitlePane when the JInternalFrame is not
507 * selected.
508 */
509 private Color notSelectedTitleColor;
510
511 /**
512 * The background color of the TitlePane when the JInternalFrame is
513 * selected.
514 */
515 private Color selectedTitleColor;
516
517 /**
518 * The label used to display the title. This label is not added to the
519 * TitlePane.
520 */
521 JLabel title;
522
523 /** The action associated with closing the JInternalFrame. */
524 private Action closeAction;
525
526 /** The action associated with iconifying the JInternalFrame. */
527 private Action iconifyAction;
528
529 /** The action associated with maximizing the JInternalFrame. */
530 private Action maximizeAction;
531
532 /** The JMenuBar that is located at the top left of the Title Pane. */
533 private JMenuBar menuBar;
534
535 /** The JMenu inside the menuBar. */
536 protected JMenu windowMenu;
537
538 MetalTitlePane(JRootPane rp)
539 {
540 rootPane = rp;
541 setLayout(createLayout());
542 title = new JLabel();
543 title.setHorizontalAlignment(SwingConstants.LEFT);
544 title.setHorizontalTextPosition(SwingConstants.LEFT);
545 title.setOpaque(false);
546 installTitlePane();
547 }
548
549 protected LayoutManager createLayout()
550 {
551 return new MetalTitlePaneLayout();
552 }
553
554 /**
555 * This method installs the TitlePane onto the JInternalFrameTitlePane. It
556 * also creates any children components that need to be created and adds
557 * listeners to the appropriate components.
558 */
559 protected void installTitlePane()
560 {
561 installDefaults();
562 installListeners();
563 createActions();
564 assembleSystemMenu();
565 createButtons();
566 setButtonIcons();
567 addSubComponents();
568 enableActions();
569 }
570
571 private void enableActions()
572 {
573 // TODO: Implement this.
574 }
575
576 private void addSubComponents()
577 {
578 add(menuBar);
579 add(closeButton);
580 add(iconButton);
581 add(maxButton);
582 }
583
584 private void installListeners()
585 {
586 MouseInputAdapter mouseHandler = new MouseHandler();
587 addMouseListener(mouseHandler);
588 addMouseMotionListener(mouseHandler);
589 }
590
591 private void createActions()
592 {
593 closeAction = new CloseAction();
594 iconifyAction = new IconifyAction();
595 maximizeAction = new MaximizeAction();
596 }
597
598 private void assembleSystemMenu()
599 {
600 menuBar = createSystemMenuBar();
601 windowMenu = createSystemMenu();
602 menuBar.add(windowMenu);
603 addSystemMenuItems(windowMenu);
604 enableActions();
605 }
606
607 protected JMenuBar createSystemMenuBar()
608 {
609 if (menuBar == null)
610 menuBar = new JMenuBar();
611 menuBar.removeAll();
612 return menuBar;
613 }
614
615 protected JMenu createSystemMenu()
616 {
617 if (windowMenu == null)
618 windowMenu = new JMenu();
619 windowMenu.removeAll();
620 return windowMenu;
621 }
622
623 private void addSystemMenuItems(JMenu menu)
624 {
625 // TODO: Implement this.
626 }
627
628 protected void createButtons()
629 {
630 closeButton = new PaneButton(closeAction);
631 closeButton.setText(null);
632 iconButton = new PaneButton(iconifyAction);
633 iconButton.setText(null);
634 maxButton = new PaneButton(maximizeAction);
635 maxButton.setText(null);
636 closeButton.setBorderPainted(false);
637 closeButton.setContentAreaFilled(false);
638 iconButton.setBorderPainted(false);
639 iconButton.setContentAreaFilled(false);
640 maxButton.setBorderPainted(false);
641 maxButton.setContentAreaFilled(false);
642 }
643
644 protected void setButtonIcons()
645 {
646 if (closeIcon != null && closeButton != null)
647 closeButton.setIcon(closeIcon);
648 if (iconIcon != null && iconButton != null)
649 iconButton.setIcon(iconIcon);
650 if (maxIcon != null && maxButton != null)
651 maxButton.setIcon(maxIcon);
652 }
653
654 /**
655 * Paints a representation of the current state of the internal frame.
656 *
657 * @param g the graphics device.
658 */
659 public void paintComponent(Graphics g)
660 {
661 Window frame = SwingUtilities.getWindowAncestor(rootPane);
662 Color savedColor = g.getColor();
663 paintTitleBackground(g);
664 paintChildren(g);
665 Dimension d = getSize();
666 if (frame.isActive())
667 g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
668 else
669 g.setColor(MetalLookAndFeel.getControlDarkShadow());
670
671 // put a dot in each of the top corners
672 g.drawLine(0, 0, 0, 0);
673 g.drawLine(d.width - 1, 0, d.width - 1, 0);
674
675 g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
676
677 // draw the metal pattern
678 if (UIManager.get("InternalFrame.activeTitleGradient") != null
679 && frame.isActive())
680 {
681 MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(),
682 SwingConstants.VERTICAL,
683 "InternalFrame.activeTitleGradient");
684 }
685
686 Rectangle b = title.getBounds();
687 int startX = b.x + b.width + 5;
688 int endX = startX;
689 if (iconButton.isVisible())
690 endX = Math.max(iconButton.getX(), endX);
691 else if (maxButton.isVisible())
692 endX = Math.max(maxButton.getX(), endX);
693 else if (closeButton.isVisible())
694 endX = Math.max(closeButton.getX(), endX);
695 endX -= 7;
696 if (endX > startX)
697 MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray);
698 g.setColor(savedColor);
699 }
700
701 /**
702 * This method paints the TitlePane's background.
703 *
704 * @param g The Graphics object to paint with.
705 */
706 protected void paintTitleBackground(Graphics g)
707 {
708 Window frame = SwingUtilities.getWindowAncestor(rootPane);
709
710 if (!isOpaque())
711 return;
712
713 Color saved = g.getColor();
714 Dimension dims = getSize();
715
716 Color bg = getBackground();
717 if (frame.isActive())
718 bg = selectedTitleColor;
719 else
720 bg = notSelectedTitleColor;
721 g.setColor(bg);
722 g.fillRect(0, 0, dims.width, dims.height);
723 g.setColor(saved);
724 }
725
726 /**
727 * This method installs the defaults determined by the look and feel.
728 */
729 private void installDefaults()
730 {
731 title.setFont(UIManager.getFont("InternalFrame.titleFont"));
732 selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
733 notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
734 closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
735 iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
736 maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
737 minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16);
738 Frame frame = (Frame) SwingUtilities.getWindowAncestor(rootPane);
739 title = new JLabel(frame.getTitle(),
740 MetalIconFactory.getInternalFrameDefaultMenuIcon(),
741 SwingConstants.LEFT);
742 }
743 }
744
745 private static class MetalRootLayout
746 implements LayoutManager2
747 {
748
749 /**
750 * The cached layout info for the glass pane.
751 */
752 private Rectangle glassPaneBounds;
753
754 /**
755 * The cached layout info for the layered pane.
756 */
757 private Rectangle layeredPaneBounds;
758
759 /**
760 * The cached layout info for the content pane.
761 */
762 private Rectangle contentPaneBounds;
763
764 /**
765 * The cached layout info for the menu bar.
766 */
767 private Rectangle menuBarBounds;
768
769 /**
770 * The cached layout info for the title pane.
771 */
772 private Rectangle titlePaneBounds;
773
774 /**
775 * The cached preferred size.
776 */
777 private Dimension prefSize;
778
779 /**
780 * The title pane for l&f decorated frames.
781 */
782 private MetalTitlePane titlePane;
783
784 /**
785 * Creates a new MetalRootLayout.
786 *
787 * @param tp the title pane
788 */
789 MetalRootLayout(MetalTitlePane tp)
790 {
791 titlePane = tp;
792 }
793
794 public void addLayoutComponent(Component component, Object constraints)
795 {
796 // Nothing to do here.
797 }
798
799 public Dimension maximumLayoutSize(Container target)
800 {
801 return preferredLayoutSize(target);
802 }
803
804 public float getLayoutAlignmentX(Container target)
805 {
806 return 0.0F;
807 }
808
809 public float getLayoutAlignmentY(Container target)
810 {
811 return 0.0F;
812 }
813
814 public void invalidateLayout(Container target)
815 {
816 synchronized (this)
817 {
818 glassPaneBounds = null;
819 layeredPaneBounds = null;
820 contentPaneBounds = null;
821 menuBarBounds = null;
822 titlePaneBounds = null;
823 prefSize = null;
824 }
825 }
826
827 public void addLayoutComponent(String name, Component component)
828 {
829 // Nothing to do here.
830 }
831
832 public void removeLayoutComponent(Component component)
833 {
834 // TODO Auto-generated method stub
835
836 }
837
838 public Dimension preferredLayoutSize(Container parent)
839 {
840 JRootPane rp = (JRootPane) parent;
841 JLayeredPane layeredPane = rp.getLayeredPane();
842 Component contentPane = rp.getContentPane();
843 Component menuBar = rp.getJMenuBar();
844
845 // We must synchronize here, otherwise we cannot guarantee that the
846 // prefSize is still non-null when returning.
847 synchronized (this)
848 {
849 if (prefSize == null)
850 {
851 Insets i = parent.getInsets();
852 prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
853 Dimension contentPrefSize = contentPane.getPreferredSize();
854 prefSize.width += contentPrefSize.width;
855 prefSize.height += contentPrefSize.height
856 + titlePane.getPreferredSize().height;
857 if (menuBar != null)
858 {
859 Dimension menuBarSize = menuBar.getPreferredSize();
860 if (menuBarSize.width > contentPrefSize.width)
861 prefSize.width += menuBarSize.width - contentPrefSize.width;
862 prefSize.height += menuBarSize.height;
863 }
864 }
865 // Return a copy here so the cached value won't get trashed by some
866 // other component.
867 return new Dimension(prefSize);
868 }
869 }
870
871 public Dimension minimumLayoutSize(Container parent)
872 {
873 return preferredLayoutSize(parent);
874 }
875
876 public void layoutContainer(Container parent)
877 {
878 JRootPane rp = (JRootPane) parent;
879 JLayeredPane layeredPane = rp.getLayeredPane();
880 Component contentPane = rp.getContentPane();
881 Component menuBar = rp.getJMenuBar();
882 Component glassPane = rp.getGlassPane();
883
884 if (glassPaneBounds == null || layeredPaneBounds == null
885 || contentPaneBounds == null || menuBarBounds == null)
886 {
887 Insets i = rp.getInsets();
888 int containerWidth = parent.getBounds().width - i.left - i.right;
889 int containerHeight = parent.getBounds().height - i.top - i.bottom;
890
891 // 1. The glassPane fills entire viewable region (bounds - insets).
892 // 2. The layeredPane filles entire viewable region.
893 // 3. The titlePane is placed at the upper edge of the layeredPane.
894 // 4. The menuBar is positioned at the upper edge of layeredPane.
895 // 5. The contentPane fills viewable region minus menuBar minus
896 // titlePane, if present.
897
898 // +-------------------------------+
899 // | JLayeredPane |
900 // | +--------------------------+ |
901 // | | titlePane + |
902 // | +--------------------------+ |
903 // | +--------------------------+ |
904 // | | menuBar | |
905 // | +--------------------------+ |
906 // | +--------------------------+ |
907 // | |contentPane | |
908 // | | | |
909 // | | | |
910 // | | | |
911 // | +--------------------------+ |
912 // +-------------------------------+
913
914 // Setup titlePaneBounds.
915 if (titlePaneBounds == null)
916 titlePaneBounds = new Rectangle();
917 titlePaneBounds.width = containerWidth;
918 titlePaneBounds.height = titlePane.getPreferredSize().height;
919
920 // Setup menuBarBounds.
921 if (menuBarBounds == null)
922 menuBarBounds = new Rectangle();
923 menuBarBounds.setBounds(0,
924 titlePaneBounds.y + titlePaneBounds.height,
925 containerWidth, 0);
926 if (menuBar != null)
927 {
928 Dimension menuBarSize = menuBar.getPreferredSize();
929 if (menuBarSize.height > containerHeight)
930 menuBarBounds.height = containerHeight;
931 else
932 menuBarBounds.height = menuBarSize.height;
933 }
934
935 // Setup contentPaneBounds.
936 if (contentPaneBounds == null)
937 contentPaneBounds = new Rectangle();
938 contentPaneBounds.setBounds(0,
939 menuBarBounds.y + menuBarBounds.height,
940 containerWidth,
941 containerHeight - menuBarBounds.y
942 - menuBarBounds.height);
943 glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
944 layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
945 }
946
947 // Layout components.
948 glassPane.setBounds(glassPaneBounds);
949 layeredPane.setBounds(layeredPaneBounds);
950 if (menuBar != null)
951 menuBar.setBounds(menuBarBounds);
952 contentPane.setBounds(contentPaneBounds);
953 titlePane.setBounds(titlePaneBounds);
954 }
955
956 }
957
958 /**
959 * The shared UI instance for MetalRootPaneUIs.
960 */
961 private static MetalRootPaneUI instance;
962
963 /**
964 * Constructs a shared instance of <code>MetalRootPaneUI</code>.
965 */
966 public MetalRootPaneUI()
967 {
968 super();
969 }
970
971 /**
972 * Returns a shared instance of <code>MetalRootPaneUI</code>.
973 *
974 * @param component the component for which we return an UI instance
975 *
976 * @return A shared instance of <code>MetalRootPaneUI</code>.
977 */
978 public static ComponentUI createUI(JComponent component)
979 {
980 if (instance == null)
981 instance = new MetalRootPaneUI();
982 return instance;
983 }
984
985 /**
986 * Installs this UI to the root pane. If the
987 * <code>windowDecorationsStyle</code> property is set on the root pane,
988 * the Metal window decorations are installed on the root pane.
989 *
990 * @param c
991 */
992 public void installUI(JComponent c)
993 {
994 super.installUI(c);
995 JRootPane rp = (JRootPane) c;
996 if (rp.getWindowDecorationStyle() != JRootPane.NONE)
997 installWindowDecorations(rp);
998 }
999
1000 /**
1001 * Uninstalls the UI from the root pane. This performs the superclass
1002 * behaviour and uninstalls the window decorations that have possibly been
1003 * installed by {@link #installUI}.
1004 *
1005 * @param c the root pane
1006 */
1007 public void uninstallUI(JComponent c)
1008 {
1009 JRootPane rp = (JRootPane) c;
1010 if (rp.getWindowDecorationStyle() != JRootPane.NONE)
1011 uninstallWindowDecorations(rp);
1012 super.uninstallUI(c);
1013 }
1014
1015 /**
1016 * Receives notification if any of the JRootPane's property changes. In
1017 * particular this catches changes to the <code>windowDecorationStyle</code>
1018 * property and installs the window decorations accordingly.
1019 *
1020 * @param ev the property change event
1021 */
1022 public void propertyChange(PropertyChangeEvent ev)
1023 {
1024 super.propertyChange(ev);
1025 String propertyName = ev.getPropertyName();
1026 if (propertyName.equals("windowDecorationStyle"))
1027 {
1028 JRootPane rp = (JRootPane) ev.getSource();
1029 if (rp.getWindowDecorationStyle() != JRootPane.NONE)
1030 installWindowDecorations(rp);
1031 else
1032 uninstallWindowDecorations(rp);
1033 }
1034 }
1035
1036 /**
1037 * Installs the window decorations to the root pane. This sets up a border,
1038 * a title pane and a layout manager that can layout the root pane with that
1039 * title pane.
1040 *
1041 * @param rp the root pane.
1042 */
1043 private void installWindowDecorations(JRootPane rp)
1044 {
1045 rp.setBorder(new MetalFrameBorder());
1046 MetalTitlePane titlePane = new MetalTitlePane(rp);
1047 rp.setLayout(new MetalRootLayout(titlePane));
1048 // We should have a contentPane already.
1049 assert rp.getLayeredPane().getComponentCount() > 0
1050 : "We should have a contentPane already";
1051
1052 rp.getLayeredPane().add(titlePane,
1053 JLayeredPane.FRAME_CONTENT_LAYER, 1);
1054 }
1055
1056 /**
1057 * Uninstalls the window decorations from the root pane. This should rarely
1058 * be necessary, but we do it anyway.
1059 *
1060 * @param rp the root pane
1061 */
1062 private void uninstallWindowDecorations(JRootPane rp)
1063 {
1064 rp.setBorder(null);
1065 JLayeredPane lp = rp.getLayeredPane();
1066 for (int i = lp.getComponentCount() - 1; i >= 0; --i)
1067 {
1068 if (lp.getComponent(i) instanceof MetalTitlePane)
1069 {
1070 lp.remove(i);
1071 break;
1072 }
1073 }
1074 }
1075 }