1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.apple.laf;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.beans.*;
  31 
  32 import javax.swing.*;
  33 import javax.swing.border.*;
  34 import javax.swing.event.MouseInputAdapter;
  35 import javax.swing.plaf.*;
  36 import javax.swing.plaf.basic.BasicInternalFrameUI;
  37 
  38 import apple.laf.*;
  39 import apple.laf.JRSUIConstants.*;
  40 
  41 import com.apple.laf.AquaUtils.*;
  42 import com.apple.laf.AquaUtils.Painter;
  43 
  44 import sun.lwawt.macosx.CPlatformWindow;
  45 
  46 /**
  47  * From AquaInternalFrameUI
  48  *
  49  * InternalFrame implementation for Aqua LAF
  50  *
  51  * We want to inherit most of the inner classes, but not the base class,
  52  * so be very careful about subclassing so you know you get what you want
  53  *
  54  */
  55 public class AquaInternalFrameUI extends BasicInternalFrameUI implements SwingConstants {
  56     protected static final String IS_PALETTE_PROPERTY = "JInternalFrame.isPalette";
  57     private static final String FRAME_TYPE = "JInternalFrame.frameType";
  58     private static final String NORMAL_FRAME = "normal";
  59     private static final String PALETTE_FRAME = "palette";
  60     private static final String OPTION_DIALOG = "optionDialog";
  61 
  62     // Instance variables
  63     PropertyChangeListener fPropertyListener;
  64 
  65     protected Color fSelectedTextColor;
  66     protected Color fNotSelectedTextColor;
  67 
  68     AquaInternalFrameBorder fAquaBorder;
  69     private ResizeBox resizeBox;
  70 
  71     // for button tracking
  72     boolean fMouseOverPressedButton;
  73     int fWhichButtonPressed = -1;
  74     boolean fRollover = false;
  75     boolean fDocumentEdited = false; // to indicate whether we should use the dirty document red dot.
  76     boolean fIsPallet;
  77 
  78     public int getWhichButtonPressed() {
  79         return fWhichButtonPressed;
  80     }
  81 
  82     public boolean getMouseOverPressedButton() {
  83         return fMouseOverPressedButton;
  84     }
  85 
  86     public boolean getRollover() {
  87         return fRollover;
  88     }
  89 
  90     // ComponentUI Interface Implementation methods
  91     public static ComponentUI createUI(final JComponent b) {
  92         return new AquaInternalFrameUI((JInternalFrame)b);
  93     }
  94 
  95     public AquaInternalFrameUI(final JInternalFrame b) {
  96         super(b);
  97     }
  98 
  99     /// Inherit  (but be careful to check everything they call):
 100     @Override
 101     public void installUI(final JComponent c) {
 102 //        super.installUI(c);  // Swing 1.1.1 has a bug in installUI - it doesn't check for null northPane
 103         frame = (JInternalFrame)c;
 104         frame.add(frame.getRootPane(), "Center");
 105 
 106         installDefaults();
 107         installListeners();
 108         installComponents();
 109         installKeyboardActions();
 110 
 111         Object paletteProp = c.getClientProperty(IS_PALETTE_PROPERTY);
 112         if (paletteProp != null) {
 113             setPalette(((Boolean)paletteProp).booleanValue());
 114         } else {
 115             paletteProp = c.getClientProperty(FRAME_TYPE);
 116             if (paletteProp != null) {
 117                 setFrameType((String)paletteProp);
 118             } else {
 119                 setFrameType(NORMAL_FRAME);
 120             }
 121         }
 122 
 123         // We only have a southPane, for grow box room, created in setFrameType
 124         frame.setMinimumSize(new Dimension(fIsPallet ? 120 : 150, fIsPallet ? 39 : 65));
 125         frame.setOpaque(false);
 126 
 127         c.setBorder(new CompoundUIBorder(fIsPallet ? paletteWindowShadow.get() : documentWindowShadow.get(), c.getBorder()));
 128     }
 129 
 130     @Override
 131     protected void installDefaults() {
 132         super.installDefaults();
 133         fSelectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
 134         fNotSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
 135     }
 136 
 137     @Override
 138     public void setSouthPane(final JComponent c) {
 139         if (southPane != null) {
 140             frame.remove(southPane);
 141             deinstallMouseHandlers(southPane);
 142         }
 143         if (c != null) {
 144             frame.add(c);
 145             installMouseHandlers(c);
 146         }
 147         southPane = c;
 148     }
 149 
 150     static final RecyclableSingleton<Icon> closeIcon = new RecyclableSingleton<Icon>() {
 151         @Override
 152         protected Icon getInstance() {
 153             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_CLOSE_BOX);
 154         }
 155     };
 156     public static Icon exportCloseIcon() {
 157         return closeIcon.get();
 158     }
 159 
 160     static final RecyclableSingleton<Icon> minimizeIcon = new RecyclableSingleton<Icon>() {
 161         @Override
 162         protected Icon getInstance() {
 163             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_COLLAPSE_BOX);
 164         }
 165     };
 166     public static Icon exportMinimizeIcon() {
 167         return minimizeIcon.get();
 168     }
 169 
 170     static final RecyclableSingleton<Icon> zoomIcon = new RecyclableSingleton<Icon>() {
 171         @Override
 172         protected Icon getInstance() {
 173             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_ZOOM_BOX);
 174         }
 175     };
 176     public static Icon exportZoomIcon() {
 177         return zoomIcon.get();
 178     }
 179 
 180     static class AquaInternalFrameButtonIcon extends AquaIcon.JRSUIIcon {
 181         public AquaInternalFrameButtonIcon(final Widget widget) {
 182             painter.state.set(widget);
 183         }
 184 
 185         @Override
 186         public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
 187             painter.state.set(getStateFor(c));
 188             super.paintIcon(c, g, x, y);
 189         }
 190 
 191         State getStateFor(final Component c) {
 192             return State.ROLLOVER;
 193         }
 194 
 195         @Override
 196         public int getIconWidth() {
 197             return 19;
 198         }
 199 
 200         @Override
 201         public int getIconHeight() {
 202             return 19;
 203         }
 204     }
 205 
 206     @Override
 207     protected void installKeyboardActions() {
 208     } //$ Not Mac-ish - should we support?
 209 
 210     @Override
 211     protected void installComponents() {
 212         final JLayeredPane layeredPane = frame.getLayeredPane();
 213         resizeBox = new ResizeBox(layeredPane);
 214         resizeBox.repositionResizeBox();
 215 
 216         layeredPane.add(resizeBox);
 217         layeredPane.setLayer(resizeBox, JLayeredPane.DRAG_LAYER);
 218         layeredPane.addComponentListener(resizeBox);
 219 
 220         resizeBox.addListeners();
 221         resizeBox.setVisible(frame.isResizable());
 222     }
 223 
 224     /// Inherit all the listeners - that's the main reason we subclass Basic!
 225     @Override
 226     protected void installListeners() {
 227         fPropertyListener = new PropertyListener();
 228         frame.addPropertyChangeListener(fPropertyListener);
 229         super.installListeners();
 230     }
 231 
 232     // uninstallDefaults
 233 
 234     @Override
 235     protected void uninstallComponents() {
 236         super.uninstallComponents();
 237         final JLayeredPane layeredPane = frame.getLayeredPane();
 238         resizeBox.removeListeners();
 239         layeredPane.removeComponentListener(resizeBox);
 240         layeredPane.remove(resizeBox);
 241         resizeBox = null;
 242     }
 243 
 244     @Override
 245     protected void uninstallListeners() {
 246         super.uninstallListeners();
 247         frame.removePropertyChangeListener(fPropertyListener);
 248     }
 249 
 250     @Override
 251     protected void uninstallKeyboardActions() {
 252     }
 253 
 254     // Called when a DesktopIcon replaces an InternalFrame & vice versa
 255     //protected void replacePane(JComponent currentPane, JComponent newPane) {}
 256     @Override
 257     protected void installMouseHandlers(final JComponent c) {
 258         c.addMouseListener(borderListener);
 259         c.addMouseMotionListener(borderListener);
 260     }
 261 
 262     @Override
 263     protected void deinstallMouseHandlers(final JComponent c) {
 264         c.removeMouseListener(borderListener);
 265         c.removeMouseMotionListener(borderListener);
 266     }
 267 
 268     ActionMap createActionMap() {
 269         final ActionMap map = new ActionMapUIResource();
 270         // add action for the system menu
 271         // Set the ActionMap's parent to the Auditory Feedback Action Map
 272         final AquaLookAndFeel lf = (AquaLookAndFeel)UIManager.getLookAndFeel();
 273         final ActionMap audioMap = lf.getAudioActionMap();
 274         map.setParent(audioMap);
 275         return map;
 276     }
 277 
 278     @Override
 279     public Dimension getPreferredSize(JComponent x) {
 280         Dimension preferredSize = super.getPreferredSize(x);
 281         Dimension minimumSize = frame.getMinimumSize();
 282         if (preferredSize.width < minimumSize.width) {
 283             preferredSize.width = minimumSize.width;
 284         }
 285         if (preferredSize.height < minimumSize.height) {
 286             preferredSize.height = minimumSize.height;
 287         }
 288         return preferredSize;
 289     }
 290 
 291     @Override
 292     public void setNorthPane(final JComponent c) {
 293         replacePane(northPane, c);
 294         northPane = c;
 295     }
 296 
 297     /**
 298      * Installs necessary mouse handlers on {@code newPane}
 299      * and adds it to the frame.
 300      * Reverse process for the {@code currentPane}.
 301      */
 302     @Override
 303     protected void replacePane(final JComponent currentPane, final JComponent newPane) {
 304         if (currentPane != null) {
 305             deinstallMouseHandlers(currentPane);
 306             frame.remove(currentPane);
 307         }
 308         if (newPane != null) {
 309             frame.add(newPane);
 310             installMouseHandlers(newPane);
 311         }
 312     }
 313 
 314     // Our "Border" listener is shared by the AquaDesktopIcon
 315     @Override
 316     protected MouseInputAdapter createBorderListener(final JInternalFrame w) {
 317         return new AquaBorderListener();
 318     }
 319 
 320     /**
 321      * Mac-specific stuff begins here
 322      */
 323     void setFrameType(final String frameType) {
 324         // Basic sets the background of the contentPane to null so it can inherit JInternalFrame.setBackground
 325         // but if *that's* null, we get the JDesktop, which makes ours look invisible!
 326         // So JInternalFrame has to have a background color
 327         // See Sun bugs 4268949 & 4320889
 328         final Color bg = frame.getBackground();
 329         final boolean replaceColor = (bg == null || bg instanceof UIResource);
 330 
 331         final Font font = frame.getFont();
 332         final boolean replaceFont = (font == null || font instanceof UIResource);
 333 
 334         boolean isPalette = false;
 335         if (frameType.equals(OPTION_DIALOG)) {
 336             fAquaBorder = AquaInternalFrameBorder.dialog();
 337             if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.optionDialogBackground"));
 338             if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.optionDialogTitleFont"));
 339         } else if (frameType.equals(PALETTE_FRAME)) {
 340             fAquaBorder = AquaInternalFrameBorder.utility();
 341             if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.paletteBackground"));
 342             if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.paletteTitleFont"));
 343             isPalette = true;
 344         } else {
 345             fAquaBorder = AquaInternalFrameBorder.window();
 346             if (replaceColor) frame.setBackground(UIManager.getColor("InternalFrame.background"));
 347             if (replaceFont) frame.setFont(UIManager.getFont("InternalFrame.titleFont"));
 348         }
 349         // We don't get the borders from UIManager, in case someone changes them - this class will not work with
 350         // different borders.  If they want different ones, they have to make their own InternalFrameUI class
 351 
 352         fAquaBorder.setColors(fSelectedTextColor, fNotSelectedTextColor);
 353         frame.setBorder(fAquaBorder);
 354 
 355         fIsPallet = isPalette;
 356     }
 357 
 358     public void setPalette(final boolean isPalette) {
 359         setFrameType(isPalette ? PALETTE_FRAME : NORMAL_FRAME);
 360     }
 361 
 362     public boolean isDocumentEdited() {
 363         return fDocumentEdited;
 364     }
 365 
 366     public void setDocumentEdited(final boolean flag) {
 367         fDocumentEdited = flag;
 368     }
 369 
 370 /*
 371     // helpful debug drawing, shows component and content bounds
 372     public void paint(final Graphics g, final JComponent c) {
 373         super.paint(g, c);
 374 
 375         g.setColor(Color.green);
 376         g.drawRect(0, 0, frame.getWidth() - 1, frame.getHeight() - 1);
 377 
 378         final Insets insets = frame.getInsets();
 379         g.setColor(Color.orange);
 380         g.drawRect(insets.left - 2, insets.top - 2, frame.getWidth() - insets.left - insets.right + 4, frame.getHeight() - insets.top - insets.bottom + 4);
 381     }
 382 */
 383 
 384     // Border Listener Class
 385     /**
 386      * Listens for border adjustments.
 387      */
 388     protected class AquaBorderListener extends MouseInputAdapter {
 389         // _x & _y are the mousePressed location in absolute coordinate system
 390         int _x, _y;
 391         // __x & __y are the mousePressed location in source view's coordinate system
 392         int __x, __y;
 393         Rectangle startingBounds;
 394         boolean fDraggingFrame;
 395         int resizeDir;
 396 
 397         protected final int RESIZE_NONE = 0;
 398         private boolean discardRelease = false;
 399 
 400         @Override
 401         public void mouseClicked(final MouseEvent e) {
 402             if (didForwardEvent(e)) return;
 403 
 404             if (e.getClickCount() <= 1 || e.getSource() != getNorthPane()) return;
 405 
 406             if (frame.isIconifiable() && frame.isIcon()) {
 407                 try {
 408                     frame.setIcon(false);
 409                 } catch(final PropertyVetoException e2) {}
 410             } else if (frame.isMaximizable()) {
 411                 if (!frame.isMaximum()) try {
 412                     frame.setMaximum(true);
 413                 } catch(final PropertyVetoException e2) {}
 414                 else try {
 415                     frame.setMaximum(false);
 416                 } catch(final PropertyVetoException e3) {}
 417             }
 418         }
 419 
 420         public void updateRollover(final MouseEvent e) {
 421             final boolean oldRollover = fRollover;
 422             final Insets i = frame.getInsets();
 423             fRollover = (isTitleBarDraggableArea(e) && fAquaBorder.getWithinRolloverArea(i, e.getX(), e.getY()));
 424             if (fRollover != oldRollover) {
 425                 repaintButtons();
 426             }
 427         }
 428 
 429         public void repaintButtons() {
 430             fAquaBorder.repaintButtonArea(frame);
 431         }
 432 
 433         @Override
 434         @SuppressWarnings("deprecation")
 435         public void mouseReleased(final MouseEvent e) {
 436             if (didForwardEvent(e)) return;
 437 
 438             fDraggingFrame = false;
 439 
 440             if (fWhichButtonPressed != -1) {
 441                 final int newButton = fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY());
 442 
 443                 final int buttonPresed = fWhichButtonPressed;
 444                 fWhichButtonPressed = -1;
 445                 fMouseOverPressedButton = false;
 446 
 447                 if (buttonPresed == newButton) {
 448                     fMouseOverPressedButton = false;
 449                     fRollover = false; // not sure if this is needed?
 450 
 451                     fAquaBorder.doButtonAction(frame, buttonPresed);
 452                 }
 453 
 454                 updateRollover(e);
 455                 repaintButtons();
 456                 return;
 457             }
 458 
 459             if (discardRelease) {
 460                 discardRelease = false;
 461                 return;
 462             }
 463             if (resizeDir == RESIZE_NONE) getDesktopManager().endDraggingFrame(frame);
 464             else {
 465                 final Container c = frame.getTopLevelAncestor();
 466                 if (c instanceof JFrame) {
 467                     ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 468 
 469                     ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setVisible(false);
 470                 } else if (c instanceof JApplet) {
 471                     ((JApplet)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 472                     ((JApplet)c).getGlassPane().setVisible(false);
 473                 } else if (c instanceof JWindow) {
 474                     ((JWindow)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 475                     ((JWindow)c).getGlassPane().setVisible(false);
 476                 } else if (c instanceof JDialog) {
 477                     ((JDialog)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 478                     ((JDialog)c).getGlassPane().setVisible(false);
 479                 }
 480                 getDesktopManager().endResizingFrame(frame);
 481             }
 482             _x = 0;
 483             _y = 0;
 484             __x = 0;
 485             __y = 0;
 486             startingBounds = null;
 487             resizeDir = RESIZE_NONE;
 488         }
 489 
 490         @Override
 491         public void mousePressed(final MouseEvent e) {
 492             if (didForwardEvent(e)) return;
 493 
 494             final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null);
 495             __x = e.getX();
 496             __y = e.getY();
 497             _x = p.x;
 498             _y = p.y;
 499             startingBounds = frame.getBounds();
 500             resizeDir = RESIZE_NONE;
 501 
 502             if (updatePressed(e)) { return; }
 503 
 504             if (!frame.isSelected()) {
 505                 try {
 506                     frame.setSelected(true);
 507                 } catch(final PropertyVetoException e1) {}
 508             }
 509 
 510             if (isTitleBarDraggableArea(e)) {
 511                 getDesktopManager().beginDraggingFrame(frame);
 512                 fDraggingFrame = true;
 513                 return;
 514             }
 515 
 516             if (e.getSource() == getNorthPane()) {
 517                 getDesktopManager().beginDraggingFrame(frame);
 518                 return;
 519             }
 520 
 521             if (!frame.isResizable()) { return; }
 522 
 523             if (e.getSource() == frame) {
 524                 discardRelease = true;
 525                 return;
 526             }
 527         }
 528 
 529         // returns true if we have handled the pressed
 530         public boolean updatePressed(final MouseEvent e) {
 531             // get the component.
 532             fWhichButtonPressed = getButtonHit(e);
 533             fMouseOverPressedButton = true;
 534             repaintButtons();
 535             return (fWhichButtonPressed >= 0);
 536             // e.getX(), e.getY()
 537         }
 538 
 539         public int getButtonHit(final MouseEvent e) {
 540             return fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY());
 541         }
 542 
 543         public boolean isTitleBarDraggableArea(final MouseEvent e) {
 544             if (e.getSource() != frame) return false;
 545 
 546             final Point point = e.getPoint();
 547             final Insets insets = frame.getInsets();
 548 
 549             if (point.y < insets.top - fAquaBorder.getTitleHeight()) return false;
 550             if (point.y > insets.top) return false;
 551             if (point.x < insets.left) return false;
 552             if (point.x > frame.getWidth() - insets.left - insets.right) return false;
 553 
 554             return true;
 555         }
 556 
 557         @Override
 558         public void mouseDragged(final MouseEvent e) {
 559 // do not forward drags
 560 //            if (didForwardEvent(e)) return;
 561 
 562             if (startingBounds == null) {
 563                 // (STEVE) Yucky work around for bug ID 4106552
 564                 return;
 565             }
 566 
 567             if (fWhichButtonPressed != -1) {
 568                 // track the button we started on.
 569                 final int newButton = getButtonHit(e);
 570                 fMouseOverPressedButton = (fWhichButtonPressed == newButton);
 571                 repaintButtons();
 572                 return;
 573             }
 574 
 575             final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null);
 576             final int deltaX = _x - p.x;
 577             final int deltaY = _y - p.y;
 578             int newX, newY;
 579 
 580             // Handle a MOVE
 581             if (!fDraggingFrame && e.getSource() != getNorthPane()) return;
 582 
 583             if (frame.isMaximum() || ((e.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK)) {
 584                 // don't allow moving of frames if maximixed or left mouse
 585                 // button was not used.
 586                 return;
 587             }
 588 
 589             final Dimension s = frame.getParent().getSize();
 590             final int pWidth = s.width;
 591             final int pHeight = s.height;
 592 
 593             final Insets i = frame.getInsets();
 594             newX = startingBounds.x - deltaX;
 595             newY = startingBounds.y - deltaY;
 596 
 597             // Make sure we stay in-bounds
 598             if (newX + i.left <= -__x) newX = -__x - i.left;
 599             if (newY + i.top <= -__y) newY = -__y - i.top;
 600             if (newX + __x + i.right > pWidth) newX = pWidth - __x - i.right;
 601             if (newY + __y + i.bottom > pHeight) newY = pHeight - __y - i.bottom;
 602 
 603             getDesktopManager().dragFrame(frame, newX, newY);
 604             return;
 605         }
 606 
 607         @Override
 608         public void mouseMoved(final MouseEvent e) {
 609             if (didForwardEvent(e)) return;
 610             updateRollover(e);
 611         }
 612 
 613         // guards against accidental infinite recursion
 614         boolean isTryingToForwardEvent = false;
 615         boolean didForwardEvent(final MouseEvent e) {
 616             if (isTryingToForwardEvent) return true; // we didn't actually...but we wound up back where we started.
 617 
 618             isTryingToForwardEvent = true;
 619             final boolean didForwardEvent = didForwardEventInternal(e);
 620             isTryingToForwardEvent = false;
 621 
 622             return didForwardEvent;
 623         }
 624 
 625         boolean didForwardEventInternal(final MouseEvent e) {
 626             if (fDraggingFrame) return false;
 627 
 628             final Point originalPoint = e.getPoint();
 629             if (!isEventInWindowShadow(originalPoint)) return false;
 630 
 631             final Container parent = frame.getParent();
 632             if (!(parent instanceof JDesktopPane)) return false;
 633             final JDesktopPane pane = (JDesktopPane)parent;
 634             final Point parentPoint = SwingUtilities.convertPoint(frame, originalPoint, parent);
 635 
 636         /*     // debug drawing
 637             Graphics g = parent.getGraphics();
 638             g.setColor(Color.red);
 639             g.drawLine(parentPoint.x, parentPoint.y, parentPoint.x, parentPoint.y);
 640         */
 641 
 642             final Component hitComponent = findComponentToHitBehindMe(pane, parentPoint);
 643             if (hitComponent == null || hitComponent == frame) return false;
 644 
 645             final Point hitComponentPoint = SwingUtilities.convertPoint(pane, parentPoint, hitComponent);
 646             hitComponent.dispatchEvent(
 647                     new MouseEvent(hitComponent, e.getID(), e.getWhen(),
 648                                    e.getModifiers(), hitComponentPoint.x,
 649                                    hitComponentPoint.y, e.getClickCount(),
 650                                    e.isPopupTrigger(), e.getButton()));
 651             return true;
 652         }
 653 
 654         Component findComponentToHitBehindMe(final JDesktopPane pane, final Point parentPoint) {
 655             final JInternalFrame[] allFrames = pane.getAllFrames();
 656 
 657             boolean foundSelf = false;
 658             for (final JInternalFrame f : allFrames) {
 659                 if (f == frame) { foundSelf = true; continue; }
 660                 if (!foundSelf) continue;
 661 
 662                 final Rectangle bounds = f.getBounds();
 663                 if (bounds.contains(parentPoint)) return f;
 664             }
 665 
 666             return pane;
 667         }
 668 
 669         boolean isEventInWindowShadow(final Point point) {
 670             final Rectangle bounds = frame.getBounds();
 671             final Insets insets = frame.getInsets();
 672             insets.top -= fAquaBorder.getTitleHeight();
 673 
 674             if (point.x < insets.left) return true;
 675             if (point.x > bounds.width - insets.right) return true;
 676             if (point.y < insets.top) return true;
 677             if (point.y > bounds.height - insets.bottom) return true;
 678 
 679             return false;
 680         }
 681     }
 682 
 683     static void updateComponentTreeUIActivation(final Component c, final Object active) {
 684         if (c instanceof javax.swing.JComponent) {
 685             ((javax.swing.JComponent)c).putClientProperty(AquaFocusHandler.FRAME_ACTIVE_PROPERTY, active);
 686         }
 687 
 688         Component[] children = null;
 689 
 690         if (c instanceof javax.swing.JMenu) {
 691             children = ((javax.swing.JMenu)c).getMenuComponents();
 692         } else if (c instanceof Container) {
 693             children = ((Container)c).getComponents();
 694         }
 695 
 696         if (children != null) {
 697             for (final Component element : children) {
 698                 updateComponentTreeUIActivation(element, active);
 699             }
 700         }
 701     }
 702 
 703     class PropertyListener implements PropertyChangeListener {
 704         @Override
 705         public void propertyChange(final PropertyChangeEvent e) {
 706             final String name = e.getPropertyName();
 707             if (FRAME_TYPE.equals(name)) {
 708                 if (e.getNewValue() instanceof String) {
 709                     setFrameType((String)e.getNewValue());
 710                 }
 711             } else if (IS_PALETTE_PROPERTY.equals(name)) {
 712                 if (e.getNewValue() != null) {
 713                     setPalette(((Boolean)e.getNewValue()).booleanValue());
 714                 } else {
 715                     setPalette(false);
 716                 }
 717                 // TODO: CPlatformWindow?
 718             } else if ("windowModified".equals(name) || CPlatformWindow.WINDOW_DOCUMENT_MODIFIED.equals(name)) {
 719                 // repaint title bar
 720                 setDocumentEdited(((Boolean)e.getNewValue()).booleanValue());
 721                 frame.repaint(0, 0, frame.getWidth(), frame.getBorder().getBorderInsets(frame).top);
 722             } else if ("resizable".equals(name) || "state".equals(name) || "iconable".equals(name) || "maximizable".equals(name) || "closable".equals(name)) {
 723                 if ("resizable".equals(name)) {
 724                     frame.revalidate();
 725                 }
 726                 frame.repaint();
 727             } else if ("title".equals(name)) {
 728                 frame.repaint();
 729             } else if ("componentOrientation".equals(name)) {
 730                 frame.revalidate();
 731                 frame.repaint();
 732             } else if (JInternalFrame.IS_SELECTED_PROPERTY.equals(name)) {
 733                 final Component source = (Component)(e.getSource());
 734                 updateComponentTreeUIActivation(source, frame.isSelected() ? Boolean.TRUE : Boolean.FALSE);
 735             }
 736 
 737         }
 738     } // end class PaletteListener
 739 
 740     static final InternalFrameShadow documentWindowShadow = new InternalFrameShadow() {
 741         @Override
 742         Border getForegroundShadowBorder() {
 743             return new AquaUtils.SlicedShadowBorder(new Painter() {
 744                 @Override
 745                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 746                     g.setColor(new Color(0, 0, 0, 196));
 747                     g.fillRoundRect(x, y, w, h, 16, 16);
 748                     g.fillRect(x, y + h - 16, w, 16);
 749                 }
 750             }, new Painter() {
 751                 @Override
 752                 public void paint(final Graphics g, int x, int y, int w, int h) {
 753                     g.setColor(new Color(0, 0, 0, 64));
 754                     g.drawLine(x + 2, y - 8, x + w - 2, y - 8);
 755                 }
 756             },
 757             0, 7, 1.1f, 1.0f, 24, 51, 51, 25, 25, 25, 25);
 758         }
 759 
 760         @Override
 761         Border getBackgroundShadowBorder() {
 762             return new AquaUtils.SlicedShadowBorder(new Painter() {
 763                 @Override
 764                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 765                     g.setColor(new Color(0, 0, 0, 128));
 766                     g.fillRoundRect(x - 3, y - 8, w + 6, h, 16, 16);
 767                     g.fillRect(x - 3, y + h - 20, w + 6, 19);
 768                 }
 769             }, new Painter() {
 770                 @Override
 771                 public void paint(final Graphics g, int x, int y, int w, int h) {
 772                     g.setColor(new Color(0, 0, 0, 32));
 773                     g.drawLine(x, y - 11, x + w - 1, y - 11);
 774                 }
 775             },
 776             0, 0, 3.0f, 1.0f, 10, 51, 51, 25, 25, 25, 25);
 777         }
 778     };
 779 
 780     static final InternalFrameShadow paletteWindowShadow = new InternalFrameShadow() {
 781         @Override
 782         Border getForegroundShadowBorder() {
 783             return new AquaUtils.SlicedShadowBorder(new Painter() {
 784                 @Override
 785                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 786                     g.setColor(new Color(0, 0, 0, 128));
 787                     g.fillRect(x, y + 3, w, h - 3);
 788                 }
 789             }, null,
 790             0, 3, 1.0f, 1.0f, 10, 25, 25, 12, 12, 12, 12);
 791         }
 792 
 793         @Override
 794         Border getBackgroundShadowBorder() {
 795             return getForegroundShadowBorder();
 796         }
 797     };
 798 
 799     @SuppressWarnings("serial") // Superclass is not serializable across versions
 800     static class CompoundUIBorder extends CompoundBorder implements UIResource {
 801         public CompoundUIBorder(final Border inside, final Border outside) { super(inside, outside); }
 802     }
 803 
 804     abstract static class InternalFrameShadow extends RecyclableSingleton<Border> {
 805         abstract Border getForegroundShadowBorder();
 806         abstract Border getBackgroundShadowBorder();
 807 
 808         @Override
 809         protected Border getInstance() {
 810             final Border fgShadow = getForegroundShadowBorder();
 811             final Border bgShadow = getBackgroundShadowBorder();
 812 
 813             return new Border() {
 814                 @Override
 815                 public Insets getBorderInsets(final Component c) {
 816                     return fgShadow.getBorderInsets(c);
 817                 }
 818 
 819                 @Override
 820                 public boolean isBorderOpaque() {
 821                     return false;
 822                 }
 823 
 824                 @Override
 825                 public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) {
 826                     if (((JInternalFrame)c).isSelected()) {
 827                         fgShadow.paintBorder(c, g, x, y, w, h);
 828                     } else {
 829                         bgShadow.paintBorder(c, g, x, y, w, h);
 830                     }
 831                 }
 832             };
 833         }
 834     }
 835 
 836     static final RecyclableSingleton<Icon> RESIZE_ICON = new RecyclableSingleton<Icon>() {
 837         @Override
 838         protected Icon getInstance() {
 839             return new AquaIcon.ScalingJRSUIIcon(11, 11) {
 840                 @Override
 841                 public void initIconPainter(final AquaPainter<JRSUIState> iconState) {
 842                     iconState.state.set(Widget.GROW_BOX_TEXTURED);
 843                     iconState.state.set(WindowType.UTILITY);
 844                 }
 845             };
 846         }
 847     };
 848 
 849     @SuppressWarnings("serial") // Superclass is not serializable across versions
 850     private final class ResizeBox extends JLabel
 851             implements MouseListener, MouseMotionListener, MouseWheelListener,
 852             ComponentListener, PropertyChangeListener, UIResource {
 853 
 854         private final JLayeredPane layeredPane;
 855         private Dimension originalSize;
 856         private Point originalLocation;
 857 
 858         ResizeBox(final JLayeredPane layeredPane) {
 859             super(RESIZE_ICON.get());
 860             setSize(11, 11);
 861             this.layeredPane = layeredPane;
 862 
 863             addMouseListener(this);
 864             addMouseMotionListener(this);
 865             addMouseWheelListener(this);
 866         }
 867 
 868         void addListeners() {
 869             frame.addPropertyChangeListener("resizable", this);
 870         }
 871 
 872         void removeListeners() {
 873             frame.removePropertyChangeListener("resizable", this);
 874         }
 875 
 876         void repositionResizeBox() {
 877             if (frame == null) { setSize(0, 0); } else { setSize(11, 11); }
 878             setLocation(layeredPane.getWidth() - 12, layeredPane.getHeight() - 12);
 879         }
 880 
 881         void resizeInternalFrame(final Point pt) {
 882             if (originalLocation == null || frame == null) return;
 883 
 884             final Container parent = frame.getParent();
 885             if (!(parent instanceof JDesktopPane)) return;
 886 
 887             final Point newPoint = SwingUtilities.convertPoint(this, pt, frame);
 888             int deltaX = originalLocation.x - newPoint.x;
 889             int deltaY = originalLocation.y - newPoint.y;
 890             final Dimension min = frame.getMinimumSize();
 891             final Dimension max = frame.getMaximumSize();
 892 
 893             final int newX = frame.getX();
 894             final int newY = frame.getY();
 895             int newW = frame.getWidth();
 896             int newH = frame.getHeight();
 897 
 898             final Rectangle parentBounds = parent.getBounds();
 899 
 900             if (originalSize.width - deltaX < min.width) {
 901                 deltaX = originalSize.width - min.width;
 902             }  else if (originalSize.width - deltaX > max.width) {
 903                 deltaX = -(max.width - originalSize.width);
 904             }
 905 
 906             if (newX + originalSize.width - deltaX > parentBounds.width) {
 907                 deltaX = newX + originalSize.width - parentBounds.width;
 908             }
 909 
 910             if (originalSize.height - deltaY < min.height) {
 911                 deltaY = originalSize.height - min.height;
 912             }  else if (originalSize.height - deltaY > max.height) {
 913                 deltaY = -(max.height - originalSize.height);
 914             }
 915 
 916             if (newY + originalSize.height - deltaY > parentBounds.height) {
 917                 deltaY = newY + originalSize.height - parentBounds.height;
 918             }
 919 
 920             newW = originalSize.width - deltaX;
 921             newH = originalSize.height - deltaY;
 922 
 923             getDesktopManager().resizeFrame(frame, newX, newY, newW, newH);
 924         }
 925 
 926         boolean testGrowboxPoint(final int x, final int y, final int w, final int h) {
 927             return (w - x) + (h - y) < 12;
 928         }
 929 
 930         void forwardEventToFrame(final MouseEvent e) {
 931             final Point pt = new Point();
 932             final Component c = getComponentToForwardTo(e, pt);
 933             if (c == null) return;
 934             c.dispatchEvent(new MouseEvent(c, e.getID(), e.getWhen(), e.getModifiers(), pt.x, pt.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()));
 935         }
 936 
 937         Component getComponentToForwardTo(final MouseEvent e, final Point dst) {
 938             if (frame == null) return null;
 939             final Container contentPane = frame.getContentPane();
 940             if (contentPane == null) return null;
 941             Point pt = SwingUtilities.convertPoint(this, e.getPoint(), contentPane);
 942             final Component c = SwingUtilities.getDeepestComponentAt(contentPane, pt.x, pt.y);
 943             if (c == null) return null;
 944             pt = SwingUtilities.convertPoint(contentPane, pt, c);
 945             if (dst != null) dst.setLocation(pt);
 946             return c;
 947         }
 948 
 949         @Override
 950         public void mouseClicked(final MouseEvent e) {
 951             forwardEventToFrame(e);
 952         }
 953 
 954         @Override
 955         public void mouseEntered(final MouseEvent e) { }
 956 
 957         @Override
 958         public void mouseExited(final MouseEvent e) { }
 959 
 960         @Override
 961         public void mousePressed(final MouseEvent e) {
 962             if (frame == null) return;
 963 
 964             if (frame.isResizable() && !frame.isMaximum() && testGrowboxPoint(e.getX(), e.getY(), getWidth(), getHeight())) {
 965                 originalLocation = SwingUtilities.convertPoint(this, e.getPoint(), frame);
 966                 originalSize = frame.getSize();
 967                 getDesktopManager().beginResizingFrame(frame, SwingConstants.SOUTH_EAST);
 968                 return;
 969             }
 970 
 971             forwardEventToFrame(e);
 972         }
 973 
 974         @Override
 975         public void mouseReleased(final MouseEvent e) {
 976             if (originalLocation != null) {
 977                 resizeInternalFrame(e.getPoint());
 978                 originalLocation = null;
 979                 getDesktopManager().endResizingFrame(frame);
 980                 return;
 981             }
 982 
 983             forwardEventToFrame(e);
 984         }
 985 
 986         @Override
 987         public void mouseDragged(final MouseEvent e) {
 988             resizeInternalFrame(e.getPoint());
 989             repositionResizeBox();
 990         }
 991 
 992         @Override
 993         public void mouseMoved(final MouseEvent e) { }
 994 
 995         @Override
 996         public void mouseWheelMoved(final MouseWheelEvent e) {
 997             final Point pt = new Point();
 998             final Component c = getComponentToForwardTo(e, pt);
 999             if (c == null) return;
1000             c.dispatchEvent(new MouseWheelEvent(c, e.getID(), e.getWhen(),
1001                     e.getModifiers(), pt.x, pt.y, e.getXOnScreen(), e.getYOnScreen(),
1002                     e.getClickCount(), e.isPopupTrigger(), e.getScrollType(),
1003                     e.getScrollAmount(), e.getWheelRotation(),
1004                     e.getPreciseWheelRotation()));
1005         }
1006 
1007         @Override
1008         public void componentResized(final ComponentEvent e) {
1009             repositionResizeBox();
1010         }
1011 
1012         @Override
1013         public void componentShown(final ComponentEvent e) {
1014             repositionResizeBox();
1015         }
1016 
1017         @Override
1018         public void componentMoved(final ComponentEvent e) {
1019             repositionResizeBox();
1020         }
1021 
1022         @Override
1023         public void componentHidden(final ComponentEvent e) { }
1024 
1025         @Override
1026         public void propertyChange(final PropertyChangeEvent evt) {
1027             if (!"resizable".equals(evt.getPropertyName())) return;
1028             setVisible(Boolean.TRUE.equals(evt.getNewValue()));
1029         }
1030     }
1031 }