summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java')
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java
new file mode 100644
index 000000000..84f1303f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java
@@ -0,0 +1,1075 @@
+/* MetalRootPaneUI.java
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JRootPane;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicRootPaneUI;
+
+/**
+ * A UI delegate for the {@link JRootPane} component. This implementation
+ * supports the JRootPane <code>windowDecorationStyle</code> property.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.4
+ */
+public class MetalRootPaneUI
+ extends BasicRootPaneUI
+{
+
+ /**
+ * The border that is used on JRootPane when the windowDecorationStyle
+ * property of the JRootPane is set to a different value than NONE.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class MetalFrameBorder
+ extends AbstractBorder
+ {
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component
+ * @param newInsets the insets to be filled with the return value, may be
+ * <code>null</code> in which case a new object is created
+ *
+ * @return the border insets
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(5, 5, 5, 5);
+ else
+ {
+ newInsets.top = 5;
+ newInsets.left = 5;
+ newInsets.bottom = 5;
+ newInsets.right = 5;
+ }
+ return newInsets;
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component
+ *
+ * @return the border insets
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component
+ * @param g the graphics device
+ * @param x the x-coordinate
+ * @param y the y-coordinate
+ * @param w the width
+ * @param h the height
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ JRootPane f = (JRootPane) c;
+ Window frame = SwingUtilities.getWindowAncestor(f);
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // Fill the border background.
+ g.fillRect(x, y, w, 5);
+ g.fillRect(x, y, 5, h);
+ g.fillRect(x + w - 5, y, 5, h);
+ g.fillRect(x, y + h - 5, w, 5);
+
+ // Draw a dot in each corner.
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x, y, 1, 1);
+ g.fillRect(x + w - 1, y, 1, 1);
+ g.fillRect(x + w - 1, y + h - 1, 1, 1);
+ g.fillRect(x, y + h - 1, 1, 1);
+
+ // Draw the lines.
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
+ g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
+ g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
+ g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
+
+ // Draw the line highlights.
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
+ g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
+ g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
+ g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
+ }
+ }
+
+ /**
+ * The component that renders the title bar for frames. This duplicates
+ * most of {@link MetalInternalFrameTitlePane}. It is not reasonably possible
+ * to reuse that class because that is bound to the JInternalFrame and we
+ * need to handle JFrames/JRootPanes here.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class MetalTitlePane extends JComponent
+ {
+
+ /**
+ * Handles dragging of the title pane and moves the window accordingly.
+ */
+ private class MouseHandler
+ extends MouseInputAdapter
+ {
+ /**
+ * The point where the dragging started.
+ */
+ Point lastDragLocation;
+
+ /**
+ * Receives notification when the mouse gets pressed on the title pane.
+ * This updates the lastDragLocation.
+ *
+ * @param ev the mouse event
+ */
+ public void mousePressed(MouseEvent ev)
+ {
+ lastDragLocation = ev.getPoint();
+ }
+
+ /**
+ * Receives notification when the mouse is dragged on the title pane.
+ * This will move the nearest window accordingly.
+ *
+ * @param ev the mouse event
+ */
+ public void mouseDragged(MouseEvent ev)
+ {
+ Point dragLocation = ev.getPoint();
+ int deltaX = dragLocation.x - lastDragLocation.x;
+ int deltaY = dragLocation.y - lastDragLocation.y;
+ Window window = SwingUtilities.getWindowAncestor(rootPane);
+ Point loc = window.getLocation();
+ window.setLocation(loc.x + deltaX, loc.y + deltaY);
+ // Note that we do not update the lastDragLocation. This is because
+ // we move the underlying window while dragging the component, which
+ // results in having the same lastDragLocation under the mouse while
+ // dragging.
+ }
+ }
+
+ /**
+ * The Action responsible for closing the JInternalFrame.
+ */
+ private class CloseAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public CloseAction()
+ {
+ super("Close");
+ }
+
+ /**
+ * This method is called when something closes the frame.
+ *
+ * @param e the ActionEvent
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+ if (frame instanceof JFrame)
+ {
+ JFrame jframe = (JFrame) frame;
+ switch (jframe.getDefaultCloseOperation())
+ {
+ case JFrame.EXIT_ON_CLOSE:
+ jframe.setVisible(false);
+ jframe.dispose();
+ System.exit(0);
+ break;
+ case JFrame.DISPOSE_ON_CLOSE:
+ jframe.setVisible(false);
+ jframe.dispose();
+ break;
+ case JFrame.HIDE_ON_CLOSE:
+ jframe.setVisible(false);
+ break;
+ case JFrame.DO_NOTHING_ON_CLOSE:
+ default:
+ break;
+ }
+ }
+ else if (frame instanceof JDialog)
+ {
+ JDialog jdialog = (JDialog) frame;
+ switch (jdialog.getDefaultCloseOperation())
+ {
+ case JFrame.DISPOSE_ON_CLOSE:
+ jdialog.setVisible(false);
+ jdialog.dispose();
+ break;
+ case JFrame.HIDE_ON_CLOSE:
+ jdialog.setVisible(false);
+ break;
+ case JFrame.DO_NOTHING_ON_CLOSE:
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This action is performed when the iconify button is pressed.
+ */
+ private class IconifyAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ Window w = SwingUtilities.getWindowAncestor(rootPane);
+ if (w instanceof Frame)
+ {
+ Frame f = (Frame) w;
+ int state = f.getExtendedState();
+ f.setExtendedState(Frame.ICONIFIED);
+ }
+ }
+
+ }
+
+ /**
+ * This action is performed when the maximize button is pressed.
+ */
+ private class MaximizeAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ Window w = SwingUtilities.getWindowAncestor(rootPane);
+ if (w instanceof Frame)
+ {
+ Frame f = (Frame) w;
+ int state = f.getExtendedState();
+ f.setExtendedState(Frame.MAXIMIZED_BOTH);
+ }
+ }
+ }
+
+ /**
+ * This helper class is used to create the minimize, maximize and close
+ * buttons in the top right corner of the Title Pane. These buttons are
+ * special since they cannot be given focus and have no border.
+ */
+ private class PaneButton extends JButton
+ {
+ /**
+ * Creates a new PaneButton object with the given Action.
+ *
+ * @param a The Action that the button uses.
+ */
+ public PaneButton(Action a)
+ {
+ super(a);
+ setMargin(new Insets(0, 0, 0, 0));
+ }
+
+ /**
+ * This method returns true if the Component can be focused.
+ *
+ * @return false.
+ */
+ public boolean isFocusable()
+ {
+ // These buttons cannot be given focus.
+ return false;
+ }
+
+ }
+
+ /**
+ * The layout for the JRootPane when the <code>windowDecorationStyle</code>
+ * property is set. In addition to the usual JRootPane.RootLayout behaviour
+ * this lays out the titlePane.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class MetalTitlePaneLayout implements LayoutManager
+ {
+ /**
+ * Creates a new <code>TitlePaneLayout</code> object.
+ */
+ public MetalTitlePaneLayout()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Adds a Component to the Container.
+ *
+ * @param name The name to reference the added Component by.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the children of the Title Pane.
+ *
+ * @param c The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+
+ Dimension size = c.getSize();
+ Insets insets = c.getInsets();
+ int width = size.width - insets.left - insets.right;
+ int height = size.height - insets.top - insets.bottom;
+
+ int loc = width - insets.right - 1;
+ int top = insets.top + 2;
+ int buttonHeight = height - 4;
+ if (closeButton.isVisible())
+ {
+ int buttonWidth = closeIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 6;
+ }
+
+ if (maxButton.isVisible())
+ {
+ int buttonWidth = maxIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (iconButton.isVisible())
+ {
+ int buttonWidth = minIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 2;
+ }
+
+ Dimension titlePreferredSize = title.getPreferredSize();
+ title.setBounds(insets.left + 5, insets.top,
+ Math.min(titlePreferredSize.width, loc - insets.left - 10),
+ height);
+
+ }
+
+ /**
+ * This method returns the minimum size of the given Container given the
+ * children that it has.
+ *
+ * @param c The Container to get a minimum size for.
+ *
+ * @return The minimum size of the Container.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * Returns the preferred size of the given Container taking
+ * into account the children that it has.
+ *
+ * @param c The Container to lay out.
+ *
+ * @return The preferred size of the Container.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return new Dimension(22, 22);
+ }
+
+ /**
+ * Removes a Component from the Container.
+ *
+ * @param c The Component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ JRootPane rootPane;
+
+ /** The button that closes the JInternalFrame. */
+ JButton closeButton;
+
+ /** The button that iconifies the JInternalFrame. */
+ JButton iconButton;
+
+ /** The button that maximizes the JInternalFrame. */
+ JButton maxButton;
+
+ Icon minIcon;
+
+ /** The icon displayed in the maximize button. */
+ Icon maxIcon;
+
+ /** The icon displayed in the iconify button. */
+ private Icon iconIcon;
+
+ /** The icon displayed in the close button. */
+ Icon closeIcon;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is not
+ * selected.
+ */
+ private Color notSelectedTitleColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is
+ * selected.
+ */
+ private Color selectedTitleColor;
+
+ /**
+ * The label used to display the title. This label is not added to the
+ * TitlePane.
+ */
+ JLabel title;
+
+ /** The action associated with closing the JInternalFrame. */
+ private Action closeAction;
+
+ /** The action associated with iconifying the JInternalFrame. */
+ private Action iconifyAction;
+
+ /** The action associated with maximizing the JInternalFrame. */
+ private Action maximizeAction;
+
+ /** The JMenuBar that is located at the top left of the Title Pane. */
+ private JMenuBar menuBar;
+
+ /** The JMenu inside the menuBar. */
+ protected JMenu windowMenu;
+
+ MetalTitlePane(JRootPane rp)
+ {
+ rootPane = rp;
+ setLayout(createLayout());
+ title = new JLabel();
+ title.setHorizontalAlignment(SwingConstants.LEFT);
+ title.setHorizontalTextPosition(SwingConstants.LEFT);
+ title.setOpaque(false);
+ installTitlePane();
+ }
+
+ protected LayoutManager createLayout()
+ {
+ return new MetalTitlePaneLayout();
+ }
+
+ /**
+ * This method installs the TitlePane onto the JInternalFrameTitlePane. It
+ * also creates any children components that need to be created and adds
+ * listeners to the appropriate components.
+ */
+ protected void installTitlePane()
+ {
+ installDefaults();
+ installListeners();
+ createActions();
+ assembleSystemMenu();
+ createButtons();
+ setButtonIcons();
+ addSubComponents();
+ enableActions();
+ }
+
+ private void enableActions()
+ {
+ // TODO: Implement this.
+ }
+
+ private void addSubComponents()
+ {
+ add(menuBar);
+ add(closeButton);
+ add(iconButton);
+ add(maxButton);
+ }
+
+ private void installListeners()
+ {
+ MouseInputAdapter mouseHandler = new MouseHandler();
+ addMouseListener(mouseHandler);
+ addMouseMotionListener(mouseHandler);
+ }
+
+ private void createActions()
+ {
+ closeAction = new CloseAction();
+ iconifyAction = new IconifyAction();
+ maximizeAction = new MaximizeAction();
+ }
+
+ private void assembleSystemMenu()
+ {
+ menuBar = createSystemMenuBar();
+ windowMenu = createSystemMenu();
+ menuBar.add(windowMenu);
+ addSystemMenuItems(windowMenu);
+ enableActions();
+ }
+
+ protected JMenuBar createSystemMenuBar()
+ {
+ if (menuBar == null)
+ menuBar = new JMenuBar();
+ menuBar.removeAll();
+ return menuBar;
+ }
+
+ protected JMenu createSystemMenu()
+ {
+ if (windowMenu == null)
+ windowMenu = new JMenu();
+ windowMenu.removeAll();
+ return windowMenu;
+ }
+
+ private void addSystemMenuItems(JMenu menu)
+ {
+ // TODO: Implement this.
+ }
+
+ protected void createButtons()
+ {
+ closeButton = new PaneButton(closeAction);
+ closeButton.setText(null);
+ iconButton = new PaneButton(iconifyAction);
+ iconButton.setText(null);
+ maxButton = new PaneButton(maximizeAction);
+ maxButton.setText(null);
+ closeButton.setBorderPainted(false);
+ closeButton.setContentAreaFilled(false);
+ iconButton.setBorderPainted(false);
+ iconButton.setContentAreaFilled(false);
+ maxButton.setBorderPainted(false);
+ maxButton.setContentAreaFilled(false);
+ }
+
+ protected void setButtonIcons()
+ {
+ if (closeIcon != null && closeButton != null)
+ closeButton.setIcon(closeIcon);
+ if (iconIcon != null && iconButton != null)
+ iconButton.setIcon(iconIcon);
+ if (maxIcon != null && maxButton != null)
+ maxButton.setIcon(maxIcon);
+ }
+
+ /**
+ * Paints a representation of the current state of the internal frame.
+ *
+ * @param g the graphics device.
+ */
+ public void paintComponent(Graphics g)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+ Color savedColor = g.getColor();
+ paintTitleBackground(g);
+ paintChildren(g);
+ Dimension d = getSize();
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // put a dot in each of the top corners
+ g.drawLine(0, 0, 0, 0);
+ g.drawLine(d.width - 1, 0, d.width - 1, 0);
+
+ g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
+
+ // draw the metal pattern
+ if (UIManager.get("InternalFrame.activeTitleGradient") != null
+ && frame.isActive())
+ {
+ MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(),
+ SwingConstants.VERTICAL,
+ "InternalFrame.activeTitleGradient");
+ }
+
+ Rectangle b = title.getBounds();
+ int startX = b.x + b.width + 5;
+ int endX = startX;
+ if (iconButton.isVisible())
+ endX = Math.max(iconButton.getX(), endX);
+ else if (maxButton.isVisible())
+ endX = Math.max(maxButton.getX(), endX);
+ else if (closeButton.isVisible())
+ endX = Math.max(closeButton.getX(), endX);
+ endX -= 7;
+ if (endX > startX)
+ MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * This method paints the TitlePane's background.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintTitleBackground(Graphics g)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+
+ if (!isOpaque())
+ return;
+
+ Color saved = g.getColor();
+ Dimension dims = getSize();
+
+ Color bg = getBackground();
+ if (frame.isActive())
+ bg = selectedTitleColor;
+ else
+ bg = notSelectedTitleColor;
+ g.setColor(bg);
+ g.fillRect(0, 0, dims.width, dims.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method installs the defaults determined by the look and feel.
+ */
+ private void installDefaults()
+ {
+ title.setFont(UIManager.getFont("InternalFrame.titleFont"));
+ selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
+ notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
+ closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
+ iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
+ maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
+ minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16);
+ Frame frame = (Frame) SwingUtilities.getWindowAncestor(rootPane);
+ title = new JLabel(frame.getTitle(),
+ MetalIconFactory.getInternalFrameDefaultMenuIcon(),
+ SwingConstants.LEFT);
+ }
+ }
+
+ private static class MetalRootLayout
+ implements LayoutManager2
+ {
+
+ /**
+ * The cached layout info for the glass pane.
+ */
+ private Rectangle glassPaneBounds;
+
+ /**
+ * The cached layout info for the layered pane.
+ */
+ private Rectangle layeredPaneBounds;
+
+ /**
+ * The cached layout info for the content pane.
+ */
+ private Rectangle contentPaneBounds;
+
+ /**
+ * The cached layout info for the menu bar.
+ */
+ private Rectangle menuBarBounds;
+
+ /**
+ * The cached layout info for the title pane.
+ */
+ private Rectangle titlePaneBounds;
+
+ /**
+ * The cached preferred size.
+ */
+ private Dimension prefSize;
+
+ /**
+ * The title pane for l&f decorated frames.
+ */
+ private MetalTitlePane titlePane;
+
+ /**
+ * Creates a new MetalRootLayout.
+ *
+ * @param tp the title pane
+ */
+ MetalRootLayout(MetalTitlePane tp)
+ {
+ titlePane = tp;
+ }
+
+ public void addLayoutComponent(Component component, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0F;
+ }
+
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0F;
+ }
+
+ public void invalidateLayout(Container target)
+ {
+ synchronized (this)
+ {
+ glassPaneBounds = null;
+ layeredPaneBounds = null;
+ contentPaneBounds = null;
+ menuBarBounds = null;
+ titlePaneBounds = null;
+ prefSize = null;
+ }
+ }
+
+ public void addLayoutComponent(String name, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ public void removeLayoutComponent(Component component)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ JRootPane rp = (JRootPane) parent;
+ JLayeredPane layeredPane = rp.getLayeredPane();
+ Component contentPane = rp.getContentPane();
+ Component menuBar = rp.getJMenuBar();
+
+ // We must synchronize here, otherwise we cannot guarantee that the
+ // prefSize is still non-null when returning.
+ synchronized (this)
+ {
+ if (prefSize == null)
+ {
+ Insets i = parent.getInsets();
+ prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
+ Dimension contentPrefSize = contentPane.getPreferredSize();
+ prefSize.width += contentPrefSize.width;
+ prefSize.height += contentPrefSize.height
+ + titlePane.getPreferredSize().height;
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.width > contentPrefSize.width)
+ prefSize.width += menuBarSize.width - contentPrefSize.width;
+ prefSize.height += menuBarSize.height;
+ }
+ }
+ // Return a copy here so the cached value won't get trashed by some
+ // other component.
+ return new Dimension(prefSize);
+ }
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ public void layoutContainer(Container parent)
+ {
+ JRootPane rp = (JRootPane) parent;
+ JLayeredPane layeredPane = rp.getLayeredPane();
+ Component contentPane = rp.getContentPane();
+ Component menuBar = rp.getJMenuBar();
+ Component glassPane = rp.getGlassPane();
+
+ if (glassPaneBounds == null || layeredPaneBounds == null
+ || contentPaneBounds == null || menuBarBounds == null)
+ {
+ Insets i = rp.getInsets();
+ int containerWidth = parent.getBounds().width - i.left - i.right;
+ int containerHeight = parent.getBounds().height - i.top - i.bottom;
+
+ // 1. The glassPane fills entire viewable region (bounds - insets).
+ // 2. The layeredPane filles entire viewable region.
+ // 3. The titlePane is placed at the upper edge of the layeredPane.
+ // 4. The menuBar is positioned at the upper edge of layeredPane.
+ // 5. The contentPane fills viewable region minus menuBar minus
+ // titlePane, if present.
+
+ // +-------------------------------+
+ // | JLayeredPane |
+ // | +--------------------------+ |
+ // | | titlePane + |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | | menuBar | |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | |contentPane | |
+ // | | | |
+ // | | | |
+ // | | | |
+ // | +--------------------------+ |
+ // +-------------------------------+
+
+ // Setup titlePaneBounds.
+ if (titlePaneBounds == null)
+ titlePaneBounds = new Rectangle();
+ titlePaneBounds.width = containerWidth;
+ titlePaneBounds.height = titlePane.getPreferredSize().height;
+
+ // Setup menuBarBounds.
+ if (menuBarBounds == null)
+ menuBarBounds = new Rectangle();
+ menuBarBounds.setBounds(0,
+ titlePaneBounds.y + titlePaneBounds.height,
+ containerWidth, 0);
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.height > containerHeight)
+ menuBarBounds.height = containerHeight;
+ else
+ menuBarBounds.height = menuBarSize.height;
+ }
+
+ // Setup contentPaneBounds.
+ if (contentPaneBounds == null)
+ contentPaneBounds = new Rectangle();
+ contentPaneBounds.setBounds(0,
+ menuBarBounds.y + menuBarBounds.height,
+ containerWidth,
+ containerHeight - menuBarBounds.y
+ - menuBarBounds.height);
+ glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ }
+
+ // Layout components.
+ glassPane.setBounds(glassPaneBounds);
+ layeredPane.setBounds(layeredPaneBounds);
+ if (menuBar != null)
+ menuBar.setBounds(menuBarBounds);
+ contentPane.setBounds(contentPaneBounds);
+ titlePane.setBounds(titlePaneBounds);
+ }
+
+ }
+
+ /**
+ * The shared UI instance for MetalRootPaneUIs.
+ */
+ private static MetalRootPaneUI instance;
+
+ /**
+ * Constructs a shared instance of <code>MetalRootPaneUI</code>.
+ */
+ public MetalRootPaneUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalRootPaneUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalRootPaneUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalRootPaneUI();
+ return instance;
+ }
+
+ /**
+ * Installs this UI to the root pane. If the
+ * <code>windowDecorationsStyle</code> property is set on the root pane,
+ * the Metal window decorations are installed on the root pane.
+ *
+ * @param c
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JRootPane rp = (JRootPane) c;
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ installWindowDecorations(rp);
+ }
+
+ /**
+ * Uninstalls the UI from the root pane. This performs the superclass
+ * behaviour and uninstalls the window decorations that have possibly been
+ * installed by {@link #installUI}.
+ *
+ * @param c the root pane
+ */
+ public void uninstallUI(JComponent c)
+ {
+ JRootPane rp = (JRootPane) c;
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ uninstallWindowDecorations(rp);
+ super.uninstallUI(c);
+ }
+
+ /**
+ * Receives notification if any of the JRootPane's property changes. In
+ * particular this catches changes to the <code>windowDecorationStyle</code>
+ * property and installs the window decorations accordingly.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ super.propertyChange(ev);
+ String propertyName = ev.getPropertyName();
+ if (propertyName.equals("windowDecorationStyle"))
+ {
+ JRootPane rp = (JRootPane) ev.getSource();
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ installWindowDecorations(rp);
+ else
+ uninstallWindowDecorations(rp);
+ }
+ }
+
+ /**
+ * Installs the window decorations to the root pane. This sets up a border,
+ * a title pane and a layout manager that can layout the root pane with that
+ * title pane.
+ *
+ * @param rp the root pane.
+ */
+ private void installWindowDecorations(JRootPane rp)
+ {
+ rp.setBorder(new MetalFrameBorder());
+ MetalTitlePane titlePane = new MetalTitlePane(rp);
+ rp.setLayout(new MetalRootLayout(titlePane));
+ // We should have a contentPane already.
+ assert rp.getLayeredPane().getComponentCount() > 0
+ : "We should have a contentPane already";
+
+ rp.getLayeredPane().add(titlePane,
+ JLayeredPane.FRAME_CONTENT_LAYER, 1);
+ }
+
+ /**
+ * Uninstalls the window decorations from the root pane. This should rarely
+ * be necessary, but we do it anyway.
+ *
+ * @param rp the root pane
+ */
+ private void uninstallWindowDecorations(JRootPane rp)
+ {
+ rp.setBorder(null);
+ JLayeredPane lp = rp.getLayeredPane();
+ for (int i = lp.getComponentCount() - 1; i >= 0; --i)
+ {
+ if (lp.getComponent(i) instanceof MetalTitlePane)
+ {
+ lp.remove(i);
+ break;
+ }
+ }
+ }
+}