mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-13 03:29:50 +00:00
1076 lines
31 KiB
Java
1076 lines
31 KiB
Java
/* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|