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