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