/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.plaf.metal;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.awt.*;
Provides the metal look and feel implementation of RootPaneUI
.
MetalRootPaneUI
provides support for the
windowDecorationStyle
property of JRootPane
.
MetalRootPaneUI
does this by way of installing a custom
LayoutManager
, a private Component
to render
the appropriate widgets, and a private Border
. The
LayoutManager
is always installed, regardless of the value of
the windowDecorationStyle
property, but the
Border
and Component
are only installed/added if
the windowDecorationStyle
is other than
JRootPane.NONE
.
Warning:
Serialized objects of this class will not be compatible with
future Swing releases. The current serialization support is
appropriate for short term storage or RMI between applications running
the same version of Swing. As of 1.4, support for long term storage
of all JavaBeans™
has been added to the java.beans
package. Please see XMLEncoder
.
Author: Terry Kellerman Since: 1.4
/**
* Provides the metal look and feel implementation of <code>RootPaneUI</code>.
* <p>
* <code>MetalRootPaneUI</code> provides support for the
* <code>windowDecorationStyle</code> property of <code>JRootPane</code>.
* <code>MetalRootPaneUI</code> does this by way of installing a custom
* <code>LayoutManager</code>, a private <code>Component</code> to render
* the appropriate widgets, and a private <code>Border</code>. The
* <code>LayoutManager</code> is always installed, regardless of the value of
* the <code>windowDecorationStyle</code> property, but the
* <code>Border</code> and <code>Component</code> are only installed/added if
* the <code>windowDecorationStyle</code> is other than
* <code>JRootPane.NONE</code>.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans™
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Terry Kellerman
* @since 1.4
*/
@SuppressWarnings("serial") // Same-version serialization only
public class MetalRootPaneUI extends BasicRootPaneUI
{
Keys to lookup borders in defaults table.
/**
* Keys to lookup borders in defaults table.
*/
private static final String[] borderKeys = new String[] {
null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
"RootPane.informationDialogBorder",
"RootPane.errorDialogBorder", "RootPane.colorChooserDialogBorder",
"RootPane.fileChooserDialogBorder", "RootPane.questionDialogBorder",
"RootPane.warningDialogBorder"
};
The amount of space (in pixels) that the cursor is changed on.
/**
* The amount of space (in pixels) that the cursor is changed on.
*/
private static final int CORNER_DRAG_WIDTH = 16;
Region from edges that dragging is active from.
/**
* Region from edges that dragging is active from.
*/
private static final int BORDER_DRAG_THICKNESS = 5;
Window the JRootPane
is in.
/**
* Window the <code>JRootPane</code> is in.
*/
private Window window;
JComponent
providing window decorations. This will be
null if not providing window decorations.
/**
* <code>JComponent</code> providing window decorations. This will be
* null if not providing window decorations.
*/
private JComponent titlePane;
MouseInputListener
that is added to the parent
Window
the JRootPane
is contained in.
/**
* <code>MouseInputListener</code> that is added to the parent
* <code>Window</code> the <code>JRootPane</code> is contained in.
*/
private MouseInputListener mouseInputListener;
The LayoutManager
that is set on the
JRootPane
.
/**
* The <code>LayoutManager</code> that is set on the
* <code>JRootPane</code>.
*/
private LayoutManager layoutManager;
LayoutManager
of the JRootPane
before we
replaced it.
/**
* <code>LayoutManager</code> of the <code>JRootPane</code> before we
* replaced it.
*/
private LayoutManager savedOldLayout;
JRootPane
providing the look and feel for.
/**
* <code>JRootPane</code> providing the look and feel for.
*/
private JRootPane root;
Cursor
used to track the cursor set by the user.
This is initially Cursor.DEFAULT_CURSOR
.
/**
* <code>Cursor</code> used to track the cursor set by the user.
* This is initially <code>Cursor.DEFAULT_CURSOR</code>.
*/
private Cursor lastCursor =
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
Creates a UI for a JRootPane
.
Params: - c – the JRootPane the RootPaneUI will be created for
Returns: the RootPaneUI implementation for the passed in JRootPane
/**
* Creates a UI for a <code>JRootPane</code>.
*
* @param c the JRootPane the RootPaneUI will be created for
* @return the RootPaneUI implementation for the passed in JRootPane
*/
public static ComponentUI createUI(JComponent c) {
return new MetalRootPaneUI();
}
Invokes supers implementation of installUI
to install
the necessary state onto the passed in JRootPane
to render the metal look and feel implementation of
RootPaneUI
. If
the windowDecorationStyle
property of the
JRootPane
is other than JRootPane.NONE
,
this will add a custom Component
to render the widgets to
JRootPane
, as well as installing a custom
Border
and LayoutManager
on the
JRootPane
.
Params: - c – the JRootPane to install state onto
/**
* Invokes supers implementation of <code>installUI</code> to install
* the necessary state onto the passed in <code>JRootPane</code>
* to render the metal look and feel implementation of
* <code>RootPaneUI</code>. If
* the <code>windowDecorationStyle</code> property of the
* <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
* this will add a custom <code>Component</code> to render the widgets to
* <code>JRootPane</code>, as well as installing a custom
* <code>Border</code> and <code>LayoutManager</code> on the
* <code>JRootPane</code>.
*
* @param c the JRootPane to install state onto
*/
public void installUI(JComponent c) {
super.installUI(c);
root = (JRootPane)c;
int style = root.getWindowDecorationStyle();
if (style != JRootPane.NONE) {
installClientDecorations(root);
}
}
Invokes supers implementation to uninstall any of its state. This will
also reset the LayoutManager
of the JRootPane
.
If a Component
has been added to the JRootPane
to render the window decoration style, this method will remove it.
Similarly, this will revert the Border and LayoutManager of the
JRootPane
to what it was before installUI
was invoked.
Params: - c – the JRootPane to uninstall state from
/**
* Invokes supers implementation to uninstall any of its state. This will
* also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
* If a <code>Component</code> has been added to the <code>JRootPane</code>
* to render the window decoration style, this method will remove it.
* Similarly, this will revert the Border and LayoutManager of the
* <code>JRootPane</code> to what it was before <code>installUI</code>
* was invoked.
*
* @param c the JRootPane to uninstall state from
*/
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
uninstallClientDecorations(root);
layoutManager = null;
mouseInputListener = null;
root = null;
}
Installs the appropriate Border
onto the
JRootPane
.
/**
* Installs the appropriate <code>Border</code> onto the
* <code>JRootPane</code>.
*/
void installBorder(JRootPane root) {
int style = root.getWindowDecorationStyle();
if (style == JRootPane.NONE) {
LookAndFeel.uninstallBorder(root);
}
else {
LookAndFeel.installBorder(root, borderKeys[style]);
}
}
Removes any border that may have been installed.
/**
* Removes any border that may have been installed.
*/
private void uninstallBorder(JRootPane root) {
LookAndFeel.uninstallBorder(root);
}
Installs the necessary Listeners on the parent Window
,
if there is one.
This takes the parent so that cleanup can be done from
removeNotify
, at which point the parent hasn't been
reset yet.
Params: - parent – The parent of the JRootPane
/**
* Installs the necessary Listeners on the parent <code>Window</code>,
* if there is one.
* <p>
* This takes the parent so that cleanup can be done from
* <code>removeNotify</code>, at which point the parent hasn't been
* reset yet.
*
* @param parent The parent of the JRootPane
*/
private void installWindowListeners(JRootPane root, Component parent) {
if (parent instanceof Window) {
window = (Window)parent;
}
else {
window = SwingUtilities.getWindowAncestor(parent);
}
if (window != null) {
if (mouseInputListener == null) {
mouseInputListener = createWindowMouseInputListener(root);
}
window.addMouseListener(mouseInputListener);
window.addMouseMotionListener(mouseInputListener);
}
}
Uninstalls the necessary Listeners on the Window
the
Listeners were last installed on.
/**
* Uninstalls the necessary Listeners on the <code>Window</code> the
* Listeners were last installed on.
*/
private void uninstallWindowListeners(JRootPane root) {
if (window != null) {
window.removeMouseListener(mouseInputListener);
window.removeMouseMotionListener(mouseInputListener);
}
}
Installs the appropriate LayoutManager on the JRootPane
to render the window decorations.
/**
* Installs the appropriate LayoutManager on the <code>JRootPane</code>
* to render the window decorations.
*/
private void installLayout(JRootPane root) {
if (layoutManager == null) {
layoutManager = createLayoutManager();
}
savedOldLayout = root.getLayout();
root.setLayout(layoutManager);
}
Uninstalls the previously installed LayoutManager
.
/**
* Uninstalls the previously installed <code>LayoutManager</code>.
*/
private void uninstallLayout(JRootPane root) {
if (savedOldLayout != null) {
root.setLayout(savedOldLayout);
savedOldLayout = null;
}
}
Installs the necessary state onto the JRootPane to render client
decorations. This is ONLY invoked if the JRootPane
has a decoration style other than JRootPane.NONE
.
/**
* Installs the necessary state onto the JRootPane to render client
* decorations. This is ONLY invoked if the <code>JRootPane</code>
* has a decoration style other than <code>JRootPane.NONE</code>.
*/
private void installClientDecorations(JRootPane root) {
installBorder(root);
JComponent titlePane = createTitlePane(root);
setTitlePane(root, titlePane);
installWindowListeners(root, root.getParent());
installLayout(root);
if (window != null) {
root.revalidate();
root.repaint();
}
}
Uninstalls any state that installClientDecorations
has
installed.
NOTE: This may be called if you haven't installed client decorations
yet (ie before installClientDecorations
has been invoked).
/**
* Uninstalls any state that <code>installClientDecorations</code> has
* installed.
* <p>
* NOTE: This may be called if you haven't installed client decorations
* yet (ie before <code>installClientDecorations</code> has been invoked).
*/
private void uninstallClientDecorations(JRootPane root) {
uninstallBorder(root);
uninstallWindowListeners(root);
setTitlePane(root, null);
uninstallLayout(root);
// We have to revalidate/repaint root if the style is JRootPane.NONE
// only. When we needs to call revalidate/repaint with other styles
// the installClientDecorations is always called after this method
// imediatly and it will cause the revalidate/repaint at the proper
// time.
int style = root.getWindowDecorationStyle();
if (style == JRootPane.NONE) {
root.repaint();
root.revalidate();
}
// Reset the cursor, as we may have changed it to a resize cursor
if (window != null) {
window.setCursor(Cursor.getPredefinedCursor
(Cursor.DEFAULT_CURSOR));
}
window = null;
}
Returns the JComponent
to render the window decoration
style.
/**
* Returns the <code>JComponent</code> to render the window decoration
* style.
*/
private JComponent createTitlePane(JRootPane root) {
return new MetalTitlePane(root, this);
}
Returns a MouseListener
that will be added to the
Window
containing the JRootPane
.
/**
* Returns a <code>MouseListener</code> that will be added to the
* <code>Window</code> containing the <code>JRootPane</code>.
*/
private MouseInputListener createWindowMouseInputListener(JRootPane root) {
return new MouseInputHandler();
}
Returns a LayoutManager
that will be set on the
JRootPane
.
/**
* Returns a <code>LayoutManager</code> that will be set on the
* <code>JRootPane</code>.
*/
private LayoutManager createLayoutManager() {
return new MetalRootLayout();
}
Sets the window title pane -- the JComponent used to provide a plaf a
way to override the native operating system's window title pane with
one whose look and feel are controlled by the plaf. The plaf creates
and sets this value; the default is null, implying a native operating
system window title pane.
Params: - titlePane – the
JComponent
to use for the window title pane.
/**
* Sets the window title pane -- the JComponent used to provide a plaf a
* way to override the native operating system's window title pane with
* one whose look and feel are controlled by the plaf. The plaf creates
* and sets this value; the default is null, implying a native operating
* system window title pane.
*
* @param titlePane the <code>JComponent</code> to use for the window title pane.
*/
private void setTitlePane(JRootPane root, JComponent titlePane) {
JLayeredPane layeredPane = root.getLayeredPane();
JComponent oldTitlePane = getTitlePane();
if (oldTitlePane != null) {
oldTitlePane.setVisible(false);
layeredPane.remove(oldTitlePane);
}
if (titlePane != null) {
layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
titlePane.setVisible(true);
}
this.titlePane = titlePane;
}
Returns the JComponent
rendering the title pane. If this
returns null, it implies there is no need to render window decorations.
See Also: Returns: the current window title pane, or null
/**
* Returns the <code>JComponent</code> rendering the title pane. If this
* returns null, it implies there is no need to render window decorations.
*
* @return the current window title pane, or null
* @see #setTitlePane
*/
private JComponent getTitlePane() {
return titlePane;
}
Returns the JRootPane
we're providing the look and
feel for.
/**
* Returns the <code>JRootPane</code> we're providing the look and
* feel for.
*/
private JRootPane getRootPane() {
return root;
}
Invoked when a property changes. MetalRootPaneUI
is
primarily interested in events originating from the
JRootPane
it has been installed on identifying the
property windowDecorationStyle
. If the
windowDecorationStyle
has changed to a value other
than JRootPane.NONE
, this will add a Component
to the JRootPane
to render the window decorations, as well
as installing a Border
on the JRootPane
.
On the other hand, if the windowDecorationStyle
has
changed to JRootPane.NONE
, this will remove the
Component
that has been added to the JRootPane
as well resetting the Border to what it was before
installUI
was invoked.
Params: - e – A PropertyChangeEvent object describing the event source
and the property that has changed.
/**
* Invoked when a property changes. <code>MetalRootPaneUI</code> is
* primarily interested in events originating from the
* <code>JRootPane</code> it has been installed on identifying the
* property <code>windowDecorationStyle</code>. If the
* <code>windowDecorationStyle</code> has changed to a value other
* than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
* to the <code>JRootPane</code> to render the window decorations, as well
* as installing a <code>Border</code> on the <code>JRootPane</code>.
* On the other hand, if the <code>windowDecorationStyle</code> has
* changed to <code>JRootPane.NONE</code>, this will remove the
* <code>Component</code> that has been added to the <code>JRootPane</code>
* as well resetting the Border to what it was before
* <code>installUI</code> was invoked.
*
* @param e A PropertyChangeEvent object describing the event source
* and the property that has changed.
*/
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
String propertyName = e.getPropertyName();
if(propertyName == null) {
return;
}
if(propertyName.equals("windowDecorationStyle")) {
JRootPane root = (JRootPane) e.getSource();
int style = root.getWindowDecorationStyle();
// This is potentially more than needs to be done,
// but it rarely happens and makes the install/uninstall process
// simpler. MetalTitlePane also assumes it will be recreated if
// the decoration style changes.
uninstallClientDecorations(root);
if (style != JRootPane.NONE) {
installClientDecorations(root);
}
}
else if (propertyName.equals("ancestor")) {
uninstallWindowListeners(root);
if (((JRootPane)e.getSource()).getWindowDecorationStyle() !=
JRootPane.NONE) {
installWindowListeners(root, root.getParent());
}
}
return;
}
A custom layout manager that is responsible for the layout of
layeredPane, glassPane, menuBar and titlePane, if one has been
installed.
/**
* A custom layout manager that is responsible for the layout of
* layeredPane, glassPane, menuBar and titlePane, if one has been
* installed.
*/
// NOTE: Ideally this would extends JRootPane.RootLayout, but that
// would force this to be non-static.
private static class MetalRootLayout implements LayoutManager2 {
Returns the amount of space the layout would like to have.
Params: - parent – the Container for which this layout manager is being used
Returns: a Dimension object containing the layout's preferred size
/**
* Returns the amount of space the layout would like to have.
*
* @param parent the Container for which this layout manager is being used
* @return a Dimension object containing the layout's preferred size
*/
public Dimension preferredLayoutSize(Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if(root.getContentPane() != null) {
cpd = root.getContentPane().getPreferredSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if(root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getPreferredSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if (root.getWindowDecorationStyle() != JRootPane.NONE &&
(root.getUI() instanceof MetalRootPaneUI)) {
JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
getTitlePane();
if (titlePane != null) {
tpd = titlePane.getPreferredSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
cpHeight + mbHeight + tpWidth + i.top + i.bottom);
}
Returns the minimum amount of space the layout needs.
Params: - parent – the Container for which this layout manager is being used
Returns: a Dimension object containing the layout's minimum size
/**
* Returns the minimum amount of space the layout needs.
*
* @param parent the Container for which this layout manager is being used
* @return a Dimension object containing the layout's minimum size
*/
public Dimension minimumLayoutSize(Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if(root.getContentPane() != null) {
cpd = root.getContentPane().getMinimumSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if(root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMinimumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if (root.getWindowDecorationStyle() != JRootPane.NONE &&
(root.getUI() instanceof MetalRootPaneUI)) {
JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
getTitlePane();
if (titlePane != null) {
tpd = titlePane.getMinimumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
cpHeight + mbHeight + tpWidth + i.top + i.bottom);
}
Returns the maximum amount of space the layout can use.
Params: - target – the Container for which this layout manager is being used
Returns: a Dimension object containing the layout's maximum size
/**
* Returns the maximum amount of space the layout can use.
*
* @param target the Container for which this layout manager is being used
* @return a Dimension object containing the layout's maximum size
*/
public Dimension maximumLayoutSize(Container target) {
Dimension cpd, mbd, tpd;
int cpWidth = Integer.MAX_VALUE;
int cpHeight = Integer.MAX_VALUE;
int mbWidth = Integer.MAX_VALUE;
int mbHeight = Integer.MAX_VALUE;
int tpWidth = Integer.MAX_VALUE;
int tpHeight = Integer.MAX_VALUE;
Insets i = target.getInsets();
JRootPane root = (JRootPane) target;
if(root.getContentPane() != null) {
cpd = root.getContentPane().getMaximumSize();
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
}
if(root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMaximumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if (root.getWindowDecorationStyle() != JRootPane.NONE &&
(root.getUI() instanceof MetalRootPaneUI)) {
JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
getTitlePane();
if (titlePane != null)
{
tpd = titlePane.getMaximumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
// Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE
// Only will happen if sums to more than 2 billion units. Not likely.
if (maxHeight != Integer.MAX_VALUE) {
maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
}
int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
// Similar overflow comment as above
if (maxWidth != Integer.MAX_VALUE) {
maxWidth += i.left + i.right;
}
return new Dimension(maxWidth, maxHeight);
}
Instructs the layout manager to perform the layout for the specified
container.
Params: - parent – the Container for which this layout manager is being used
/**
* Instructs the layout manager to perform the layout for the specified
* container.
*
* @param parent the Container for which this layout manager is being used
*/
public void layoutContainer(Container parent) {
JRootPane root = (JRootPane) parent;
Rectangle b = root.getBounds();
Insets i = root.getInsets();
int nextY = 0;
int w = b.width - i.right - i.left;
int h = b.height - i.top - i.bottom;
if(root.getLayeredPane() != null) {
root.getLayeredPane().setBounds(i.left, i.top, w, h);
}
if(root.getGlassPane() != null) {
root.getGlassPane().setBounds(i.left, i.top, w, h);
}
// Note: This is laying out the children in the layeredPane,
// technically, these are not our children.
if (root.getWindowDecorationStyle() != JRootPane.NONE &&
(root.getUI() instanceof MetalRootPaneUI)) {
JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
getTitlePane();
if (titlePane != null) {
Dimension tpd = titlePane.getPreferredSize();
if (tpd != null) {
int tpHeight = tpd.height;
titlePane.setBounds(0, 0, w, tpHeight);
nextY += tpHeight;
}
}
}
if(root.getJMenuBar() != null) {
Dimension mbd = root.getJMenuBar().getPreferredSize();
root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
nextY += mbd.height;
}
if(root.getContentPane() != null) {
Dimension cpd = root.getContentPane().getPreferredSize();
root.getContentPane().setBounds(0, nextY, w,
h < nextY ? 0 : h - nextY);
}
}
public void addLayoutComponent(String name, Component comp) {}
public void removeLayoutComponent(Component comp) {}
public void addLayoutComponent(Component comp, Object constraints) {}
public float getLayoutAlignmentX(Container target) { return 0.0f; }
public float getLayoutAlignmentY(Container target) { return 0.0f; }
public void invalidateLayout(Container target) {}
}
Maps from positions to cursor type. Refer to calculateCorner and
calculatePosition for details of this.
/**
* Maps from positions to cursor type. Refer to calculateCorner and
* calculatePosition for details of this.
*/
private static final int[] cursorMapping = new int[]
{ Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR,
Cursor.NE_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
Cursor.NW_RESIZE_CURSOR, 0, 0, 0, Cursor.NE_RESIZE_CURSOR,
Cursor.W_RESIZE_CURSOR, 0, 0, 0, Cursor.E_RESIZE_CURSOR,
Cursor.SW_RESIZE_CURSOR, 0, 0, 0, Cursor.SE_RESIZE_CURSOR,
Cursor.SW_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR
};
MouseInputHandler is responsible for handling resize/moving of
the Window. It sets the cursor directly on the Window when then
mouse moves over a hot spot.
/**
* MouseInputHandler is responsible for handling resize/moving of
* the Window. It sets the cursor directly on the Window when then
* mouse moves over a hot spot.
*/
private class MouseInputHandler implements MouseInputListener {
Set to true if the drag operation is moving the window.
/**
* Set to true if the drag operation is moving the window.
*/
private boolean isMovingWindow;
Used to determine the corner the resize is occurring from.
/**
* Used to determine the corner the resize is occurring from.
*/
private int dragCursor;
X location the mouse went down on for a drag operation.
/**
* X location the mouse went down on for a drag operation.
*/
private int dragOffsetX;
Y location the mouse went down on for a drag operation.
/**
* Y location the mouse went down on for a drag operation.
*/
private int dragOffsetY;
Width of the window when the drag started.
/**
* Width of the window when the drag started.
*/
private int dragWidth;
Height of the window when the drag started.
/**
* Height of the window when the drag started.
*/
private int dragHeight;
public void mousePressed(MouseEvent ev) {
JRootPane rootPane = getRootPane();
if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
return;
}
Point dragWindowOffset = ev.getPoint();
Window w = (Window)ev.getSource();
if (w != null) {
w.toFront();
}
Point convertedDragWindowOffset = SwingUtilities.convertPoint(
w, dragWindowOffset, getTitlePane());
Frame f = null;
Dialog d = null;
if (w instanceof Frame) {
f = (Frame)w;
} else if (w instanceof Dialog) {
d = (Dialog)w;
}
int frameState = (f != null) ? f.getExtendedState() : 0;
if (getTitlePane() != null &&
getTitlePane().contains(convertedDragWindowOffset)) {
if ((f != null && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
|| (d != null))
&& dragWindowOffset.y >= BORDER_DRAG_THICKNESS
&& dragWindowOffset.x >= BORDER_DRAG_THICKNESS
&& dragWindowOffset.x < w.getWidth()
- BORDER_DRAG_THICKNESS) {
isMovingWindow = true;
dragOffsetX = dragWindowOffset.x;
dragOffsetY = dragWindowOffset.y;
}
}
else if (f != null && f.isResizable()
&& ((frameState & Frame.MAXIMIZED_BOTH) == 0)
|| (d != null && d.isResizable())) {
dragOffsetX = dragWindowOffset.x;
dragOffsetY = dragWindowOffset.y;
dragWidth = w.getWidth();
dragHeight = w.getHeight();
dragCursor = getCursor(calculateCorner(
w, dragWindowOffset.x, dragWindowOffset.y));
}
}
public void mouseReleased(MouseEvent ev) {
if (dragCursor != 0 && window != null && !window.isValid()) {
// Some Window systems validate as you resize, others won't,
// thus the check for validity before repainting.
window.validate();
getRootPane().repaint();
}
isMovingWindow = false;
dragCursor = 0;
}
public void mouseMoved(MouseEvent ev) {
JRootPane root = getRootPane();
if (root.getWindowDecorationStyle() == JRootPane.NONE) {
return;
}
Window w = (Window)ev.getSource();
Frame f = null;
Dialog d = null;
if (w instanceof Frame) {
f = (Frame)w;
} else if (w instanceof Dialog) {
d = (Dialog)w;
}
// Update the cursor
int cursor = getCursor(calculateCorner(w, ev.getX(), ev.getY()));
if (cursor != 0 && ((f != null && (f.isResizable() &&
(f.getExtendedState() & Frame.MAXIMIZED_BOTH) == 0))
|| (d != null && d.isResizable()))) {
w.setCursor(Cursor.getPredefinedCursor(cursor));
}
else {
w.setCursor(lastCursor);
}
}
private void adjust(Rectangle bounds, Dimension min, int deltaX,
int deltaY, int deltaWidth, int deltaHeight) {
bounds.x += deltaX;
bounds.y += deltaY;
bounds.width += deltaWidth;
bounds.height += deltaHeight;
if (min != null) {
if (bounds.width < min.width) {
int correction = min.width - bounds.width;
if (deltaX != 0) {
bounds.x -= correction;
}
bounds.width = min.width;
}
if (bounds.height < min.height) {
int correction = min.height - bounds.height;
if (deltaY != 0) {
bounds.y -= correction;
}
bounds.height = min.height;
}
}
}
public void mouseDragged(MouseEvent ev) {
Window w = (Window)ev.getSource();
Point pt = ev.getPoint();
if (isMovingWindow) {
Point eventLocationOnScreen = ev.getLocationOnScreen();
w.setLocation(eventLocationOnScreen.x - dragOffsetX,
eventLocationOnScreen.y - dragOffsetY);
}
else if (dragCursor != 0) {
Rectangle r = w.getBounds();
Rectangle startBounds = new Rectangle(r);
Dimension min = w.getMinimumSize();
switch (dragCursor) {
case Cursor.E_RESIZE_CURSOR:
adjust(r, min, 0, 0, pt.x + (dragWidth - dragOffsetX) -
r.width, 0);
break;
case Cursor.S_RESIZE_CURSOR:
adjust(r, min, 0, 0, 0, pt.y + (dragHeight - dragOffsetY) -
r.height);
break;
case Cursor.N_RESIZE_CURSOR:
adjust(r, min, 0, pt.y -dragOffsetY, 0,
-(pt.y - dragOffsetY));
break;
case Cursor.W_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX, 0,
-(pt.x - dragOffsetX), 0);
break;
case Cursor.NE_RESIZE_CURSOR:
adjust(r, min, 0, pt.y - dragOffsetY,
pt.x + (dragWidth - dragOffsetX) - r.width,
-(pt.y - dragOffsetY));
break;
case Cursor.SE_RESIZE_CURSOR:
adjust(r, min, 0, 0,
pt.x + (dragWidth - dragOffsetX) - r.width,
pt.y + (dragHeight - dragOffsetY) -
r.height);
break;
case Cursor.NW_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX,
pt.y - dragOffsetY,
-(pt.x - dragOffsetX),
-(pt.y - dragOffsetY));
break;
case Cursor.SW_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX, 0,
-(pt.x - dragOffsetX),
pt.y + (dragHeight - dragOffsetY) - r.height);
break;
default:
break;
}
if (!r.equals(startBounds)) {
w.setBounds(r);
// Defer repaint/validate on mouseReleased unless dynamic
// layout is active.
if (Toolkit.getDefaultToolkit().isDynamicLayoutActive()) {
w.validate();
getRootPane().repaint();
}
}
}
}
public void mouseEntered(MouseEvent ev) {
Window w = (Window)ev.getSource();
lastCursor = w.getCursor();
mouseMoved(ev);
}
public void mouseExited(MouseEvent ev) {
Window w = (Window)ev.getSource();
w.setCursor(lastCursor);
}
@SuppressWarnings("deprecation")
public void mouseClicked(MouseEvent ev) {
Window w = (Window)ev.getSource();
Frame f = null;
if (w instanceof Frame) {
f = (Frame)w;
} else {
return;
}
Point convertedPoint = SwingUtilities.convertPoint(
w, ev.getPoint(), getTitlePane());
int state = f.getExtendedState();
if (getTitlePane() != null &&
getTitlePane().contains(convertedPoint)) {
if ((ev.getClickCount() % 2) == 0 &&
((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
if (f.isResizable()) {
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
}
else {
f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
}
return;
}
}
}
}
Returns the corner that contains the point x
,
y
, or -1 if the position doesn't match a corner.
/**
* Returns the corner that contains the point <code>x</code>,
* <code>y</code>, or -1 if the position doesn't match a corner.
*/
private int calculateCorner(Window w, int x, int y) {
Insets insets = w.getInsets();
int xPosition = calculatePosition(x - insets.left,
w.getWidth() - insets.left - insets.right);
int yPosition = calculatePosition(y - insets.top,
w.getHeight() - insets.top - insets.bottom);
if (xPosition == -1 || yPosition == -1) {
return -1;
}
return yPosition * 5 + xPosition;
}
Returns the Cursor to render for the specified corner. This returns
0 if the corner doesn't map to a valid Cursor
/**
* Returns the Cursor to render for the specified corner. This returns
* 0 if the corner doesn't map to a valid Cursor
*/
private int getCursor(int corner) {
if (corner == -1) {
return 0;
}
return cursorMapping[corner];
}
Returns an integer indicating the position of spot
in width
. The return value will be:
0 if < BORDER_DRAG_THICKNESS
1 if < CORNER_DRAG_WIDTH
2 if >= CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS
3 if >= width - CORNER_DRAG_WIDTH
4 if >= width - BORDER_DRAG_THICKNESS
5 otherwise
/**
* Returns an integer indicating the position of <code>spot</code>
* in <code>width</code>. The return value will be:
* 0 if < BORDER_DRAG_THICKNESS
* 1 if < CORNER_DRAG_WIDTH
* 2 if >= CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS
* 3 if >= width - CORNER_DRAG_WIDTH
* 4 if >= width - BORDER_DRAG_THICKNESS
* 5 otherwise
*/
private int calculatePosition(int spot, int width) {
if (spot < BORDER_DRAG_THICKNESS) {
return 0;
}
if (spot < CORNER_DRAG_WIDTH) {
return 1;
}
if (spot >= (width - BORDER_DRAG_THICKNESS)) {
return 4;
}
if (spot >= (width - CORNER_DRAG_WIDTH)) {
return 3;
}
return 2;
}
}
}