1 /* 2 * Adapted from MetalRootPaneUI comments below were copied from that class 3 */ 4 5 package com.l2fprod.gui.plaf.skin; 6 7 import com.l2fprod.util.AccessUtils; 8 9 import java.awt.*; 10 import java.awt.event.ComponentEvent; 11 import java.awt.event.InputEvent; 12 import java.awt.event.MouseEvent; 13 import java.awt.image.BufferedImage; 14 import java.beans.PropertyChangeEvent; 15 16 import javax.swing.Icon; 17 import javax.swing.JComponent; 18 import javax.swing.JFrame; 19 import javax.swing.JLayeredPane; 20 import javax.swing.JMenu; 21 import javax.swing.JRootPane; 22 import javax.swing.LookAndFeel; 23 import javax.swing.SwingUtilities; 24 import javax.swing.UIManager; 25 import javax.swing.event.MouseInputListener; 26 import javax.swing.plaf.ColorUIResource; 27 import javax.swing.plaf.ComponentUI; 28 import javax.swing.plaf.basic.BasicRootPaneUI; 29 30 /** 31 * 32 * Provides the metal look and feel implementation of <code>RootPaneUI</code>. 33 * <p> 34 * <code>MetalRootPaneUI</code> provides support for the <code>windowDecorationStyle</code> 35 * property of <code>JRootPane</code>.<code>MetalRootPaneUI</code> does 36 * this by way of installing a custom <code>LayoutManager</code>, a private 37 * <code>Component</code> to render the appropriate widgets, and a private 38 * <code>Border</code>. The <code>LayoutManager</code> is always 39 * installed, regardless of the value of the <code>windowDecorationStyle</code> 40 * property, but the <code>Border</code> and <code>Component</code> are 41 * only installed/added if the <code>windowDecorationStyle</code> is other 42 * than <code>JRootPane.NONE</code>. 43 * <p> 44 * <strong>Warning:</strong> Serialized objects of this class will not be 45 * compatible with future Swing releases. The current serialization support is 46 * appropriate for short term storage or RMI between applications running the 47 * same version of Swing. As of 1.4, support for long term storage of all 48 * JavaBeans <sup><font size="-2">TM</font></sup> has been added to the 49 * <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}. 50 * 51 * @version 1.16 02/04/02 52 * @author Terry Kellerman 53 * @since 1.4 54 */ 55 public final class SkinRootPaneUI extends BasicRootPaneUI { 56 57 // TO MAKE THE CODE COMPILE WITH JDK < 1.4 58 public final static int Frame_MAXIMIZED_BOTH = 59 AccessUtils.getIntValue(JFrame.class, "MAXIMIZED_BOTH"); 60 61 public final static int JRootPane_NONE = 62 AccessUtils.getIntValue(JRootPane.class, "NONE"); 63 getExtendedState(Frame p_Frame)64 public static int getExtendedState(Frame p_Frame) { 65 return AccessUtils.getAsInt(p_Frame, "getExtendedState"); 66 } 67 setExtendedState(Frame p_Frame, int p_Value)68 public static void setExtendedState(Frame p_Frame, int p_Value) { 69 AccessUtils.setAsInt(p_Frame, "setExtendedState", p_Value); 70 } 71 getWindowDecorationStyle(JRootPane p_Pane)72 public static int getWindowDecorationStyle(JRootPane p_Pane) { 73 return AccessUtils.getAsInt(p_Pane, "getWindowDecorationStyle"); 74 } 75 76 private Skin skin = SkinLookAndFeel.getSkin(); 77 private Window.FrameWindow title = null; 78 79 /** 80 * The amount of space (in pixels) that the cursor is changed on. 81 */ 82 private static final int CORNER_DRAG_WIDTH = 16; 83 84 /** 85 * Region from edges that dragging is active from. 86 */ 87 private static final int BORDER_DRAG_THICKNESS = 5; 88 89 /** 90 * Window the <code>JRootPane</code> is in. 91 */ 92 private java.awt.Window window; 93 94 /** 95 * <code>JComponent</code> providing window decorations. This will be null 96 * if not providing window decorations. 97 */ 98 private SkinTitlePane titlePane; 99 100 /** 101 * <code>MouseInputListener</code> that is added to the parent <code>Window</code> 102 * the <code>JRootPane</code> is contained in. 103 */ 104 private MouseInputListener mouseInputListener; 105 /** 106 * The <code>LayoutManager</code> that is set on the <code>JRootPane</code>. 107 */ 108 private LayoutManager layoutManager; 109 110 /** 111 * <code>LayoutManager</code> of the <code>JRootPane</code> before we 112 * replaced it. 113 */ 114 private LayoutManager savedOldLayout; 115 116 /** 117 * <code>JRootPane</code> providing the look and feel for. 118 */ 119 private JRootPane root; 120 121 /** 122 * <code>Cursor</code> used to track the cursor set by the user. This is 123 * initially <code>Cursor.DEFAULT_CURSOR</code>. 124 */ 125 private Cursor lastCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); 126 127 /** 128 * Creates a UI for a <code>JRootPane</code>. 129 * 130 * @param c the JRootPane the RootPaneUI will be created for 131 * @return the RootPaneUI implementation for the passed in JRootPane 132 */ createUI(JComponent c)133 public static ComponentUI createUI(JComponent c) { 134 return new SkinRootPaneUI(); 135 } 136 translateSource(MouseEvent ev)137 private static java.awt.Window translateSource(MouseEvent ev) { 138 Object source = ev.getSource(); 139 java.awt.Window w; 140 141 if (source.getClass() == SkinTitlePane.class) { 142 SkinTitlePane titleSource = (SkinTitlePane)source; 143 Window.FrameWindow win = (Window.FrameWindow)titleSource.getWindow(); 144 w = win.getMainFrame(); 145 } else 146 w = (java.awt.Window)ev.getSource(); 147 return w; 148 } 149 150 /** 151 * Invokes supers implementation of <code>installUI</code> to install the 152 * necessary state onto the passed in <code>JRootPane</code> to render the 153 * metal look and feel implementation of <code>RootPaneUI</code>. If the 154 * <code>windowDecorationStyle</code> property of the <code>JRootPane</code> 155 * is other than <code>JRootPane.NONE</code>, this will add a custom 156 * <code>Component</code> to render the widgets to <code>JRootPane</code>, 157 * as well as installing a custom <code>Border</code> and <code>LayoutManager</code> 158 * on the <code>JRootPane</code>. 159 * 160 * @param c the JRootPane to install state onto 161 */ installUI(JComponent c)162 public void installUI(JComponent c) { 163 super.installUI(c); 164 root = (JRootPane)c; 165 int style = getWindowDecorationStyle(root); 166 if (style != JRootPane_NONE) { 167 installClientDecorations(root); 168 169 } 170 //skin.getFrame().installSkin(c); 171 172 } 173 174 /** 175 * Invokes supers implementation to uninstall any of its state. This will 176 * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>. 177 * If a <code>Component</code> has been added to the <code>JRootPane</code> 178 * to render the window decoration style, this method will remove it. 179 * Similarly, this will revert the Border and LayoutManager of the <code>JRootPane</code> 180 * to what it was before <code>installUI</code> was invoked. 181 * 182 * @param c the JRootPane to uninstall state from 183 */ uninstallUI(JComponent c)184 public void uninstallUI(JComponent c) { 185 super.uninstallUI(c); 186 uninstallClientDecorations(root); 187 188 layoutManager = null; 189 mouseInputListener = null; 190 root = null; 191 } 192 193 /** 194 * Installs the appropriate <code>Border</code> onto the <code>JRootPane</code>. 195 */ installBorder(JRootPane root)196 void installBorder(JRootPane root) { 197 int style = getWindowDecorationStyle(root); 198 199 if (style == JRootPane_NONE) { 200 LookAndFeel.uninstallBorder(root); 201 } else { 202 203 //LookAndFeel.installBorder(root, borderKeys[style]); 204 skin.getFrame().installSkin(root); 205 } 206 } 207 208 /** 209 * Removes any border that may have been installed. 210 */ uninstallBorder(JRootPane root)211 private void uninstallBorder(JRootPane root) { 212 LookAndFeel.uninstallBorder(root); 213 } 214 215 /** 216 * Installs the necessary Listeners on the parent <code>Window</code>, if 217 * there is one. 218 * <p> 219 * This takes the parent so that cleanup can be done from <code>removeNotify</code>, 220 * at which point the parent hasn't been reset yet. 221 * 222 * @param parent The parent of the JRootPane 223 */ installWindowListeners(JRootPane root, Component parent)224 private void installWindowListeners(JRootPane root, Component parent) { 225 if (parent instanceof java.awt.Window) { 226 window = (java.awt.Window)parent; 227 } else { 228 window = SwingUtilities.getWindowAncestor(parent); 229 } 230 if (window != null) { 231 232 if (mouseInputListener == null) { 233 mouseInputListener = createWindowMouseInputListener(root); 234 } 235 236 window.addMouseListener(mouseInputListener); 237 window.addMouseMotionListener(mouseInputListener); 238 } 239 } 240 241 /** 242 * Uninstalls the necessary Listeners on the <code>Window</code> the 243 * Listeners were last installed on. 244 */ uninstallWindowListeners(JRootPane root)245 private void uninstallWindowListeners(JRootPane root) { 246 if (window != null) { 247 248 window.removeMouseListener(mouseInputListener); 249 window.removeMouseMotionListener(mouseInputListener); 250 } 251 } 252 253 /** 254 * Installs the appropriate LayoutManager on the <code>JRootPane</code> to 255 * render the window decorations. 256 */ installLayout(JRootPane root)257 private void installLayout(JRootPane root) { 258 if (layoutManager == null) { 259 layoutManager = createLayoutManager(); 260 } 261 savedOldLayout = root.getLayout(); 262 root.setLayout(layoutManager); 263 } 264 265 /** 266 * Uninstalls the previously installed <code>LayoutManager</code>. 267 */ uninstallLayout(JRootPane root)268 private void uninstallLayout(JRootPane root) { 269 if (savedOldLayout != null) { 270 root.setLayout(savedOldLayout); 271 savedOldLayout = null; 272 } 273 } 274 275 /** 276 * Installs the necessary state onto the JRootPane to render client 277 * decorations. This is ONLY invoked if the <code>JRootPane</code> has a 278 * decoration style other than <code>JRootPane.NONE</code>. 279 */ installClientDecorations(JRootPane root)280 private void installClientDecorations(JRootPane root) { 281 installBorder(root); 282 283 JComponent titlePane = createTitlePane(root); 284 285 setTitlePane(root, titlePane); 286 installWindowListeners(root, root.getParent()); 287 installLayout(root); 288 adjustIconAndBackground(); 289 if (window != null) { 290 root.revalidate(); 291 root.repaint(); 292 293 } 294 } 295 adjustIconAndBackground()296 private void adjustIconAndBackground() { 297 if (window != null) { 298 299 title.setFrame(window); 300 301 // the titlePane may not have been updated yet as it was created with a 302 // empty FrameWindow so update its actions. 303 titlePane.enableActions(); 304 // callback to notify the titlePane the FrameWindow was set 305 titlePane.windowSet(); 306 307 String[] colors = skin.getColors(); 308 boolean cont = true; 309 for (int i = 0; i < colors.length && cont; i++) { 310 String string = colors[i]; 311 if (string.equalsIgnoreCase("desktop")) { 312 cont = false; 313 try { 314 Color background = new ColorUIResource(Color.decode(colors[i + 1])); 315 window.setBackground(background); 316 } catch (NumberFormatException e) { 317 } 318 } 319 } 320 java.awt.Window target = title.getMainFrame(); 321 if (target instanceof JFrame) { 322 JFrame current = (JFrame)target; 323 Icon provided = titlePane.getWindow().getFrameIcon(); 324 Icon general = UIManager.getIcon("InternalFrame.icon"); 325 Icon toWork = provided; 326 if (provided == null) 327 toWork = general; 328 if (toWork != null) { 329 BufferedImage theIcon = 330 new BufferedImage( 331 toWork.getIconWidth(), 332 toWork.getIconHeight(), 333 BufferedImage.TYPE_INT_ARGB); 334 Graphics graph = theIcon.getGraphics(); 335 toWork.paintIcon(null, graph, 0, 0); 336 current.setIconImage(theIcon); 337 } 338 } 339 } 340 341 } 342 resetIconAndBackground()343 private void resetIconAndBackground() { 344 if (window != null) { 345 346 window.setBackground(null); 347 title.setFrame(null); 348 } 349 350 } 351 352 /** 353 * Uninstalls any state that <code>installClientDecorations</code> has 354 * installed. 355 * <p> 356 * NOTE: This may be called if you haven't installed client decorations yet 357 * (ie before <code>installClientDecorations</code> has been invoked). 358 */ uninstallClientDecorations(JRootPane root)359 private void uninstallClientDecorations(JRootPane root) { 360 uninstallBorder(root); 361 uninstallWindowListeners(root); 362 setTitlePane(root, null); 363 uninstallLayout(root); 364 resetIconAndBackground(); 365 // We have to revalidate/repaint root if the style is JRootPane.NONE 366 // only. When we needs to call revalidate/repaint with other styles 367 // the installClientDecorations is always called after this method 368 // imediatly and it will cause the revalidate/repaint at the proper 369 // time. 370 int style = getWindowDecorationStyle(root); 371 if (style == JRootPane_NONE) { 372 root.repaint(); 373 root.revalidate(); 374 } 375 // Reset the cursor, as we may have changed it to a resize cursor 376 if (window != null) { 377 window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 378 } 379 window = null; 380 } 381 382 /** 383 * Returns the <code>JComponent</code> to render the window decoration 384 * style. 385 */ createTitlePane(JRootPane root)386 private JComponent createTitlePane(JRootPane root) { 387 JComponent titlePane = 388 new SkinTitlePane(title = new Window.FrameWindow()) { 389 // overriden to set the popup to not be lightweight. it 390 // resulted in the popupmenu being hidden, not correctly 391 // z-ordered. Using a JWindow does the trick 392 protected JMenu createSystemMenu() { 393 JMenu menu = new JMenu(" "); 394 menu.getPopupMenu().setLightWeightPopupEnabled(false); 395 return menu; 396 } 397 }; 398 titlePane.setOpaque(false); 399 return titlePane; 400 } 401 402 /** 403 * Returns a <code>MouseListener</code> that will be added to the <code>Window</code> 404 * containing the <code>JRootPane</code>. 405 */ createWindowMouseInputListener(JRootPane root)406 private MouseInputListener createWindowMouseInputListener(JRootPane root) { 407 return new MouseInputHandler(); 408 } 409 410 /** 411 * Returns a <code>LayoutManager</code> that will be set on the <code>JRootPane</code>. 412 */ createLayoutManager()413 private LayoutManager createLayoutManager() { 414 return new MetalRootLayout(); 415 } 416 417 /** 418 * Sets the window title pane -- the JComponent used to provide a plaf a way 419 * to override the native operating system's window title pane with one whose 420 * look and feel are controlled by the plaf. The plaf creates and sets this 421 * value; the default is null, implying a native operating system window 422 * title pane. 423 */ setTitlePane(JRootPane root, JComponent titlePane)424 private void setTitlePane(JRootPane root, JComponent titlePane) { 425 JLayeredPane layeredPane = root.getLayeredPane(); 426 JComponent oldTitlePane = getTitlePane(); 427 428 if (oldTitlePane != null) { 429 oldTitlePane.setVisible(false); 430 layeredPane.remove(oldTitlePane); 431 } 432 if (titlePane != null) { 433 layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER); 434 titlePane.setVisible(true); 435 } 436 this.titlePane = (SkinTitlePane)titlePane; 437 } 438 439 /** 440 * Returns the <code>JComponent</code> rendering the title pane. If this 441 * returns null, it implies there is no need to render window decorations. 442 * 443 * @return the current window title pane, or null 444 * @see #setTitlePane 445 */ getTitlePane()446 private JComponent getTitlePane() { 447 return titlePane; 448 } 449 getFrameWindow()450 private Window.FrameWindow getFrameWindow() { 451 return title; 452 } getMainWindow()453 private java.awt.Window getMainWindow() { 454 return window; 455 } 456 457 /** 458 * Returns the <code>JRootPane</code> we're providing the look and feel 459 * for. 460 */ getRootPane()461 private JRootPane getRootPane() { 462 return root; 463 } 464 465 /** 466 * Invoked when a property changes. <code>MetalRootPaneUI</code> is 467 * primarily interested in events originating from the <code>JRootPane</code> 468 * it has been installed on identifying the property <code>windowDecorationStyle</code>. 469 * If the <code>windowDecorationStyle</code> has changed to a value other 470 * than <code>JRootPane.NONE</code>, this will add a <code>Component</code> 471 * to the <code>JRootPane</code> to render the window decorations, as well 472 * as installing a <code>Border</code> on the <code>JRootPane</code>. On 473 * the other hand, if the <code>windowDecorationStyle</code> has changed to 474 * <code>JRootPane.NONE</code>, this will remove the <code>Component</code> 475 * that has been added to the <code>JRootPane</code> as well resetting the 476 * Border to what it was before <code>installUI</code> was invoked. 477 * 478 * @param e A PropertyChangeEvent object describing the event source and the 479 * property that has changed. 480 */ propertyChange(PropertyChangeEvent e)481 public void propertyChange(PropertyChangeEvent e) { 482 super.propertyChange(e); 483 484 String propertyName = e.getPropertyName(); 485 if (propertyName == null) { 486 return; 487 } 488 489 if (propertyName.equals("windowDecorationStyle")) { 490 JRootPane root = (JRootPane)e.getSource(); 491 int style = getWindowDecorationStyle(root); 492 493 // This is potentially more than needs to be done, 494 // but it rarely happens and makes the install/uninstall process 495 // simpler. MetalTitlePane also assumes it will be recreated if 496 // the decoration style changes. 497 uninstallClientDecorations(root); 498 if (style != JRootPane_NONE) { 499 installClientDecorations(root); 500 } 501 } else if (propertyName.equals("ancestor")) { 502 uninstallWindowListeners(root); 503 if (getWindowDecorationStyle((JRootPane)e.getSource()) 504 != JRootPane_NONE) { 505 installWindowListeners(root, root.getParent()); 506 } 507 } 508 return; 509 } 510 adjust( Rectangle bounds, Dimension min, int deltaX, int deltaY, int deltaWidth, int deltaHeight)511 public static void adjust( 512 Rectangle bounds, 513 Dimension min, 514 int deltaX, 515 int deltaY, 516 int deltaWidth, 517 int deltaHeight) { 518 bounds.x += deltaX; 519 bounds.y += deltaY; 520 bounds.width += deltaWidth; 521 bounds.height += deltaHeight; 522 if (min != null) { 523 if (bounds.width < min.width) { 524 int correction = min.width - bounds.width; 525 if (deltaX != 0) { 526 bounds.x -= correction; 527 } 528 bounds.width = min.width; 529 } 530 if (bounds.height < min.height) { 531 int correction = min.height - bounds.height; 532 if (deltaY != 0) { 533 bounds.y -= correction; 534 } 535 bounds.height = min.height; 536 } 537 } 538 } 539 540 /** 541 * Returns the corner that contains the point <code>x</code>,<code>y</code>, 542 * or -1 if the position doesn't match a corner. 543 */ calculateCorner(Component c, int x, int y)544 public static int calculateCorner(Component c, int x, int y) { 545 int xPosition = calculatePosition(x, c.getWidth()); 546 int yPosition = calculatePosition(y, c.getHeight()); 547 548 if (xPosition == -1 || yPosition == -1) { 549 return -1; 550 } 551 return yPosition * 5 + xPosition; 552 } 553 554 /** 555 * Returns the Cursor to render for the specified corner. This returns 0 if 556 * the corner doesn't map to a valid Cursor 557 */ getCursor(int corner)558 public static int getCursor(int corner) { 559 if (corner == -1) { 560 return 0; 561 } 562 return SkinRootPaneUI.cursorMapping[corner]; 563 } 564 565 /** 566 * Returns an integer indicating the position of <code>spot</code> in 567 * <code>width</code>. The return value will be: 0 if 568 * < BORDER_DRAG_THICKNESS 1 if < CORNER_DRAG_WIDTH 2 if >= 569 * CORNER_DRAG_WIDTH &&< width - BORDER_DRAG_THICKNESS 3 if >= width - 570 * CORNER_DRAG_WIDTH 4 if >= width - BORDER_DRAG_THICKNESS 5 otherwise 571 */ calculatePosition(int spot, int width)572 public static int calculatePosition(int spot, int width) { 573 if (spot < SkinRootPaneUI.BORDER_DRAG_THICKNESS) { 574 return 0; 575 } 576 if (spot < SkinRootPaneUI.CORNER_DRAG_WIDTH) { 577 return 1; 578 } 579 if (spot >= (width - SkinRootPaneUI.BORDER_DRAG_THICKNESS)) { 580 return 4; 581 } 582 if (spot >= (width - SkinRootPaneUI.CORNER_DRAG_WIDTH)) { 583 return 3; 584 } 585 return 2; 586 } 587 588 /** 589 * A custom layout manager that is responsible for the layout of layeredPane, 590 * glassPane, menuBar and titlePane, if one has been installed. 591 */ 592 // NOTE: Ideally this would extends JRootPane.RootLayout, but that 593 // would force this to be non-static. 594 private static class MetalRootLayout implements LayoutManager2 { 595 /** 596 * Returns the amount of space the layout would like to have. 597 * 598 * @param parent the Container for which this layout manager is being used 599 * @return a Dimension object containing the layout's preferred size 600 */ preferredLayoutSize(Container parent)601 public Dimension preferredLayoutSize(Container parent) { 602 Dimension cpd, mbd, tpd; 603 int cpWidth = 0; 604 int cpHeight = 0; 605 int mbWidth = 0; 606 int mbHeight = 0; 607 int tpWidth = 0; 608 int tpHeight = 0; 609 Insets i = parent.getInsets(); 610 JRootPane root = (JRootPane)parent; 611 612 if (root.getContentPane() != null) { 613 cpd = root.getContentPane().getPreferredSize(); 614 } else { 615 cpd = root.getSize(); 616 } 617 if (cpd != null) { 618 cpWidth = cpd.width; 619 cpHeight = cpd.height; 620 } 621 622 if (root.getJMenuBar() != null) { 623 mbd = root.getJMenuBar().getPreferredSize(); 624 if (mbd != null) { 625 mbWidth = mbd.width; 626 mbHeight = mbd.height; 627 } 628 } 629 630 if (getWindowDecorationStyle(root) != JRootPane_NONE 631 && (root.getUI() instanceof SkinRootPaneUI)) { 632 JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane(); 633 if (titlePane != null) { 634 tpd = titlePane.getPreferredSize(); 635 if (tpd != null) { 636 tpWidth = tpd.width; 637 tpHeight = tpd.height; 638 } 639 } 640 } 641 642 return new Dimension( 643 Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right, 644 cpHeight + mbHeight + tpHeight + i.top + i.bottom); 645 } 646 647 /** 648 * Returns the minimum amount of space the layout needs. 649 * 650 * @param parent the Container for which this layout manager is being used 651 * @return a Dimension object containing the layout's minimum size 652 */ minimumLayoutSize(Container parent)653 public Dimension minimumLayoutSize(Container parent) { 654 Dimension cpd, mbd, tpd; 655 int cpWidth = 0; 656 int cpHeight = 0; 657 int mbWidth = 0; 658 int mbHeight = 0; 659 int tpWidth = 0; 660 int tpHeight = 0; 661 Insets i = parent.getInsets(); 662 JRootPane root = (JRootPane)parent; 663 664 // This code is enabled only if the parent is not a window with 665 // look and feel decorations. Otherwise the look and feel honors 666 // the minimum size and the window can not be resized to a small 667 // size in the case where a component in the component hierarchy 668 // returns a wrong/big minimum size. In such case, the minimum 669 // size will be the one of the title pane. 670 if (!(root.getParent() instanceof java.awt.Window 671 && getWindowDecorationStyle(root) != JRootPane_NONE)) { 672 if (root.getContentPane() != null) { 673 cpd = root.getContentPane().getMinimumSize(); 674 } else { 675 cpd = root.getSize(); 676 } 677 if (cpd != null) { 678 cpWidth = cpd.width; 679 cpHeight = cpd.height; 680 } 681 } 682 683 if (root.getJMenuBar() != null) { 684 mbd = root.getJMenuBar().getMinimumSize(); 685 if (mbd != null) { 686 mbWidth = mbd.width; 687 mbHeight = mbd.height; 688 } 689 } 690 if (getWindowDecorationStyle(root) != JRootPane_NONE 691 && (root.getUI() instanceof SkinRootPaneUI)) { 692 JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane(); 693 if (titlePane != null) { 694 tpd = titlePane.getMinimumSize(); 695 if (tpd != null) { 696 tpWidth = tpd.width; 697 tpHeight = tpd.height; 698 } 699 } 700 } 701 702 // FYI, MetalRootLayout has a bug in the dimension as it uses 703 // tpWidth in the height calculation 704 // Now logged as 705 // http://developer.java.sun.com/developer/bugParade/bugs/4916923.html 706 return new Dimension( 707 Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right, 708 cpHeight + mbHeight + tpHeight + i.top + i.bottom); 709 } 710 711 /** 712 * Returns the maximum amount of space the layout can use. 713 * 714 * @param target the Container for which this layout manager is being used 715 * @return a Dimension object containing the layout's maximum size 716 */ maximumLayoutSize(Container target)717 public Dimension maximumLayoutSize(Container target) { 718 Dimension cpd, mbd, tpd; 719 int cpWidth = Integer.MAX_VALUE; 720 int cpHeight = Integer.MAX_VALUE; 721 int mbWidth = Integer.MAX_VALUE; 722 int mbHeight = Integer.MAX_VALUE; 723 int tpWidth = Integer.MAX_VALUE; 724 int tpHeight = Integer.MAX_VALUE; 725 Insets i = target.getInsets(); 726 JRootPane root = (JRootPane)target; 727 728 if (root.getContentPane() != null) { 729 cpd = root.getContentPane().getMaximumSize(); 730 if (cpd != null) { 731 cpWidth = cpd.width; 732 cpHeight = cpd.height; 733 } 734 } 735 736 if (root.getJMenuBar() != null) { 737 mbd = root.getJMenuBar().getMaximumSize(); 738 if (mbd != null) { 739 mbWidth = mbd.width; 740 mbHeight = mbd.height; 741 } 742 } 743 744 if (getWindowDecorationStyle(root) != JRootPane_NONE 745 && (root.getUI() instanceof SkinRootPaneUI)) { 746 JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane(); 747 if (titlePane != null) { 748 tpd = titlePane.getMaximumSize(); 749 if (tpd != null) { 750 tpWidth = tpd.width; 751 tpHeight = tpd.height; 752 } 753 } 754 } 755 756 int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight); 757 // Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE 758 // Only will happen if sums to more than 2 billion units. Not likely. 759 if (maxHeight != Integer.MAX_VALUE) { 760 maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom; 761 } 762 763 int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth); 764 // Similar overflow comment as above 765 if (maxWidth != Integer.MAX_VALUE) { 766 maxWidth += i.left + i.right; 767 } 768 769 return new Dimension(maxWidth, maxHeight); 770 } 771 772 /** 773 * Instructs the layout manager to perform the layout for the specified 774 * container. 775 * 776 * @param parent the Container for which this layout manager is being used 777 */ layoutContainer(Container parent)778 public void layoutContainer(Container parent) { 779 JRootPane root = (JRootPane)parent; 780 Rectangle b = root.getBounds(); 781 Insets i = root.getInsets(); 782 int nextY = 0; 783 int w = b.width - i.right - i.left; 784 int h = b.height - i.top - i.bottom; 785 786 if (root.getLayeredPane() != null) { 787 root.getLayeredPane().setBounds(i.left, i.top, w, h); 788 } 789 if (root.getGlassPane() != null) { 790 root.getGlassPane().setBounds(i.left, i.top, w, h); 791 } 792 // Note: This is laying out the children in the layeredPane, 793 // technically, these are not our children. 794 if (getWindowDecorationStyle(root) != JRootPane_NONE 795 && (root.getUI() instanceof SkinRootPaneUI)) { 796 JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane(); 797 if (titlePane != null) { 798 Dimension tpd = titlePane.getPreferredSize(); 799 if (tpd != null) { 800 int tpHeight = tpd.height; 801 titlePane.setBounds(0, 0, w, tpHeight); 802 nextY += tpHeight; 803 } 804 } 805 } 806 if (root.getJMenuBar() != null) { 807 Dimension mbd = root.getJMenuBar().getPreferredSize(); 808 root.getJMenuBar().setBounds(0, nextY, w, mbd.height); 809 nextY += mbd.height; 810 } 811 if (root.getContentPane() != null) { 812 root.getContentPane().setBounds(0, nextY, w, h < nextY ? 0 : h - nextY); 813 } 814 } 815 addLayoutComponent(String name, Component comp)816 public void addLayoutComponent(String name, Component comp) { 817 } removeLayoutComponent(Component comp)818 public void removeLayoutComponent(Component comp) { 819 } addLayoutComponent(Component comp, Object constraints)820 public void addLayoutComponent(Component comp, Object constraints) { 821 } getLayoutAlignmentX(Container target)822 public float getLayoutAlignmentX(Container target) { 823 return 0.0f; 824 } getLayoutAlignmentY(Container target)825 public float getLayoutAlignmentY(Container target) { 826 return 0.0f; 827 } invalidateLayout(Container target)828 public void invalidateLayout(Container target) { 829 } 830 } 831 832 /** 833 * Maps from positions to cursor type. Refer to calculateCorner and 834 * calculatePosition for details of this. 835 */ 836 private static final int[] cursorMapping = 837 new int[] { 838 Cursor.NW_RESIZE_CURSOR, 839 Cursor.NW_RESIZE_CURSOR, 840 Cursor.N_RESIZE_CURSOR, 841 Cursor.NE_RESIZE_CURSOR, 842 Cursor.NE_RESIZE_CURSOR, 843 Cursor.NW_RESIZE_CURSOR, 844 0, 845 0, 846 0, 847 Cursor.NE_RESIZE_CURSOR, 848 Cursor.W_RESIZE_CURSOR, 849 0, 850 0, 851 0, 852 Cursor.E_RESIZE_CURSOR, 853 Cursor.SW_RESIZE_CURSOR, 854 0, 855 0, 856 0, 857 Cursor.SE_RESIZE_CURSOR, 858 Cursor.SW_RESIZE_CURSOR, 859 Cursor.SW_RESIZE_CURSOR, 860 Cursor.S_RESIZE_CURSOR, 861 Cursor.SE_RESIZE_CURSOR, 862 Cursor.SE_RESIZE_CURSOR }; 863 864 /** 865 * MouseInputHandler is responsible for handling resize/moving of the Window. 866 * It sets the cursor directly on the Window when then mouse moves over a hot 867 * spot. 868 */ 869 private class MouseInputHandler implements MouseInputListener { 870 /** 871 * Set to true if the drag operation is moving the window. 872 */ 873 private boolean isMovingWindow; 874 875 /** 876 * Used to determine the corner the resize is occuring from. 877 */ 878 private int dragCursor; 879 880 /** 881 * X location the mouse went down on for a drag operation. 882 */ 883 private int dragOffsetX; 884 885 /** 886 * Y location the mouse went down on for a drag operation. 887 */ 888 private int dragOffsetY; 889 890 /** 891 * Width of the window when the drag started. 892 */ 893 private int dragWidth; 894 895 /** 896 * Height of the window when the drag started. 897 */ 898 private int dragHeight; 899 900 /** 901 * Sometimes mouse entered events generated twice, without proper mouse exit event. If we dont check for this 902 * possibility, this will cause strange mouse icons. For example the resize cursor appears in the whole window. 903 */ 904 private boolean mouseAlreadyEntered = false; 905 mousePressed(MouseEvent ev)906 public void mousePressed(MouseEvent ev) { 907 if (root==null || getWindowDecorationStyle(root) == JRootPane_NONE) { 908 return; 909 } 910 Point dragWindowOffset = ev.getPoint(); 911 java.awt.Window w = translateSource(ev); 912 if (w != null) { 913 w.toFront(); 914 } 915 Point convertedDragWindowOffset = 916 SwingUtilities.convertPoint(w, dragWindowOffset, getTitlePane()); 917 918 if (getTitlePane() != null 919 && getTitlePane().contains(convertedDragWindowOffset)) { 920 if (!getFrameWindow().isMaximum() 921 && dragWindowOffset.y >= BORDER_DRAG_THICKNESS 922 && dragWindowOffset.x >= BORDER_DRAG_THICKNESS 923 && dragWindowOffset.x < w.getWidth() - BORDER_DRAG_THICKNESS) { 924 isMovingWindow = true; 925 dragOffsetX = dragWindowOffset.x; 926 dragOffsetY = dragWindowOffset.y; 927 } 928 } 929 //this was an else if before but the title panel would not 930 //cause a resize 931 if (getFrameWindow().isResizable() 932 && !getFrameWindow().isShaded() 933 && !getFrameWindow().isMaximum()) { 934 //&& ((frameState & Frame_MAXIMIZED_BOTH) == 0) 935 //|| (d != null && d.isResizable())) { 936 dragOffsetX = dragWindowOffset.x; 937 dragOffsetY = dragWindowOffset.y; 938 dragWidth = w.getWidth(); 939 dragHeight = w.getHeight(); 940 //System.out.println("Calulation cursor"); 941 dragCursor = 942 SkinRootPaneUI.getCursor( 943 SkinRootPaneUI.calculateCorner( 944 w, 945 dragWindowOffset.x, 946 dragWindowOffset.y)); 947 } 948 } 949 mouseReleased(MouseEvent ev)950 public void mouseReleased(MouseEvent ev) { 951 if (dragCursor != 0 && window != null && !window.isValid()) { 952 // Some Window systems validate as you resize, others won't, 953 // thus the check for validity before repainting. 954 window.validate(); 955 getRootPane().repaint(); 956 } 957 isMovingWindow = false; 958 dragCursor = 0; 959 } 960 mouseMoved(MouseEvent ev)961 public void mouseMoved(MouseEvent ev) { 962 JRootPane root = getRootPane(); 963 964 if (root!=null && getWindowDecorationStyle(root) == JRootPane_NONE) { 965 return; 966 } 967 968 java.awt.Window w = translateSource(ev); 969 970 // Update the cursor 971 int cursor = 972 SkinRootPaneUI.getCursor( 973 SkinRootPaneUI.calculateCorner(w, ev.getX(), ev.getY())); 974 975 if (cursor != 0 976 && getFrameWindow().isResizable() 977 && !getFrameWindow().isShaded() 978 && !getFrameWindow().isMaximum()) { 979 w.setCursor(Cursor.getPredefinedCursor(cursor)); 980 } else { 981 w.setCursor(lastCursor); 982 } 983 } 984 mouseDragged(MouseEvent ev)985 public void mouseDragged(MouseEvent ev) { 986 java.awt.Window w = translateSource(ev); 987 Point pt = ev.getPoint(); 988 // System.out.println("MovingWindow:"+isMovingWindow+" 989 // dragcursor:"+dragCursor); 990 if (isMovingWindow) { 991 Point windowPt = w.getLocationOnScreen(); 992 993 windowPt.x += pt.x - dragOffsetX; 994 windowPt.y += pt.y - dragOffsetY; 995 w.setLocation(windowPt); 996 } else if (dragCursor != 0) { 997 Rectangle r = w.getBounds(); 998 Rectangle startBounds = new Rectangle(r); 999 Dimension min = w.getMinimumSize(); 1000 1001 switch (dragCursor) { 1002 case Cursor.E_RESIZE_CURSOR : 1003 SkinRootPaneUI.adjust( 1004 r, 1005 min, 1006 0, 1007 0, 1008 pt.x + (dragWidth - dragOffsetX) - r.width, 1009 0); 1010 break; 1011 case Cursor.S_RESIZE_CURSOR : 1012 SkinRootPaneUI.adjust( 1013 r, 1014 min, 1015 0, 1016 0, 1017 0, 1018 pt.y + (dragHeight - dragOffsetY) - r.height); 1019 break; 1020 case Cursor.N_RESIZE_CURSOR : 1021 SkinRootPaneUI.adjust( 1022 r, 1023 min, 1024 0, 1025 pt.y - dragOffsetY, 1026 0, 1027 - (pt.y - dragOffsetY)); 1028 break; 1029 case Cursor.W_RESIZE_CURSOR : 1030 SkinRootPaneUI.adjust( 1031 r, 1032 min, 1033 pt.x - dragOffsetX, 1034 0, 1035 - (pt.x - dragOffsetX), 1036 0); 1037 break; 1038 case Cursor.NE_RESIZE_CURSOR : 1039 SkinRootPaneUI.adjust( 1040 r, 1041 min, 1042 0, 1043 pt.y - dragOffsetY, 1044 pt.x + (dragWidth - dragOffsetX) - r.width, 1045 - (pt.y - dragOffsetY)); 1046 break; 1047 case Cursor.SE_RESIZE_CURSOR : 1048 SkinRootPaneUI.adjust( 1049 r, 1050 min, 1051 0, 1052 0, 1053 pt.x + (dragWidth - dragOffsetX) - r.width, 1054 pt.y + (dragHeight - dragOffsetY) - r.height); 1055 break; 1056 case Cursor.NW_RESIZE_CURSOR : 1057 SkinRootPaneUI.adjust( 1058 r, 1059 min, 1060 pt.x - dragOffsetX, 1061 pt.y - dragOffsetY, 1062 - (pt.x - dragOffsetX), 1063 - (pt.y - dragOffsetY)); 1064 break; 1065 case Cursor.SW_RESIZE_CURSOR : 1066 SkinRootPaneUI.adjust( 1067 r, 1068 min, 1069 pt.x - dragOffsetX, 1070 0, 1071 - (pt.x - dragOffsetX), 1072 pt.y + (dragHeight - dragOffsetY) - r.height); 1073 break; 1074 default : 1075 break; 1076 } 1077 if (!r.equals(startBounds)) { 1078 if (getFrameWindow().isResizable()) { 1079 w.setBounds(r); 1080 // Defer repaint/validate on mouseReleased unless dynamic 1081 // layout is active. 1082 if (Boolean 1083 .TRUE 1084 .equals( 1085 AccessUtils.invoke( 1086 Toolkit.getDefaultToolkit(), 1087 "isDynamicLayoutActive", 1088 null, 1089 null))) { 1090 w.validate(); 1091 getRootPane().repaint(); 1092 1093 } 1094 getFrameWindow().dispatchEvent( 1095 new ComponentEvent( 1096 getMainWindow(), 1097 ComponentEvent.COMPONENT_RESIZED)); 1098 } 1099 } 1100 } 1101 } 1102 mouseEntered(MouseEvent ev)1103 public void mouseEntered(MouseEvent ev) { 1104 java.awt.Window w = translateSource(ev); 1105 if (!mouseAlreadyEntered) { 1106 lastCursor = w.getCursor(); 1107 } 1108 mouseAlreadyEntered = true; 1109 mouseMoved(ev); 1110 } 1111 mouseExited(MouseEvent ev)1112 public void mouseExited(MouseEvent ev) { 1113 java.awt.Window w = translateSource(ev); 1114 mouseAlreadyEntered = false; 1115 w.setCursor(lastCursor); 1116 } 1117 mouseClicked(MouseEvent ev)1118 public void mouseClicked(MouseEvent ev) { 1119 java.awt.Window w = translateSource(ev); 1120 Frame f; 1121 1122 if (w instanceof Frame) { 1123 f = (Frame)w; 1124 } else { 1125 return; 1126 } 1127 1128 Point convertedPoint = 1129 SwingUtilities.convertPoint(w, ev.getPoint(), getTitlePane()); 1130 1131 int state = getExtendedState(f); 1132 if (getTitlePane() != null && getTitlePane().contains(convertedPoint)) { 1133 if ((ev.getClickCount() % 2) == 0 1134 && ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) { 1135 if (f.isResizable()) { 1136 if ((state & Frame_MAXIMIZED_BOTH) != 0) { 1137 setExtendedState(f, state & ~Frame_MAXIMIZED_BOTH); 1138 } else { 1139 setExtendedState(f, state | Frame_MAXIMIZED_BOTH); 1140 } 1141 return; 1142 } 1143 } 1144 } 1145 } 1146 1147 } 1148 } 1149