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