/******************************************************************************* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tiny Look and Feel * * (C) Copyright 2003 - 2007 Hans Bickel * * For * licensing information and credits, please refer to the * comment in file * de.muntjak.tinylookandfeel.TinyLookAndFeel * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.muntjak.tinylookandfeel; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.LayoutManager2; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; import javax.swing.JLayeredPane; import javax.swing.JRootPane; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRootPaneUI; /* * Provides the metal look and feel implementation of <code>RootPaneUI</code>. * <p> <code>TinyRootPaneUI</code> provides support for the <code>windowDecorationStyle</code> * property of <code>JRootPane</code>. <code>TinyRootPaneUI</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<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. Please see * {@link java.beans.XMLEncoder}. * * @version 1.14 12/03/01 @author Terry Kellerman * @since 1.4 */ /** * TinyRootPaneUI * * @version 1.0 * @author Hans Bickel */ @SuppressWarnings ( { "all" } ) public class TinyRootPaneUI extends BasicRootPaneUI { /** * 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. */ private static final int CORNER_DRAG_WIDTH = 16; /** * Region from edges that dragging is active from. */ private static final int BORDER_DRAG_THICKNESS = 5; /** * Window the <code>JRootPane</code> is in. */ private Window window; /** * <code>JComponent</code> providing window decorations. This will be null * if not providing window decorations. */ private JComponent titlePane; /** * <code>MouseInputListener</code> that is added to the parent * <code>Window</code> the <code>JRootPane</code> is contained in. */ private MouseInputListener mouseInputListener; /** * The <code>LayoutManager</code> that is set on the <code>JRootPane</code>. */ private LayoutManager layoutManager; /** * <code>LayoutManager</code> of the <code>JRootPane</code> before we * replaced it. */ private LayoutManager savedOldLayout; /** * <code>JRootPane</code> providing the look and feel for. */ private JRootPane root; /** * <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 <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 TinyRootPaneUI (); } /** * 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 <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 <code>Border</code> onto the * <code>JRootPane</code>. */ void installBorder ( JRootPane root ) { int style = root.getWindowDecorationStyle (); if ( style == JRootPane.NONE ) { LookAndFeel.uninstallBorder ( root ); } else { // installs an instance of TinyFrameBorder LookAndFeel.installBorder ( root, borderKeys [ style ] ); } } /** * Removes any border that may have been installed. */ private void uninstallBorder ( JRootPane root ) { LookAndFeel.uninstallBorder ( root ); } /** * 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 <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 <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 <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 <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 <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 ); 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 <code>JComponent</code> to render the window decoration * style. */ private JComponent createTitlePane ( JRootPane root ) { return new TinyTitlePane ( root, this ); } /** * 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 <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. * * @param content 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; root.validate (); root.repaint (); } /** * 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 <code>JRootPane</code> we're providing the look and feel for. */ private JRootPane getRootPane () { return root; } /** * Invoked when a property changes. <code>TinyRootPaneUI</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. */ // 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. * * @param 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 TinyRootPaneUI ) ) { JComponent titlePane = ( ( TinyRootPaneUI ) 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 + tpHeight + i.top + i.bottom ); } /** * Returns the minimum amount of space the layout needs. * * @param 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 TinyRootPaneUI ) ) { JComponent titlePane = ( ( TinyRootPaneUI ) 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 + tpHeight + i.top + i.bottom ); } /** * Returns the maximum amount of space the layout can use. * * @param 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 TinyRootPaneUI ) ) { JComponent titlePane = ( ( TinyRootPaneUI ) 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. * * @param 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 TinyRootPaneUI ) ) { JComponent titlePane = ( ( TinyRootPaneUI ) 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. */ 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. */ private class MouseInputHandler implements MouseInputListener { /** * Set to true if the drag operation is moving the window. */ private boolean isMovingWindow; /** * Used to determine the corner the resize is occuring from. */ private int dragCursor; /** * 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. */ private int dragOffsetY; /** * Width of the window when the drag started. */ private int dragWidth; /** * 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 (); 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 ( ev.getClickCount () == 2 ) { if ( f != null && f.isResizable () ) { if ( ( frameState & Frame.MAXIMIZED_HORIZ ) == Frame.MAXIMIZED_HORIZ || ( frameState & Frame.MAXIMIZED_VERT ) == Frame.MAXIMIZED_VERT ) { f.setExtendedState ( frameState & ~Frame.MAXIMIZED_BOTH ); } else { f.setExtendedState ( frameState | Frame.MAXIMIZED_BOTH ); } return; } } if ( ( ( f != null && ( ( frameState & Frame.MAXIMIZED_HORIZ ) != Frame.MAXIMIZED_HORIZ && ( frameState & Frame.MAXIMIZED_VERT ) != Frame.MAXIMIZED_VERT ) ) || ( 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_HORIZ ) != Frame.MAXIMIZED_HORIZ && ( frameState & Frame.MAXIMIZED_VERT ) != Frame.MAXIMIZED_VERT ) ) || ( 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_VERT ) != Frame.MAXIMIZED_VERT && ( f .getExtendedState () & Frame.MAXIMIZED_HORIZ ) != Frame.MAXIMIZED_HORIZ ) ) || ( 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 windowPt = w.getLocationOnScreen (); windowPt.x += pt.x - dragOffsetX; windowPt.y += pt.y - dragOffsetY; w.setLocation ( windowPt ); } 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. // REM: not active on my system... (Win 2000 Server) 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 ); } public void mouseClicked ( MouseEvent ev ) { } /** * 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 ( Component c, int x, int y ) { int xPosition = calculatePosition ( x, c.getWidth () ); int yPosition = calculatePosition ( y, c.getHeight () ); 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 */ private int getCursor ( int corner ) { if ( corner == -1 ) { return 0; } return cursorMapping [ corner ]; } /** * 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; } } }