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 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 }