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</code>
 299      * and adds it to the frame.
 300      * Reverse process for the <code>currentPane</code>.
 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         public void mouseReleased(final MouseEvent e) {
 435             if (didForwardEvent(e)) return;
 436 
 437             fDraggingFrame = false;
 438 
 439             if (fWhichButtonPressed != -1) {
 440                 final int newButton = fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY());
 441 
 442                 final int buttonPresed = fWhichButtonPressed;
 443                 fWhichButtonPressed = -1;
 444                 fMouseOverPressedButton = false;
 445 
 446                 if (buttonPresed == newButton) {
 447                     fMouseOverPressedButton = false;
 448                     fRollover = false; // not sure if this is needed?
 449 
 450                     fAquaBorder.doButtonAction(frame, buttonPresed);
 451                 }
 452 
 453                 updateRollover(e);
 454                 repaintButtons();
 455                 return;
 456             }
 457 
 458             if (discardRelease) {
 459                 discardRelease = false;
 460                 return;
 461             }
 462             if (resizeDir == RESIZE_NONE) getDesktopManager().endDraggingFrame(frame);
 463             else {
 464                 final Container c = frame.getTopLevelAncestor();
 465                 if (c instanceof JFrame) {
 466                     ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 467 
 468                     ((JFrame)frame.getTopLevelAncestor()).getGlassPane().setVisible(false);
 469                 } else if (c instanceof JApplet) {
 470                     ((JApplet)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 471                     ((JApplet)c).getGlassPane().setVisible(false);
 472                 } else if (c instanceof JWindow) {
 473                     ((JWindow)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 474                     ((JWindow)c).getGlassPane().setVisible(false);
 475                 } else if (c instanceof JDialog) {
 476                     ((JDialog)c).getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 477                     ((JDialog)c).getGlassPane().setVisible(false);
 478                 }
 479                 getDesktopManager().endResizingFrame(frame);
 480             }
 481             _x = 0;
 482             _y = 0;
 483             __x = 0;
 484             __y = 0;
 485             startingBounds = null;
 486             resizeDir = RESIZE_NONE;
 487         }
 488 
 489         @Override
 490         public void mousePressed(final MouseEvent e) {
 491             if (didForwardEvent(e)) return;
 492 
 493             final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null);
 494             __x = e.getX();
 495             __y = e.getY();
 496             _x = p.x;
 497             _y = p.y;
 498             startingBounds = frame.getBounds();
 499             resizeDir = RESIZE_NONE;
 500 
 501             if (updatePressed(e)) { return; }
 502 
 503             if (!frame.isSelected()) {
 504                 try {
 505                     frame.setSelected(true);
 506                 } catch(final PropertyVetoException e1) {}
 507             }
 508 
 509             if (isTitleBarDraggableArea(e)) {
 510                 getDesktopManager().beginDraggingFrame(frame);
 511                 fDraggingFrame = true;
 512                 return;
 513             }
 514 
 515             if (e.getSource() == getNorthPane()) {
 516                 getDesktopManager().beginDraggingFrame(frame);
 517                 return;
 518             }
 519 
 520             if (!frame.isResizable()) { return; }
 521 
 522             if (e.getSource() == frame) {
 523                 discardRelease = true;
 524                 return;
 525             }
 526         }
 527 
 528         // returns true if we have handled the pressed
 529         public boolean updatePressed(final MouseEvent e) {
 530             // get the component.
 531             fWhichButtonPressed = getButtonHit(e);
 532             fMouseOverPressedButton = true;
 533             repaintButtons();
 534             return (fWhichButtonPressed >= 0);
 535             // e.getX(), e.getY()
 536         }
 537 
 538         public int getButtonHit(final MouseEvent e) {
 539             return fAquaBorder.getWhichButtonHit(frame, e.getX(), e.getY());
 540         }
 541 
 542         public boolean isTitleBarDraggableArea(final MouseEvent e) {
 543             if (e.getSource() != frame) return false;
 544 
 545             final Point point = e.getPoint();
 546             final Insets insets = frame.getInsets();
 547 
 548             if (point.y < insets.top - fAquaBorder.getTitleHeight()) return false;
 549             if (point.y > insets.top) return false;
 550             if (point.x < insets.left) return false;
 551             if (point.x > frame.getWidth() - insets.left - insets.right) return false;
 552 
 553             return true;
 554         }
 555 
 556         @Override
 557         public void mouseDragged(final MouseEvent e) {
 558 // do not forward drags
 559 //            if (didForwardEvent(e)) return;
 560 
 561             if (startingBounds == null) {
 562                 // (STEVE) Yucky work around for bug ID 4106552
 563                 return;
 564             }
 565 
 566             if (fWhichButtonPressed != -1) {
 567                 // track the button we started on.
 568                 final int newButton = getButtonHit(e);
 569                 fMouseOverPressedButton = (fWhichButtonPressed == newButton);
 570                 repaintButtons();
 571                 return;
 572             }
 573 
 574             final Point p = SwingUtilities.convertPoint((Component)e.getSource(), e.getX(), e.getY(), null);
 575             final int deltaX = _x - p.x;
 576             final int deltaY = _y - p.y;
 577             int newX, newY;
 578 
 579             // Handle a MOVE
 580             if (!fDraggingFrame && e.getSource() != getNorthPane()) return;
 581 
 582             if (frame.isMaximum() || ((e.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK)) {
 583                 // don't allow moving of frames if maximixed or left mouse
 584                 // button was not used.
 585                 return;
 586             }
 587 
 588             final Dimension s = frame.getParent().getSize();
 589             final int pWidth = s.width;
 590             final int pHeight = s.height;
 591 
 592             final Insets i = frame.getInsets();
 593             newX = startingBounds.x - deltaX;
 594             newY = startingBounds.y - deltaY;
 595 
 596             // Make sure we stay in-bounds
 597             if (newX + i.left <= -__x) newX = -__x - i.left;
 598             if (newY + i.top <= -__y) newY = -__y - i.top;
 599             if (newX + __x + i.right > pWidth) newX = pWidth - __x - i.right;
 600             if (newY + __y + i.bottom > pHeight) newY = pHeight - __y - i.bottom;
 601 
 602             getDesktopManager().dragFrame(frame, newX, newY);
 603             return;
 604         }
 605 
 606         @Override
 607         public void mouseMoved(final MouseEvent e) {
 608             if (didForwardEvent(e)) return;
 609             updateRollover(e);
 610         }
 611 
 612         // guards against accidental infinite recursion
 613         boolean isTryingToForwardEvent = false;
 614         boolean didForwardEvent(final MouseEvent e) {
 615             if (isTryingToForwardEvent) return true; // we didn't actually...but we wound up back where we started.
 616 
 617             isTryingToForwardEvent = true;
 618             final boolean didForwardEvent = didForwardEventInternal(e);
 619             isTryingToForwardEvent = false;
 620 
 621             return didForwardEvent;
 622         }
 623 
 624         boolean didForwardEventInternal(final MouseEvent e) {
 625             if (fDraggingFrame) return false;
 626 
 627             final Point originalPoint = e.getPoint();
 628             if (!isEventInWindowShadow(originalPoint)) return false;
 629 
 630             final Container parent = frame.getParent();
 631             if (!(parent instanceof JDesktopPane)) return false;
 632             final JDesktopPane pane = (JDesktopPane)parent;
 633             final Point parentPoint = SwingUtilities.convertPoint(frame, originalPoint, parent);
 634 
 635         /*     // debug drawing
 636             Graphics g = parent.getGraphics();
 637             g.setColor(Color.red);
 638             g.drawLine(parentPoint.x, parentPoint.y, parentPoint.x, parentPoint.y);
 639         */
 640 
 641             final Component hitComponent = findComponentToHitBehindMe(pane, parentPoint);
 642             if (hitComponent == null || hitComponent == frame) return false;
 643 
 644             final Point hitComponentPoint = SwingUtilities.convertPoint(pane, parentPoint, hitComponent);
 645             hitComponent.dispatchEvent(
 646                     new MouseEvent(hitComponent, e.getID(), e.getWhen(),
 647                                    e.getModifiers(), hitComponentPoint.x,
 648                                    hitComponentPoint.y, e.getClickCount(),
 649                                    e.isPopupTrigger(), e.getButton()));
 650             return true;
 651         }
 652 
 653         Component findComponentToHitBehindMe(final JDesktopPane pane, final Point parentPoint) {
 654             final JInternalFrame[] allFrames = pane.getAllFrames();
 655 
 656             boolean foundSelf = false;
 657             for (final JInternalFrame f : allFrames) {
 658                 if (f == frame) { foundSelf = true; continue; }
 659                 if (!foundSelf) continue;
 660 
 661                 final Rectangle bounds = f.getBounds();
 662                 if (bounds.contains(parentPoint)) return f;
 663             }
 664 
 665             return pane;
 666         }
 667 
 668         boolean isEventInWindowShadow(final Point point) {
 669             final Rectangle bounds = frame.getBounds();
 670             final Insets insets = frame.getInsets();
 671             insets.top -= fAquaBorder.getTitleHeight();
 672 
 673             if (point.x < insets.left) return true;
 674             if (point.x > bounds.width - insets.right) return true;
 675             if (point.y < insets.top) return true;
 676             if (point.y > bounds.height - insets.bottom) return true;
 677 
 678             return false;
 679         }
 680     }
 681 
 682     static void updateComponentTreeUIActivation(final Component c, final Object active) {
 683         if (c instanceof javax.swing.JComponent) {
 684             ((javax.swing.JComponent)c).putClientProperty(AquaFocusHandler.FRAME_ACTIVE_PROPERTY, active);
 685         }
 686 
 687         Component[] children = null;
 688 
 689         if (c instanceof javax.swing.JMenu) {
 690             children = ((javax.swing.JMenu)c).getMenuComponents();
 691         } else if (c instanceof Container) {
 692             children = ((Container)c).getComponents();
 693         }
 694 
 695         if (children != null) {
 696             for (final Component element : children) {
 697                 updateComponentTreeUIActivation(element, active);
 698             }
 699         }
 700     }
 701 
 702     class PropertyListener implements PropertyChangeListener {
 703         @Override
 704         public void propertyChange(final PropertyChangeEvent e) {
 705             final String name = e.getPropertyName();
 706             if (FRAME_TYPE.equals(name)) {
 707                 if (e.getNewValue() instanceof String) {
 708                     setFrameType((String)e.getNewValue());
 709                 }
 710             } else if (IS_PALETTE_PROPERTY.equals(name)) {
 711                 if (e.getNewValue() != null) {
 712                     setPalette(((Boolean)e.getNewValue()).booleanValue());
 713                 } else {
 714                     setPalette(false);
 715                 }
 716                 // TODO: CPlatformWindow?
 717             } else if ("windowModified".equals(name) || CPlatformWindow.WINDOW_DOCUMENT_MODIFIED.equals(name)) {
 718                 // repaint title bar
 719                 setDocumentEdited(((Boolean)e.getNewValue()).booleanValue());
 720                 frame.repaint(0, 0, frame.getWidth(), frame.getBorder().getBorderInsets(frame).top);
 721             } else if ("resizable".equals(name) || "state".equals(name) || "iconable".equals(name) || "maximizable".equals(name) || "closable".equals(name)) {
 722                 if ("resizable".equals(name)) {
 723                     frame.revalidate();
 724                 }
 725                 frame.repaint();
 726             } else if ("title".equals(name)) {
 727                 frame.repaint();
 728             } else if ("componentOrientation".equals(name)) {
 729                 frame.revalidate();
 730                 frame.repaint();
 731             } else if (JInternalFrame.IS_SELECTED_PROPERTY.equals(name)) {
 732                 final Component source = (Component)(e.getSource());
 733                 updateComponentTreeUIActivation(source, frame.isSelected() ? Boolean.TRUE : Boolean.FALSE);
 734             }
 735 
 736         }
 737     } // end class PaletteListener
 738 
 739     static final InternalFrameShadow documentWindowShadow = new InternalFrameShadow() {
 740         @Override
 741         Border getForegroundShadowBorder() {
 742             return new AquaUtils.SlicedShadowBorder(new Painter() {
 743                 @Override
 744                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 745                     g.setColor(new Color(0, 0, 0, 196));
 746                     g.fillRoundRect(x, y, w, h, 16, 16);
 747                     g.fillRect(x, y + h - 16, w, 16);
 748                 }
 749             }, new Painter() {
 750                 @Override
 751                 public void paint(final Graphics g, int x, int y, int w, int h) {
 752                     g.setColor(new Color(0, 0, 0, 64));
 753                     g.drawLine(x + 2, y - 8, x + w - 2, y - 8);
 754                 }
 755             },
 756             0, 7, 1.1f, 1.0f, 24, 51, 51, 25, 25, 25, 25);
 757         }
 758 
 759         @Override
 760         Border getBackgroundShadowBorder() {
 761             return new AquaUtils.SlicedShadowBorder(new Painter() {
 762                 @Override
 763                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 764                     g.setColor(new Color(0, 0, 0, 128));
 765                     g.fillRoundRect(x - 3, y - 8, w + 6, h, 16, 16);
 766                     g.fillRect(x - 3, y + h - 20, w + 6, 19);
 767                 }
 768             }, new Painter() {
 769                 @Override
 770                 public void paint(final Graphics g, int x, int y, int w, int h) {
 771                     g.setColor(new Color(0, 0, 0, 32));
 772                     g.drawLine(x, y - 11, x + w - 1, y - 11);
 773                 }
 774             },
 775             0, 0, 3.0f, 1.0f, 10, 51, 51, 25, 25, 25, 25);
 776         }
 777     };
 778 
 779     static final InternalFrameShadow paletteWindowShadow = new InternalFrameShadow() {
 780         @Override
 781         Border getForegroundShadowBorder() {
 782             return new AquaUtils.SlicedShadowBorder(new Painter() {
 783                 @Override
 784                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 785                     g.setColor(new Color(0, 0, 0, 128));
 786                     g.fillRect(x, y + 3, w, h - 3);
 787                 }
 788             }, null,
 789             0, 3, 1.0f, 1.0f, 10, 25, 25, 12, 12, 12, 12);
 790         }
 791 
 792         @Override
 793         Border getBackgroundShadowBorder() {
 794             return getForegroundShadowBorder();
 795         }
 796     };
 797 
 798     @SuppressWarnings("serial") // Superclass is not serializable across versions
 799     static class CompoundUIBorder extends CompoundBorder implements UIResource {
 800         public CompoundUIBorder(final Border inside, final Border outside) { super(inside, outside); }
 801     }
 802 
 803     abstract static class InternalFrameShadow extends RecyclableSingleton<Border> {
 804         abstract Border getForegroundShadowBorder();
 805         abstract Border getBackgroundShadowBorder();
 806 
 807         @Override
 808         protected Border getInstance() {
 809             final Border fgShadow = getForegroundShadowBorder();
 810             final Border bgShadow = getBackgroundShadowBorder();
 811 
 812             return new Border() {
 813                 @Override
 814                 public Insets getBorderInsets(final Component c) {
 815                     return fgShadow.getBorderInsets(c);
 816                 }
 817 
 818                 @Override
 819                 public boolean isBorderOpaque() {
 820                     return false;
 821                 }
 822 
 823                 @Override
 824                 public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) {
 825                     if (((JInternalFrame)c).isSelected()) {
 826                         fgShadow.paintBorder(c, g, x, y, w, h);
 827                     } else {
 828                         bgShadow.paintBorder(c, g, x, y, w, h);
 829                     }
 830                 }
 831             };
 832         }
 833     }
 834 
 835     static final RecyclableSingleton<Icon> RESIZE_ICON = new RecyclableSingleton<Icon>() {
 836         @Override
 837         protected Icon getInstance() {
 838             return new AquaIcon.ScalingJRSUIIcon(11, 11) {
 839                 @Override
 840                 public void initIconPainter(final AquaPainter<JRSUIState> iconState) {
 841                     iconState.state.set(Widget.GROW_BOX_TEXTURED);
 842                     iconState.state.set(WindowType.UTILITY);
 843                 }
 844             };
 845         }
 846     };
 847 
 848     @SuppressWarnings("serial") // Superclass is not serializable across versions
 849     private final class ResizeBox extends JLabel
 850             implements MouseListener, MouseMotionListener, MouseWheelListener,
 851             ComponentListener, PropertyChangeListener, UIResource {
 852 
 853         private final JLayeredPane layeredPane;
 854         private Dimension originalSize;
 855         private Point originalLocation;
 856 
 857         ResizeBox(final JLayeredPane layeredPane) {
 858             super(RESIZE_ICON.get());
 859             setSize(11, 11);
 860             this.layeredPane = layeredPane;
 861 
 862             addMouseListener(this);
 863             addMouseMotionListener(this);
 864             addMouseWheelListener(this);
 865         }
 866 
 867         void addListeners() {
 868             frame.addPropertyChangeListener("resizable", this);
 869         }
 870 
 871         void removeListeners() {
 872             frame.removePropertyChangeListener("resizable", this);
 873         }
 874 
 875         void repositionResizeBox() {
 876             if (frame == null) { setSize(0, 0); } else { setSize(11, 11); }
 877             setLocation(layeredPane.getWidth() - 12, layeredPane.getHeight() - 12);
 878         }
 879 
 880         void resizeInternalFrame(final Point pt) {
 881             if (originalLocation == null || frame == null) return;
 882 
 883             final Container parent = frame.getParent();
 884             if (!(parent instanceof JDesktopPane)) return;
 885 
 886             final Point newPoint = SwingUtilities.convertPoint(this, pt, frame);
 887             int deltaX = originalLocation.x - newPoint.x;
 888             int deltaY = originalLocation.y - newPoint.y;
 889             final Dimension min = frame.getMinimumSize();
 890             final Dimension max = frame.getMaximumSize();
 891 
 892             final int newX = frame.getX();
 893             final int newY = frame.getY();
 894             int newW = frame.getWidth();
 895             int newH = frame.getHeight();
 896 
 897             final Rectangle parentBounds = parent.getBounds();
 898 
 899             if (originalSize.width - deltaX < min.width) {
 900                 deltaX = originalSize.width - min.width;
 901             }  else if (originalSize.width - deltaX > max.width) {
 902                 deltaX = -(max.width - originalSize.width);
 903             }
 904 
 905             if (newX + originalSize.width - deltaX > parentBounds.width) {
 906                 deltaX = newX + originalSize.width - parentBounds.width;
 907             }
 908 
 909             if (originalSize.height - deltaY < min.height) {
 910                 deltaY = originalSize.height - min.height;
 911             }  else if (originalSize.height - deltaY > max.height) {
 912                 deltaY = -(max.height - originalSize.height);
 913             }
 914 
 915             if (newY + originalSize.height - deltaY > parentBounds.height) {
 916                 deltaY = newY + originalSize.height - parentBounds.height;
 917             }
 918 
 919             newW = originalSize.width - deltaX;
 920             newH = originalSize.height - deltaY;
 921 
 922             getDesktopManager().resizeFrame(frame, newX, newY, newW, newH);
 923         }
 924 
 925         boolean testGrowboxPoint(final int x, final int y, final int w, final int h) {
 926             return (w - x) + (h - y) < 12;
 927         }
 928 
 929         void forwardEventToFrame(final MouseEvent e) {
 930             final Point pt = new Point();
 931             final Component c = getComponentToForwardTo(e, pt);
 932             if (c == null) return;
 933             c.dispatchEvent(new MouseEvent(c, e.getID(), e.getWhen(), e.getModifiers(), pt.x, pt.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()));
 934         }
 935 
 936         Component getComponentToForwardTo(final MouseEvent e, final Point dst) {
 937             if (frame == null) return null;
 938             final Container contentPane = frame.getContentPane();
 939             if (contentPane == null) return null;
 940             Point pt = SwingUtilities.convertPoint(this, e.getPoint(), contentPane);
 941             final Component c = SwingUtilities.getDeepestComponentAt(contentPane, pt.x, pt.y);
 942             if (c == null) return null;
 943             pt = SwingUtilities.convertPoint(contentPane, pt, c);
 944             if (dst != null) dst.setLocation(pt);
 945             return c;
 946         }
 947 
 948         @Override
 949         public void mouseClicked(final MouseEvent e) {
 950             forwardEventToFrame(e);
 951         }
 952 
 953         @Override
 954         public void mouseEntered(final MouseEvent e) { }
 955 
 956         @Override
 957         public void mouseExited(final MouseEvent e) { }
 958 
 959         @Override
 960         public void mousePressed(final MouseEvent e) {
 961             if (frame == null) return;
 962 
 963             if (frame.isResizable() && !frame.isMaximum() && testGrowboxPoint(e.getX(), e.getY(), getWidth(), getHeight())) {
 964                 originalLocation = SwingUtilities.convertPoint(this, e.getPoint(), frame);
 965                 originalSize = frame.getSize();
 966                 getDesktopManager().beginResizingFrame(frame, SwingConstants.SOUTH_EAST);
 967                 return;
 968             }
 969 
 970             forwardEventToFrame(e);
 971         }
 972 
 973         @Override
 974         public void mouseReleased(final MouseEvent e) {
 975             if (originalLocation != null) {
 976                 resizeInternalFrame(e.getPoint());
 977                 originalLocation = null;
 978                 getDesktopManager().endResizingFrame(frame);
 979                 return;
 980             }
 981 
 982             forwardEventToFrame(e);
 983         }
 984 
 985         @Override
 986         public void mouseDragged(final MouseEvent e) {
 987             resizeInternalFrame(e.getPoint());
 988             repositionResizeBox();
 989         }
 990 
 991         @Override
 992         public void mouseMoved(final MouseEvent e) { }
 993 
 994         @Override
 995         public void mouseWheelMoved(final MouseWheelEvent e) {
 996             final Point pt = new Point();
 997             final Component c = getComponentToForwardTo(e, pt);
 998             if (c == null) return;
 999             c.dispatchEvent(new MouseWheelEvent(c, e.getID(), e.getWhen(),
1000                     e.getModifiers(), pt.x, pt.y, e.getXOnScreen(), e.getYOnScreen(),
1001                     e.getClickCount(), e.isPopupTrigger(), e.getScrollType(),
1002                     e.getScrollAmount(), e.getWheelRotation(),
1003                     e.getPreciseWheelRotation()));
1004         }
1005 
1006         @Override
1007         public void componentResized(final ComponentEvent e) {
1008             repositionResizeBox();
1009         }
1010 
1011         @Override
1012         public void componentShown(final ComponentEvent e) {
1013             repositionResizeBox();
1014         }
1015 
1016         @Override
1017         public void componentMoved(final ComponentEvent e) {
1018             repositionResizeBox();
1019         }
1020 
1021         @Override
1022         public void componentHidden(final ComponentEvent e) { }
1023 
1024         @Override
1025         public void propertyChange(final PropertyChangeEvent evt) {
1026             if (!"resizable".equals(evt.getPropertyName())) return;
1027             setVisible(Boolean.TRUE.equals(evt.getNewValue()));
1028         }
1029     }
1030 }