/*
 * Copyright (c) 2011, 2012, 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 com.apple.laf;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;

import apple.laf.*;
import apple.laf.JRSUIConstants.*;

import com.apple.laf.AquaUtils.*;
import com.apple.laf.AquaUtils.Painter;

import sun.lwawt.macosx.CPlatformWindow;

From AquaInternalFrameUI InternalFrame implementation for Aqua LAF We want to inherit most of the inner classes, but not the base class, so be very careful about subclassing so you know you get what you want
/** * From AquaInternalFrameUI * * InternalFrame implementation for Aqua LAF * * We want to inherit most of the inner classes, but not the base class, * so be very careful about subclassing so you know you get what you want * */
public class AquaInternalFrameUI extends BasicInternalFrameUI implements SwingConstants { protected static final String IS_PALETTE_PROPERTY = "JInternalFrame.isPalette"; private static final String FRAME_TYPE = "JInternalFrame.frameType"; private static final String NORMAL_FRAME = "normal"; private static final String PALETTE_FRAME = "palette"; private static final String OPTION_DIALOG = "optionDialog"; // Instance variables PropertyChangeListener fPropertyListener; protected Color fSelectedTextColor; protected Color fNotSelectedTextColor; AquaInternalFrameBorder fAquaBorder; // for button tracking boolean fMouseOverPressedButton; int fWhichButtonPressed = -1; boolean fRollover = false; boolean fDocumentEdited = false; // to indicate whether we should use the dirty document red dot. boolean fIsPallet; public int getWhichButtonPressed() { return fWhichButtonPressed; } public boolean getMouseOverPressedButton() { return fMouseOverPressedButton; } public boolean getRollover() { return fRollover; } // ComponentUI Interface Implementation methods public static ComponentUI createUI(final JComponent b) { return new AquaInternalFrameUI((JInternalFrame)b); } public AquaInternalFrameUI(final JInternalFrame b) { super(b); } /// Inherit (but be careful to check everything they call): public void installUI(final JComponent c) { // super.installUI(c); // Swing 1.1.1 has a bug in installUI - it doesn't check for null northPane frame = (JInternalFrame)c; frame.add(frame.getRootPane(), "Center"); installDefaults(); installListeners(); installComponents(); installKeyboardActions(); Object paletteProp = c.getClientProperty(IS_PALETTE_PROPERTY); if (paletteProp != null) { setPalette(((Boolean)paletteProp).booleanValue()); } else { paletteProp = c.getClientProperty(FRAME_TYPE); if (paletteProp != null) { setFrameType((String)paletteProp); } else { setFrameType(NORMAL_FRAME); } } // We only have a southPane, for grow box room, created in setFrameType frame.setMinimumSize(new Dimension(fIsPallet ? 120 : 150, fIsPallet ? 39 : 65)); frame.setOpaque(false); c.setBorder(new CompoundUIBorder(fIsPallet ? paletteWindowShadow.get() : documentWindowShadow.get(), c.getBorder())); } protected void installDefaults() { super.installDefaults(); fSelectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground"); fNotSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground"); } public void setSouthPane(final JComponent c) { if (southPane != null) { frame.remove(southPane); deinstallMouseHandlers(southPane); } if (c != null) { frame.add(c); installMouseHandlers(c); } southPane = c; } static final RecyclableSingleton<Icon> closeIcon = new RecyclableSingleton<Icon>() { protected Icon getInstance() { return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_CLOSE_BOX); } }; public static Icon exportCloseIcon() { return closeIcon.get(); } static final RecyclableSingleton<Icon> minimizeIcon = new RecyclableSingleton<Icon>() { protected Icon getInstance() { return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_COLLAPSE_BOX); } }; public static Icon exportMinimizeIcon() { return minimizeIcon.get(); } static final RecyclableSingleton<Icon> zoomIcon = new RecyclableSingleton<Icon>() { protected Icon getInstance() { return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_ZOOM_BOX); } }; public static Icon exportZoomIcon() { return zoomIcon.get(); } static class AquaInternalFrameButtonIcon extends AquaIcon.JRSUIIcon { public AquaInternalFrameButtonIcon(final Widget widget) { painter.state.set(widget); } public void paintIcon(final Component c, final Graphics g, final int x, final int y) { painter.state.set(getStateFor(c)); super.paintIcon(c, g, x, y); } State getStateFor(final Component c) { return State.ROLLOVER; } public int getIconWidth() { return 19; } public int getIconHeight() { return 19; } } protected void installKeyboardActions() { } //$ Not Mac-ish - should we support? protected ResizeBox resizeBox; protected void installComponents() { final JLayeredPane layeredPane = frame.getLayeredPane(); if (resizeBox != null) { resizeBox.removeListeners(); layeredPane.removeComponentListener(resizeBox); layeredPane.remove(resizeBox); resizeBox = null; } resizeBox = new ResizeBox(layeredPane); resizeBox.repositionResizeBox(); layeredPane.add(resizeBox); layeredPane.setLayer(resizeBox, JLayeredPane.DRAG_LAYER); layeredPane.addComponentListener(resizeBox); resizeBox.addListeners(); resizeBox.setVisible(frame.isResizable()); } /// Inherit all the listeners - that's the main reason we subclass Basic! protected void installListeners() { fPropertyListener = new PropertyListener(); frame.addPropertyChangeListener(fPropertyListener); super.installListeners(); } // uninstallDefaults // uninstallComponents protected void uninstallListeners() { super.uninstallListeners(); frame.removePropertyChangeListener(fPropertyListener); } protected void uninstallKeyboardActions() { } // Called when a DesktopIcon replaces an InternalFrame & vice versa //protected void replacePane(JComponent currentPane, JComponent newPane) {} protected void installMouseHandlers(final JComponent c) { c.addMouseListener(borderListener); c.addMouseMotionListener(borderListener); } protected void deinstallMouseHandlers(final JComponent c) { c.removeMouseListener(borderListener); c.removeMouseMotionListener(borderListener); } ActionMap createActionMap() { final ActionMap map = new ActionMapUIResource(); // add action for the system menu // Set the ActionMap's parent to the Auditory Feedback Action Map final AquaLookAndFeel lf = (AquaLookAndFeel)UIManager.getLookAndFeel(); final ActionMap audioMap = lf.getAudioActionMap(); map.setParent(audioMap); return map; } public Dimension getPreferredSize(JComponent x) { Dimension preferredSize = super.getPreferredSize(x); Dimension minimumSize = frame.getMinimumSize(); if (preferredSize.width < minimumSize.width) { preferredSize.width = minimumSize.width; } if (preferredSize.height < minimumSize.height) { preferredSize.height = minimumSize.height; } return preferredSize; } public void setNorthPane(final JComponent c) { replacePane(northPane, c); northPane = c; }
Installs necessary mouse handlers on newPane and adds it to the frame. Reverse process for the currentPane.
/** * Installs necessary mouse handlers on <code>newPane</code> * and adds it to the frame. * Reverse process for the <code>currentPane</code>. */
protected void replacePane(final JComponent currentPane, final JComponent newPane) { if (currentPane != null) { deinstallMouseHandlers(currentPane); frame.remove(currentPane); } if (newPane != null) { frame.add(newPane); installMouseHandlers(newPane); } } // Our "Border" listener is shared by the AquaDesktopIcon protected MouseInputAdapter createBorderListener(final JInternalFrame w) { return new AquaBorderListener(); }
Mac-specific stuff begins here
/** * Mac-specific stuff begins here */
void setFrameType(final String frameType) { // Basic sets the background of the contentPane to null so it can inherit JInternalFrame.setBackground // but if *that's* null, we get the JDesktop, which makes ours look invisible! // So JInternalFrame has to have a background color // See Sun bugs 4268949 & 4320889 final Color bg = frame.getBackground(); final boolean replaceColor = (bg == null || bg instanceof UIResource); final Font font = frame.getFont(); final boolean replaceFont = (font == null || font instanceof UIResource); boolean isPalette = false; if (frameType.equals(OPTION_DIALOG)) { fAquaBorder = AquaInternalFrameBorder.dialog(); if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.optionDialogBackground")); if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.optionDialogTitleFont")); } else if (frameType.equals(PALETTE_FRAME)) { fAquaBorder = AquaInternalFrameBorder.utility(); if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.paletteBackground")); if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.paletteTitleFont")); isPalette = true; } else { fAquaBorder = AquaInternalFrameBorder.window(); if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.background")); if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.titleFont")); } // We don't get the borders from UIManager, in case someone changes them - this class will not work with // different borders. If they want different ones, they have to make their own InternalFrameUI class fAquaBorder.setColors(fSelectedTextColor, fNotSelectedTextColor); frame.setBorder(fAquaBorder); fIsPallet = isPalette; } public void setPalette(final boolean isPalette) { setFrameType(isPalette ? PALETTE_FRAME : NORMAL_FRAME); } public boolean isDocumentEdited() { return fDocumentEdited; } public void setDocumentEdited(final boolean flag) { fDocumentEdited = flag; } /* // helpful debug drawing, shows component and content bounds public void paint(final Graphics g, final JComponent c) { super.paint(g, c); g.setColor(Color.green); g.drawRect(0, 0, frame.getWidth() - 1, frame.getHeight() - 1); final Insets insets = frame.getInsets(); g.setColor(Color.orange); g.drawRect(insets.left - 2, insets.top - 2, frame.getWidth() - insets.left - insets.right + 4, frame.getHeight() - insets.top - insets.bottom + 4); } */ // Border Listener Class
Listens for border adjustments.
/** * Listens for border adjustments. */
protected class AquaBorderListener extends MouseInputAdapter { // _x & _y are the mousePressed location in absolute coordinate system int _x, _y; // __x & __y are the mousePressed location in source view's coordinate system int __x, __y; Rectangle startingBounds; boolean fDraggingFrame; int resizeDir; protected final int RESIZE_NONE = 0; private boolean discardRelease = false; public void mouseClicked(final MouseEvent e) { if (didForwardEvent(e)) return; if (e.getClickCount() <= 1 || e.getSource() != getNorthPane()) return; if (frame.isIconifiable() && frame.isIcon()) { try { frame.setIcon(false); } catch(final PropertyVetoException e2) {} } else if (frame.isMaximizable()) { if (!frame.isMaximum()) try { frame.setMaximum(true); } catch(final PropertyVetoException e2) {} else try { frame.setMaximum(false); } catch(final PropertyVetoException e3) {} } } public void updateRollover(final MouseEvent e) { final boolean oldRollover = fRollover; final Insets i = frame.getInsets(); fRollover = (isTitleBarDraggableArea(e) && fAquaBorder.getWithinRolloverArea(i, e.getX(), e.getY())); if (fRollover != oldRollover) { repaintButtons(); } } public void repaintButtons() { fAquaBorder.repaintButtonArea(frame); } public void mouseReleased(final MouseEvent e) { if (didForwardEvent(e)) return; fDraggingFrame = false; if (fWhichButtonPressed != -1) { final int newButton = fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY()); final int buttonPresed = fWhichButtonPressed; fWhichButtonPressed = -1; fMouseOverPressedButton = false; if (buttonPresed == newButton) { fMouseOverPressedButton = false; fRollover = false; // not sure if this is needed? fAquaBorder.doButtonAction(frame, buttonPresed); } updateRollover(e); repaintButtons(); return; } if (discardRelease) { discardRelease = false; return; } if (resizeDir == RESIZE_NONE) getDesktopManager().endDraggingFrame(frame); else { final Container c = frame.getTopLevelAncestor(); if (c instanceof JFrame) { ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setVisible(false); } else if (c instanceof JApplet) { ((JApplet)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ((JApplet)c).getGlassPane().setVisible(false); } else if (c instanceof JWindow) { ((JWindow)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ((JWindow)c).getGlassPane().setVisible(false); } else if (c instanceof JDialog) { ((JDialog)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ((JDialog)c).getGlassPane().setVisible(false); } getDesktopManager().endResizingFrame(frame); } _x = 0; _y = 0; __x = 0; __y = 0; startingBounds = null; resizeDir = RESIZE_NONE; } public void mousePressed(final MouseEvent e) { if (didForwardEvent(e)) return; final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null); __x = e.getX(); __y = e.getY(); _x = p.x; _y = p.y; startingBounds = frame.getBounds(); resizeDir = RESIZE_NONE; if (updatePressed(e)) { return; } if (!frame.isSelected()) { try { frame.setSelected(true); } catch(final PropertyVetoException e1) {} } if (isTitleBarDraggableArea(e)) { getDesktopManager().beginDraggingFrame(frame); fDraggingFrame = true; return; } if (e.getSource() == getNorthPane()) { getDesktopManager().beginDraggingFrame(frame); return; } if (!frame.isResizable()) { return; } if (e.getSource() == frame) { discardRelease = true; return; } } // returns true if we have handled the pressed public boolean updatePressed(final MouseEvent e) { // get the component. fWhichButtonPressed = getButtonHit(e); fMouseOverPressedButton = true; repaintButtons(); return (fWhichButtonPressed >= 0); // e.getX(), e.getY() } public int getButtonHit(final MouseEvent e) { return fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY()); } public boolean isTitleBarDraggableArea(final MouseEvent e) { if (e.getSource() != frame) return false; final Point point = e.getPoint(); final Insets insets = frame.getInsets(); if (point.y < insets.top - fAquaBorder.getTitleHeight()) return false; if (point.y > insets.top) return false; if (point.x < insets.left) return false; if (point.x > frame.getWidth() - insets.left - insets.right) return false; return true; } public void mouseDragged(final MouseEvent e) { // do not forward drags // if (didForwardEvent(e)) return; if (startingBounds == null) { // (STEVE) Yucky work around for bug ID 4106552 return; } if (fWhichButtonPressed != -1) { // track the button we started on. final int newButton = getButtonHit(e); fMouseOverPressedButton = (fWhichButtonPressed == newButton); repaintButtons(); return; } final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null); final int deltaX = _x - p.x; final int deltaY = _y - p.y; int newX, newY; // Handle a MOVE if (!fDraggingFrame && e.getSource() != getNorthPane()) return; if (frame.isMaximum() || ((e.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK)) { // don't allow moving of frames if maximixed or left mouse // button was not used. return; } final Dimension s = frame.getParent().getSize(); final int pWidth = s.width; final int pHeight = s.height; final Insets i = frame.getInsets(); newX = startingBounds.x - deltaX; newY = startingBounds.y - deltaY; // Make sure we stay in-bounds if (newX + i.left <= -__x) newX = -__x - i.left; if (newY + i.top <= -__y) newY = -__y - i.top; if (newX + __x + i.right > pWidth) newX = pWidth - __x - i.right; if (newY + __y + i.bottom > pHeight) newY = pHeight - __y - i.bottom; getDesktopManager().dragFrame(frame, newX, newY); return; } public void mouseMoved(final MouseEvent e) { if (didForwardEvent(e)) return; updateRollover(e); } // guards against accidental infinite recursion boolean isTryingToForwardEvent = false; boolean didForwardEvent(final MouseEvent e) { if (isTryingToForwardEvent) return true; // we didn't actually...but we wound up back where we started. isTryingToForwardEvent = true; final boolean didForwardEvent = didForwardEventInternal(e); isTryingToForwardEvent = false; return didForwardEvent; } boolean didForwardEventInternal(final MouseEvent e) { if (fDraggingFrame) return false; final Point originalPoint = e.getPoint(); if (!isEventInWindowShadow(originalPoint)) return false; final Container parent = frame.getParent(); if (!(parent instanceof JDesktopPane)) return false; final JDesktopPane pane = (JDesktopPane)parent; final Point parentPoint = SwingUtilities.convertPoint(frame, originalPoint, parent); /* // debug drawing Graphics g = parent.getGraphics(); g.setColor(Color.red); g.drawLine(parentPoint.x, parentPoint.y, parentPoint.x, parentPoint.y); */ final Component hitComponent = findComponentToHitBehindMe(pane, parentPoint); if (hitComponent == null || hitComponent == frame) return false; final Point hitComponentPoint = SwingUtilities.convertPoint(pane, parentPoint, hitComponent); hitComponent.dispatchEvent(new MouseEvent(hitComponent, e.getID(), e.getWhen(), e.getModifiers(), hitComponentPoint.x, hitComponentPoint.y, e.getClickCount(), e.isPopupTrigger(), e.getButton())); return true; } Component findComponentToHitBehindMe(final JDesktopPane pane, final Point parentPoint) { final JInternalFrame[] allFrames = pane.getAllFrames(); boolean foundSelf = false; for (final JInternalFrame f : allFrames) { if (f == frame) { foundSelf = true; continue; } if (!foundSelf) continue; final Rectangle bounds = f.getBounds(); if (bounds.contains(parentPoint)) return f; } return pane; } boolean isEventInWindowShadow(final Point point) { final Rectangle bounds = frame.getBounds(); final Insets insets = frame.getInsets(); insets.top -= fAquaBorder.getTitleHeight(); if (point.x < insets.left) return true; if (point.x > bounds.width - insets.right) return true; if (point.y < insets.top) return true; if (point.y > bounds.height - insets.bottom) return true; return false; } } static void updateComponentTreeUIActivation(final Component c, final Object active) { if (c instanceof javax.swing.JComponent) { ((javax.swing.JComponent)c).putClientProperty(AquaFocusHandler.FRAME_ACTIVE_PROPERTY, active); } Component[] children = null; if (c instanceof javax.swing.JMenu) { children = ((javax.swing.JMenu)c).getMenuComponents(); } else if (c instanceof Container) { children = ((Container)c).getComponents(); } if (children != null) { for (final Component element : children) { updateComponentTreeUIActivation(element, active); } } } class PropertyListener implements PropertyChangeListener { public void propertyChange(final PropertyChangeEvent e) { final String name = e.getPropertyName(); if (FRAME_TYPE.equals(name)) { if (e.getNewValue() instanceof String) { setFrameType((String)e.getNewValue()); } } else if (IS_PALETTE_PROPERTY.equals(name)) { if (e.getNewValue() != null) { setPalette(((Boolean)e.getNewValue()).booleanValue()); } else { setPalette(false); } // TODO: CPlatformWindow? } else if ("windowModified".equals(name) || CPlatformWindow.WINDOW_DOCUMENT_MODIFIED.equals(name)) { // repaint title bar setDocumentEdited(((Boolean)e.getNewValue()).booleanValue()); frame.repaint(0, 0, frame.getWidth(), frame.getBorder().getBorderInsets(frame).top); } else if ("resizable".equals(name) || "state".equals(name) || "iconable".equals(name) || "maximizable".equals(name) || "closable".equals(name)) { if ("resizable".equals(name)) { frame.revalidate(); } frame.repaint(); } else if ("title".equals(name)) { frame.repaint(); } else if ("componentOrientation".equals(name)) { frame.revalidate(); frame.repaint(); } else if (JInternalFrame.IS_SELECTED_PROPERTY.equals(name)) { final Component source = (Component)(e.getSource()); updateComponentTreeUIActivation(source, frame.isSelected() ? Boolean.TRUE : Boolean.FALSE); } } } // end class PaletteListener static final InternalFrameShadow documentWindowShadow = new InternalFrameShadow() { Border getForegroundShadowBorder() { return new AquaUtils.SlicedShadowBorder(new Painter() { public void paint(final Graphics g, final int x, final int y, final int w, final int h) { g.setColor(new Color(0, 0, 0, 196)); g.fillRoundRect(x, y, w, h, 16, 16); g.fillRect(x, y + h - 16, w, 16); } }, new Painter() { public void paint(final Graphics g, int x, int y, int w, int h) { g.setColor(new Color(0, 0, 0, 64)); g.drawLine(x + 2, y - 8, x + w - 2, y - 8); } }, 0, 7, 1.1f, 1.0f, 24, 51, 51, 25, 25, 25, 25); } Border getBackgroundShadowBorder() { return new AquaUtils.SlicedShadowBorder(new Painter() { public void paint(final Graphics g, final int x, final int y, final int w, final int h) { g.setColor(new Color(0, 0, 0, 128)); g.fillRoundRect(x - 3, y - 8, w + 6, h, 16, 16); g.fillRect(x - 3, y + h - 20, w + 6, 19); } }, new Painter() { public void paint(final Graphics g, int x, int y, int w, int h) { g.setColor(new Color(0, 0, 0, 32)); g.drawLine(x, y - 11, x + w - 1, y - 11); } }, 0, 0, 3.0f, 1.0f, 10, 51, 51, 25, 25, 25, 25); } }; static final InternalFrameShadow paletteWindowShadow = new InternalFrameShadow() { Border getForegroundShadowBorder() { return new AquaUtils.SlicedShadowBorder(new Painter() { public void paint(final Graphics g, final int x, final int y, final int w, final int h) { g.setColor(new Color(0, 0, 0, 128)); g.fillRect(x, y + 3, w, h - 3); } }, null, 0, 3, 1.0f, 1.0f, 10, 25, 25, 12, 12, 12, 12); } Border getBackgroundShadowBorder() { return getForegroundShadowBorder(); } }; static class CompoundUIBorder extends CompoundBorder implements UIResource { public CompoundUIBorder(final Border inside, final Border outside) { super(inside, outside); } } abstract static class InternalFrameShadow extends RecyclableSingleton<Border> { abstract Border getForegroundShadowBorder(); abstract Border getBackgroundShadowBorder(); protected Border getInstance() { final Border fgShadow = getForegroundShadowBorder(); final Border bgShadow = getBackgroundShadowBorder(); return new Border() { public Insets getBorderInsets(final Component c) { return fgShadow.getBorderInsets(c); } public boolean isBorderOpaque() { return false; } public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) { if (((JInternalFrame)c).isSelected()) { fgShadow.paintBorder(c, g, x, y, w, h); } else { bgShadow.paintBorder(c, g, x, y, w, h); } } }; } } static final RecyclableSingleton<Icon> RESIZE_ICON = new RecyclableSingleton<Icon>() { @Override protected Icon getInstance() { return new AquaIcon.ScalingJRSUIIcon(11, 11) { public void initIconPainter(final AquaPainter<JRSUIState> iconState) { iconState.state.set(Widget.GROW_BOX_TEXTURED); iconState.state.set(WindowType.UTILITY); } }; } }; class ResizeBox extends JLabel implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener, PropertyChangeListener, UIResource { final JLayeredPane layeredPane; Dimension originalSize; Point originalLocation; public ResizeBox(final JLayeredPane layeredPane) { super(RESIZE_ICON.get()); setSize(11, 11); this.layeredPane = layeredPane; addMouseListener(this); addMouseMotionListener(this); addMouseWheelListener(this); } void addListeners() { frame.addPropertyChangeListener("resizable", this); } void removeListeners() { frame.removePropertyChangeListener("resizable", this); } void repositionResizeBox() { if (frame == null) { setSize(0, 0); } else { setSize(11, 11); } setLocation(layeredPane.getWidth() - 12, layeredPane.getHeight() - 12); } void resizeInternalFrame(final Point pt) { if (originalLocation == null || frame == null) return; final Container parent = frame.getParent(); if (!(parent instanceof JDesktopPane)) return; final Point newPoint = SwingUtilities.convertPoint(this, pt, frame); int deltaX = originalLocation.x - newPoint.x; int deltaY = originalLocation.y - newPoint.y; final Dimension min = frame.getMinimumSize(); final Dimension max = frame.getMaximumSize(); final int newX = frame.getX(); final int newY = frame.getY(); int newW = frame.getWidth(); int newH = frame.getHeight(); final Rectangle parentBounds = parent.getBounds(); if (originalSize.width - deltaX < min.width) { deltaX = originalSize.width - min.width; } else if (originalSize.width - deltaX > max.width) { deltaX = -(max.width - originalSize.width); } if (newX + originalSize.width - deltaX > parentBounds.width) { deltaX = newX + originalSize.width - parentBounds.width; } if (originalSize.height - deltaY < min.height) { deltaY = originalSize.height - min.height; } else if (originalSize.height - deltaY > max.height) { deltaY = -(max.height - originalSize.height); } if (newY + originalSize.height - deltaY > parentBounds.height) { deltaY = newY + originalSize.height - parentBounds.height; } newW = originalSize.width - deltaX; newH = originalSize.height - deltaY; getDesktopManager().resizeFrame(frame, newX, newY, newW, newH); } boolean testGrowboxPoint(final int x, final int y, final int w, final int h) { return (w - x) + (h - y) < 12; } void forwardEventToFrame(final MouseEvent e) { final Point pt = new Point(); final Component c = getComponentToForwardTo(e, pt); if (c == null) return; c.dispatchEvent(new MouseEvent(c, e.getID(), e.getWhen(), e.getModifiers(), pt.x, pt.y, e.getClickCount(), e.isPopupTrigger(), e.getButton())); } Component getComponentToForwardTo(final MouseEvent e, final Point dst) { if (frame == null) return null; final Container contentPane = frame.getContentPane(); if (contentPane == null) return null; Point pt = SwingUtilities.convertPoint(this, e.getPoint(), contentPane); final Component c = SwingUtilities.getDeepestComponentAt(contentPane, pt.x, pt.y); if (c == null) return null; pt = SwingUtilities.convertPoint(contentPane, pt, c); if (dst != null) dst.setLocation(pt); return c; } public void mouseClicked(final MouseEvent e) { forwardEventToFrame(e); } public void mouseEntered(final MouseEvent e) { } public void mouseExited(final MouseEvent e) { } public void mousePressed(final MouseEvent e) { if (frame == null) return; if (frame.isResizable() && !frame.isMaximum() && testGrowboxPoint(e.getX(), e.getY(), getWidth(), getHeight())) { originalLocation = SwingUtilities.convertPoint(this, e.getPoint(), frame); originalSize = frame.getSize(); getDesktopManager().beginResizingFrame(frame, SwingConstants.SOUTH_EAST); return; } forwardEventToFrame(e); } public void mouseReleased(final MouseEvent e) { if (originalLocation != null) { resizeInternalFrame(e.getPoint()); originalLocation = null; getDesktopManager().endResizingFrame(frame); return; } forwardEventToFrame(e); } public void mouseDragged(final MouseEvent e) { resizeInternalFrame(e.getPoint()); repositionResizeBox(); } public void mouseMoved(final MouseEvent e) { } public void mouseWheelMoved(final MouseWheelEvent e) { final Point pt = new Point(); final Component c = getComponentToForwardTo(e, pt); if (c == null) return; c.dispatchEvent(new MouseWheelEvent(c, e.getID(), e.getWhen(), e.getModifiers(), pt.x, pt.y, e.getClickCount(), e.isPopupTrigger(), e.getScrollType(), e.getScrollAmount(), e.getWheelRotation())); } public void componentResized(final ComponentEvent e) { repositionResizeBox(); } public void componentShown(final ComponentEvent e) { repositionResizeBox(); } public void componentMoved(final ComponentEvent e) { repositionResizeBox(); } public void componentHidden(final ComponentEvent e) { } public void propertyChange(final PropertyChangeEvent evt) { if (!"resizable".equals(evt.getPropertyName())) return; setVisible(Boolean.TRUE.equals(evt.getNewValue())); } } }