0001: /*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030: package org.jvnet.substance;
0031:
0032: import java.awt.*;
0033: import java.awt.event.*;
0034: import java.beans.PropertyChangeEvent;
0035: import java.lang.reflect.Method;
0036: import java.security.*;
0037:
0038: import javax.swing.*;
0039: import javax.swing.border.Border;
0040: import javax.swing.event.MouseInputAdapter;
0041: import javax.swing.event.MouseInputListener;
0042: import javax.swing.plaf.ComponentUI;
0043: import javax.swing.plaf.basic.BasicRootPaneUI;
0044:
0045: import org.jvnet.substance.utils.*;
0046:
0047: /**
0048: * UI for root panes in <b>Substance </b> look and feel.
0049: *
0050: * @author Kirill Grouchnikov
0051: * @author Larry Salibra (fix for defect 198)
0052: */
0053: public class SubstanceRootPaneUI extends BasicRootPaneUI {
0054: /**
0055: * The amount of space (in pixels) that the cursor is changed on.
0056: */
0057: private static final int CORNER_DRAG_WIDTH = 16;
0058:
0059: /**
0060: * Region from edges that dragging is active from.
0061: */
0062: private static final int BORDER_DRAG_THICKNESS = 5;
0063:
0064: /**
0065: * Window the <code>JRootPane</code> is in.
0066: */
0067: private Window window;
0068:
0069: /**
0070: * <code>JComponent</code> providing window decorations. This will be null
0071: * if not providing window decorations.
0072: */
0073: private JComponent titlePane;
0074:
0075: /**
0076: * <code>MouseInputListener</code> that is added to the parent
0077: * <code>Window</code> the <code>JRootPane</code> is contained in.
0078: */
0079: private MouseInputListener substanceMouseInputListener;
0080:
0081: /**
0082: * Mouse listener on the title pane (dragging).
0083: */
0084: private MouseInputListener substanceTitleMouseInputListener;
0085:
0086: /**
0087: * The <code>LayoutManager</code> that is set on the
0088: * <code>JRootPane</code>.
0089: */
0090: private LayoutManager layoutManager;
0091:
0092: /**
0093: * <code>LayoutManager</code> of the <code>JRootPane</code> before we
0094: * replaced it.
0095: */
0096: private LayoutManager savedOldLayout;
0097:
0098: /**
0099: * <code>JRootPane</code> providing the look and feel for.
0100: */
0101: private JRootPane root;
0102:
0103: /**
0104: * Window listener that stops all Substance thread when the last frame is
0105: * disposed.
0106: */
0107: protected WindowListener substanceWindowListener;
0108:
0109: /**
0110: * The current window.
0111: */
0112: protected Window substanceCurrentWindow;
0113:
0114: /**
0115: * Hierarchy listener to keep track of the associated top-level window.
0116: */
0117: protected HierarchyListener substanceHierarchyListener;
0118:
0119: /**
0120: * Component listener to keep track of the primary graphics configuration
0121: * (for recomputing the maximized bounds) - fix for defect 213.
0122: */
0123: protected ComponentListener substanceWindowComponentListener;
0124:
0125: /**
0126: * The graphics configuration that contains the top-left corner of the
0127: * window (fix for defect 213).
0128: */
0129: protected GraphicsConfiguration currentRootPaneGC;
0130:
0131: /**
0132: * <code>Cursor</code> used to track the cursor set by the user. This is
0133: * initially <code>Cursor.DEFAULT_CURSOR</code>.
0134: */
0135: private Cursor lastCursor = Cursor
0136: .getPredefinedCursor(Cursor.DEFAULT_CURSOR);
0137:
0138: /**
0139: * Stores the pointer to {@link MouseInfo#getLocationOnScreen()} method
0140: * (available only under JDK 6.0). Is used to provide fix for defect 198.
0141: */
0142: protected static Method getLocationOnScreenMethod;
0143:
0144: static {
0145: try {
0146: getLocationOnScreenMethod = MouseEvent.class
0147: .getDeclaredMethod("getLocationOnScreen",
0148: new Class[0]);
0149: } catch (Exception e) {
0150: // running under JDK 5.0 or sandbox
0151: getLocationOnScreenMethod = null;
0152: }
0153: }
0154:
0155: /**
0156: * Creates a UI for a <code>JRootPane</code>.
0157: *
0158: * @param c
0159: * the JRootPane the RootPaneUI will be created for
0160: * @return the RootPaneUI implementation for the passed in JRootPane
0161: */
0162: public static ComponentUI createUI(JComponent c) {
0163: return new SubstanceRootPaneUI();
0164: }
0165:
0166: public SubstanceRootPaneUI() {
0167: }
0168:
0169: /**
0170: * Invokes supers implementation of <code>installUI</code> to install the
0171: * necessary state onto the passed in <code>JRootPane</code> to render the
0172: * metal look and feel implementation of <code>RootPaneUI</code>. If the
0173: * <code>windowDecorationStyle</code> property of the
0174: * <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
0175: * this will add a custom <code>Component</code> to render the widgets to
0176: * <code>JRootPane</code>, as well as installing a custom
0177: * <code>Border</code> and <code>LayoutManager</code> on the
0178: * <code>JRootPane</code>.
0179: *
0180: * @param c
0181: * the JRootPane to install state onto
0182: */
0183: @Override
0184: public void installUI(JComponent c) {
0185: super .installUI(c);
0186: this .root = (JRootPane) c;
0187: int style = this .root.getWindowDecorationStyle();
0188: if (style != JRootPane.NONE) {
0189: this .installClientDecorations(this .root);
0190: }
0191: // TitleButtonManager.getManager().register(this.root);
0192: }
0193:
0194: /**
0195: * Invokes supers implementation to uninstall any of its state. This will
0196: * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
0197: * If a <code>Component</code> has been added to the
0198: * <code>JRootPane</code> to render the window decoration style, this
0199: * method will remove it. Similarly, this will revert the Border and
0200: * LayoutManager of the <code>JRootPane</code> to what it was before
0201: * <code>installUI</code> was invoked.
0202: *
0203: * @param c
0204: * the JRootPane to uninstall state from
0205: */
0206: @Override
0207: public void uninstallUI(JComponent c) {
0208: super .uninstallUI(c);
0209: this .uninstallClientDecorations(this .root);
0210:
0211: this .layoutManager = null;
0212: this .substanceMouseInputListener = null;
0213:
0214: // TitleButtonManager.getManager().unregister(this.root);
0215:
0216: this .root = null;
0217: }
0218:
0219: /**
0220: * Installs the appropriate <code>Border</code> onto the
0221: * <code>JRootPane</code>.
0222: *
0223: * @param root
0224: * Root pane.
0225: */
0226: public void installBorder(JRootPane root) {
0227: int style = root.getWindowDecorationStyle();
0228:
0229: if (style == JRootPane.NONE) {
0230: LookAndFeel.uninstallBorder(root);
0231: } else {
0232: LookAndFeel.installBorder(root, "RootPane.border");
0233: }
0234: }
0235:
0236: /**
0237: * Removes any border that may have been installed.
0238: *
0239: * @param root
0240: * Root pane.
0241: */
0242: private void uninstallBorder(JRootPane root) {
0243: LookAndFeel.uninstallBorder(root);
0244: }
0245:
0246: /**
0247: * Installs the necessary Listeners on the parent <code>Window</code>, if
0248: * there is one.
0249: * <p>
0250: * This takes the parent so that cleanup can be done from
0251: * <code>removeNotify</code>, at which point the parent hasn't been reset
0252: * yet.
0253: *
0254: *
0255: * @param root
0256: * Root pane.
0257: * @param parent
0258: * The parent of the JRootPane
0259: */
0260: private void installWindowListeners(JRootPane root, Component parent) {
0261: if (parent instanceof Window) {
0262: this .window = (Window) parent;
0263: } else {
0264: this .window = SwingUtilities.getWindowAncestor(parent);
0265: }
0266: // System.out.println(titlePanes.size() + " entries in map after adding
0267: // "
0268: // + window.getClass().getName() + ":" + window.hashCode());
0269: // for (Iterator<Map.Entry<JComponent, WeakReference<Window>>> it =
0270: // titlePanes
0271: // .entrySet().iterator(); it.hasNext();) {
0272: // Map.Entry<JComponent, WeakReference<Window>> entry = it
0273: // .next();
0274: // Window w = entry.getValue().get();
0275: // System.out.println("\t" + w.getClass().getName()
0276: // + ":" + w.hashCode());
0277: // }
0278:
0279: if (this .window != null) {
0280: if (this .substanceMouseInputListener == null) {
0281: this .substanceMouseInputListener = this
0282: .createWindowMouseInputListener(root);
0283: }
0284: this .window
0285: .addMouseListener(this .substanceMouseInputListener);
0286: this .window
0287: .addMouseMotionListener(this .substanceMouseInputListener);
0288:
0289: if (this .titlePane != null) {
0290: if (this .substanceTitleMouseInputListener == null) {
0291: this .substanceTitleMouseInputListener = new TitleMouseInputHandler();
0292: }
0293: this .titlePane
0294: .addMouseMotionListener(this .substanceTitleMouseInputListener);
0295: this .titlePane
0296: .addMouseListener(this .substanceTitleMouseInputListener);
0297: }
0298: this .setMaximized();
0299: }
0300: }
0301:
0302: /**
0303: * Uninstalls the necessary Listeners on the <code>Window</code> the
0304: * Listeners were last installed on.
0305: *
0306: * @param root
0307: * Root pane.
0308: */
0309: private void uninstallWindowListeners(JRootPane root) {
0310: if (this .window != null) {
0311: this .window
0312: .removeMouseListener(this .substanceMouseInputListener);
0313: this .window
0314: .removeMouseMotionListener(this .substanceMouseInputListener);
0315: }
0316: if (this .titlePane != null) {
0317: this .titlePane
0318: .removeMouseListener(this .substanceTitleMouseInputListener);
0319: this .titlePane
0320: .removeMouseMotionListener(this .substanceTitleMouseInputListener);
0321: }
0322: }
0323:
0324: /**
0325: * Installs the appropriate LayoutManager on the <code>JRootPane</code> to
0326: * render the window decorations.
0327: *
0328: * @param root
0329: * Root pane.
0330: */
0331: private void installLayout(JRootPane root) {
0332: if (this .layoutManager == null) {
0333: this .layoutManager = this .createLayoutManager();
0334: }
0335: this .savedOldLayout = root.getLayout();
0336: root.setLayout(this .layoutManager);
0337: }
0338:
0339: @Override
0340: protected void installListeners(final JRootPane root) {
0341: super .installListeners(root);
0342:
0343: // System.out.println("Listeners on root " + root.hashCode());
0344:
0345: this .substanceHierarchyListener = new HierarchyListener() {
0346: public void hierarchyChanged(HierarchyEvent e) {
0347: Component parent = root.getParent();
0348: if (parent == null) {
0349: // fix for defect 271 - check for null parent
0350: // as early as possible
0351: return;
0352: }
0353: // System.out.println("Root pane " + root.hashCode()
0354: // + " parent : " + parent.getClass().getName());
0355: if (MemoryAnalyzer.isRunning()) {
0356: MemoryAnalyzer.enqueueUsage("Root pane @"
0357: + root.hashCode()
0358: + "\n"
0359: + SubstanceCoreUtilities
0360: .getHierarchy(parent));
0361: }
0362: if (parent.getClass().getName().startsWith(
0363: "org.jdesktop.jdic.tray")
0364: || (parent.getClass().getName().compareTo(
0365: "javax.swing.Popup$HeavyWeightWindow") == 0)) {
0366: // Workaround for bug 240 - using JDIC system tray
0367: // menu results in an HierarchyEvent being fired right
0368: // after a MouseEvent. Somehow, the
0369: // EventQueue.getCurrentEvent() returns the HierarchyEvent
0370: // even when the MouseEvent is being processed, resulting
0371: // in zeroed modifiers set on the ActionEvent passed
0372: // to the action listeners on that menu item.
0373: SwingUtilities.invokeLater(new Runnable() {
0374: public void run() {
0375: root
0376: .removeHierarchyListener(substanceHierarchyListener);
0377: // System.out.println(root.hashCode() + ":"
0378: // + root.getHierarchyListeners().length);
0379: substanceHierarchyListener = null;
0380: };
0381: });
0382: }
0383:
0384: Window currWindow = null;
0385: if (parent instanceof Window) {
0386: currWindow = (Window) parent;
0387: } else {
0388: currWindow = SwingUtilities
0389: .getWindowAncestor(parent);
0390: }
0391: if (substanceWindowListener != null) {
0392: substanceCurrentWindow
0393: .removeWindowListener(substanceWindowListener);
0394: substanceWindowListener = null;
0395: }
0396: if (substanceWindowComponentListener != null) {
0397: substanceCurrentWindow
0398: .removeComponentListener(substanceWindowComponentListener);
0399: substanceWindowComponentListener = null;
0400: }
0401: if (currWindow != null) {
0402: // fix for bug 116 - stopping threads when all frames
0403: // are not displayable
0404: substanceWindowListener = new WindowAdapter() {
0405: @Override
0406: public void windowClosed(WindowEvent e) {
0407: SwingUtilities.invokeLater(new Runnable() {
0408: public void run() {
0409: Frame[] frames = Frame.getFrames();
0410: for (Frame frame : frames) {
0411: if (frame.isDisplayable())
0412: return;
0413: }
0414: SubstanceLookAndFeel.stopThreads();
0415: }
0416: });
0417: }
0418: };
0419:
0420: if (!(parent instanceof JInternalFrame)) {
0421: currWindow
0422: .addWindowListener(substanceWindowListener);
0423: }
0424:
0425: // fix for defect 213 - maximizing frame under multiple
0426: // screens shouldn't always use insets of the primary
0427: // screen.
0428: substanceWindowComponentListener = new ComponentAdapter() {
0429: @Override
0430: public void componentMoved(ComponentEvent e) {
0431: this .processNewPosition();
0432: }
0433:
0434: @Override
0435: public void componentResized(ComponentEvent e) {
0436: this .processNewPosition();
0437: }
0438:
0439: protected void processNewPosition() {
0440: SwingUtilities.invokeLater(new Runnable() {
0441: public void run() {
0442: // currentRootPaneGC = null;
0443: if (!window.isShowing()
0444: || !window.isDisplayable()) {
0445: currentRootPaneGC = null;
0446: return;
0447: }
0448:
0449: GraphicsEnvironment ge = GraphicsEnvironment
0450: .getLocalGraphicsEnvironment();
0451: GraphicsDevice[] gds = ge
0452: .getScreenDevices();
0453: if (gds.length == 1)
0454: return;
0455: Point midLoc = new Point(
0456: window
0457: .getLocationOnScreen().x
0458: + window.getWidth()
0459: / 2,
0460: window
0461: .getLocationOnScreen().y
0462: + window
0463: .getHeight()
0464: / 2);
0465: // System.out.println("Loc : "
0466: // + window.getLocationOnScreen()
0467: // + ", width : "
0468: // + window.getWidth()
0469: // + ", mid : " + midLoc);
0470: int index = 0;
0471: for (GraphicsDevice gd : gds) {
0472: GraphicsConfiguration gc = gd
0473: .getDefaultConfiguration();
0474: Rectangle bounds = gc
0475: .getBounds();
0476: // System.out.println("Bounds : "
0477: // + bounds);
0478: if (bounds.contains(midLoc)) {
0479: if (gc != currentRootPaneGC) {
0480: currentRootPaneGC = gc;
0481: setMaximized();
0482: // System.out.println("Set");
0483: }
0484: break;
0485: }
0486: index++;
0487: }
0488: }
0489: });
0490: }
0491: };
0492: // fix for defect 225 - install the listener only on
0493: // JFrames.
0494: if (parent instanceof JFrame) {
0495: currWindow
0496: .addComponentListener(substanceWindowComponentListener);
0497: }
0498:
0499: SubstanceRootPaneUI.this .window = currWindow;
0500: }
0501: substanceCurrentWindow = currWindow;
0502: }
0503: };
0504: root.addHierarchyListener(this .substanceHierarchyListener);
0505: // System.out.println(root.hashCode() + ":"
0506: // + root.getHierarchyListeners().length);
0507:
0508: }
0509:
0510: /*
0511: * (non-Javadoc)
0512: *
0513: * @see javax.swing.plaf.basic.BasicRootPaneUI#uninstallListeners(javax.swing.JRootPane)
0514: */
0515: @Override
0516: protected void uninstallListeners(JRootPane root) {
0517: // fix for bug 116 - stopping threads when all frames are
0518: // not displayable
0519: if (this .window != null) {
0520: this .window
0521: .removeWindowListener(this .substanceWindowListener);
0522: this .substanceWindowListener = null;
0523: this .window
0524: .removeComponentListener(this .substanceWindowComponentListener);
0525: this .substanceWindowComponentListener = null;
0526: }
0527: root.removeHierarchyListener(this .substanceHierarchyListener);
0528: this .substanceHierarchyListener = null;
0529:
0530: super .uninstallListeners(root);
0531: }
0532:
0533: /**
0534: * Uninstalls the previously installed <code>LayoutManager</code>.
0535: *
0536: * @param root
0537: * Root pane.
0538: */
0539: private void uninstallLayout(JRootPane root) {
0540: if (this .savedOldLayout != null) {
0541: root.setLayout(this .savedOldLayout);
0542: this .savedOldLayout = null;
0543: }
0544: }
0545:
0546: /**
0547: * Installs the necessary state onto the JRootPane to render client
0548: * decorations. This is ONLY invoked if the <code>JRootPane</code> has a
0549: * decoration style other than <code>JRootPane.NONE</code>.
0550: *
0551: * @param root
0552: * Root pane.
0553: */
0554: private void installClientDecorations(JRootPane root) {
0555: this .installBorder(root);
0556:
0557: JComponent titlePane = this .createTitlePane(root);
0558:
0559: this .setTitlePane(root, titlePane);
0560: this .installWindowListeners(root, root.getParent());
0561: this .installLayout(root);
0562: if (this .window != null) {
0563: root.revalidate();
0564: root.repaint();
0565: }
0566: }
0567:
0568: /**
0569: * Uninstalls any state that <code>installClientDecorations</code> has
0570: * installed.
0571: * <p>
0572: * NOTE: This may be called if you haven't installed client decorations yet
0573: * (ie before <code>installClientDecorations</code> has been invoked).
0574: *
0575: * @param root
0576: * Root pane.
0577: */
0578: private void uninstallClientDecorations(JRootPane root) {
0579: this .uninstallBorder(root);
0580: this .uninstallWindowListeners(root);
0581: this .setTitlePane(root, null);
0582: this .uninstallLayout(root);
0583: // We have to revalidate/repaint root if the style is JRootPane.NONE
0584: // only. When we needs to call revalidate/repaint with other styles
0585: // the installClientDecorations is always called after this method
0586: // imediatly and it will cause the revalidate/repaint at the proper
0587: // time.
0588: int style = root.getWindowDecorationStyle();
0589: if (style == JRootPane.NONE) {
0590: root.repaint();
0591: root.revalidate();
0592: }
0593: // Reset the cursor, as we may have changed it to a resize cursor
0594: if (this .window != null) {
0595: this .window.setCursor(Cursor
0596: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
0597: }
0598: this .window = null;
0599: }
0600:
0601: /**
0602: * Returns the <code>JComponent</code> to render the window decoration
0603: * style.
0604: *
0605: * @param root
0606: * Root pane.
0607: * @return The title pane component.
0608: */
0609: private JComponent createTitlePane(JRootPane root) {
0610: return new SubstanceTitlePane(root, this );
0611: }
0612:
0613: /**
0614: * Returns a <code>MouseListener</code> that will be added to the
0615: * <code>Window</code> containing the <code>JRootPane</code>.
0616: *
0617: * @param root
0618: * Root pane.
0619: * @return Window mouse listener.
0620: */
0621: private MouseInputListener createWindowMouseInputListener(
0622: JRootPane root) {
0623: return new MouseInputHandler();
0624: }
0625:
0626: /**
0627: * Returns a <code>LayoutManager</code> that will be set on the
0628: * <code>JRootPane</code>.
0629: *
0630: * @return Layout manager.
0631: */
0632: private LayoutManager createLayoutManager() {
0633: return new SubstanceRootLayout();
0634: }
0635:
0636: /**
0637: * Sets the window title pane -- the JComponent used to provide a plaf a way
0638: * to override the native operating system's window title pane with one
0639: * whose look and feel are controlled by the plaf. The plaf creates and sets
0640: * this value; the default is null, implying a native operating system
0641: * window title pane.
0642: *
0643: * @param root
0644: * Root pane
0645: * @param titlePane
0646: * The <code>JComponent</code> to use for the window title
0647: * pane.
0648: */
0649: private void setTitlePane(JRootPane root, JComponent titlePane) {
0650: JLayeredPane layeredPane = root.getLayeredPane();
0651: JComponent oldTitlePane = this .getTitlePane();
0652:
0653: if (oldTitlePane != null) {
0654: // fix for defect 109 - memory leak on theme change
0655: if (oldTitlePane instanceof SubstanceTitlePane)
0656: ((SubstanceTitlePane) oldTitlePane).uninstall();
0657: // oldTitlePane.setVisible(false);
0658: layeredPane.remove(oldTitlePane);
0659: }
0660: if (titlePane != null) {
0661: layeredPane
0662: .add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
0663: titlePane.setVisible(true);
0664: }
0665: this .titlePane = titlePane;
0666: }
0667:
0668: /**
0669: * Sets maximized bounds according to the display screen insets.
0670: */
0671: public void setMaximized() {
0672: Component tla = this .root.getTopLevelAncestor();
0673: // fix for defect 213 - maximizing frame under multiple
0674: // screens shouldn't always use insets of the primary
0675: // screen.
0676: GraphicsConfiguration gc = (currentRootPaneGC != null) ? currentRootPaneGC
0677: : tla.getGraphicsConfiguration();
0678: Rectangle screenBounds = gc.getBounds();
0679: screenBounds.x = 0;
0680: screenBounds.y = 0;
0681: Insets screenInsets = Toolkit.getDefaultToolkit()
0682: .getScreenInsets(gc);
0683: Border rootBorder = this .root.getBorder();
0684: Insets insets = (rootBorder == null) ? new Insets(0, 0, 0, 0)
0685: : rootBorder.getBorderInsets(this .root);
0686: Rectangle maxBounds = new Rectangle(
0687: (screenBounds.x + screenInsets.left) - insets.left,
0688: (screenBounds.y + screenInsets.top) - insets.right,
0689: screenBounds.width
0690: - ((screenInsets.left + screenInsets.right)
0691: - insets.left - insets.right),
0692: screenBounds.height
0693: - ((screenInsets.top + screenInsets.bottom)
0694: - insets.top - insets.bottom));
0695: if (tla instanceof JFrame)
0696: ((JFrame) tla).setMaximizedBounds(maxBounds);
0697: if (MemoryAnalyzer.isRunning()) {
0698: MemoryAnalyzer.enqueueUsage("Frame set to bounds "
0699: + maxBounds);
0700: }
0701: }
0702:
0703: /**
0704: * Returns the <code>JComponent</code> rendering the title pane. If this
0705: * returns null, it implies there is no need to render window decorations.
0706: * This method is <b>for internal use only</b>.
0707: *
0708: * @see #setTitlePane
0709: * @return Title pane.
0710: */
0711: public JComponent getTitlePane() {
0712: return this .titlePane;
0713: }
0714:
0715: /**
0716: * Returns the <code>JRootPane</code> we're providing the look and feel
0717: * for.
0718: *
0719: * @return The associated root pane.
0720: */
0721: private JRootPane getRootPane() {
0722: return this .root;
0723: }
0724:
0725: /*
0726: * (non-Javadoc)
0727: *
0728: * @see javax.swing.plaf.basic.BasicRootPaneUI#propertyChange(java.beans.PropertyChangeEvent)
0729: */
0730: @Override
0731: public void propertyChange(PropertyChangeEvent e) {
0732: super .propertyChange(e);
0733:
0734: String propertyName = e.getPropertyName();
0735: if (propertyName == null) {
0736: return;
0737: }
0738:
0739: if (propertyName.equals("windowDecorationStyle")) {
0740: JRootPane root = (JRootPane) e.getSource();
0741: int style = root.getWindowDecorationStyle();
0742:
0743: this .uninstallClientDecorations(root);
0744: if (style != JRootPane.NONE) {
0745: this .installClientDecorations(root);
0746: }
0747: }
0748: if (propertyName.equals("ancestor")) {
0749: this .uninstallWindowListeners(this .root);
0750: if (((JRootPane) e.getSource()).getWindowDecorationStyle() != JRootPane.NONE) {
0751: this .installWindowListeners(this .root, this .root
0752: .getParent());
0753: }
0754: }
0755: if (propertyName.equals("background")) {
0756: SubstanceLookAndFeel.getTitlePaneComponent(window)
0757: .setBackground((Color) e.getNewValue());
0758: }
0759: return;
0760: }
0761:
0762: /**
0763: * A custom layout manager that is responsible for the layout of
0764: * layeredPane, glassPane, menuBar and titlePane, if one has been installed.
0765: */
0766: // NOTE: Ideally this would extends JRootPane.RootLayout, but that
0767: // would force this to be non-static.
0768: private static class SubstanceRootLayout implements LayoutManager2 {
0769: /**
0770: * Returns the amount of space the layout would like to have.
0771: *
0772: *
0773: * aram the Container for which this layout manager is being used
0774: *
0775: * @return a Dimension object containing the layout's preferred size
0776: */
0777: public Dimension preferredLayoutSize(Container parent) {
0778: Dimension cpd, mbd, tpd;
0779: int cpWidth = 0;
0780: int cpHeight = 0;
0781: int mbWidth = 0;
0782: int mbHeight = 0;
0783: int tpWidth = 0;
0784: // int tpHeight = 0;
0785: Insets i = parent.getInsets();
0786: JRootPane root = (JRootPane) parent;
0787:
0788: if (root.getContentPane() != null) {
0789: cpd = root.getContentPane().getPreferredSize();
0790: } else {
0791: cpd = root.getSize();
0792: }
0793: if (cpd != null) {
0794: cpWidth = cpd.width;
0795: cpHeight = cpd.height;
0796: }
0797:
0798: if (root.getJMenuBar() != null) {
0799: mbd = root.getJMenuBar().getPreferredSize();
0800: if (mbd != null) {
0801: mbWidth = mbd.width;
0802: mbHeight = mbd.height;
0803: }
0804: }
0805:
0806: if ((root.getWindowDecorationStyle() != JRootPane.NONE)
0807: && (root.getUI() instanceof SubstanceRootPaneUI)) {
0808: JComponent titlePane = ((SubstanceRootPaneUI) root
0809: .getUI()).getTitlePane();
0810: if (titlePane != null) {
0811: tpd = titlePane.getPreferredSize();
0812: if (tpd != null) {
0813: tpWidth = tpd.width;
0814: // tpHeight = tpd.height;
0815: }
0816: }
0817: }
0818:
0819: return new Dimension(Math.max(Math.max(cpWidth, mbWidth),
0820: tpWidth)
0821: + i.left + i.right, cpHeight + mbHeight + tpWidth
0822: + i.top + i.bottom);
0823: }
0824:
0825: /**
0826: * Returns the minimum amount of space the layout needs.
0827: *
0828: *
0829: * aram the Container for which this layout manager is being used
0830: *
0831: * @return a Dimension object containing the layout's minimum size
0832: */
0833: public Dimension minimumLayoutSize(Container parent) {
0834: Dimension cpd, mbd, tpd;
0835: int cpWidth = 0;
0836: int cpHeight = 0;
0837: int mbWidth = 0;
0838: int mbHeight = 0;
0839: int tpWidth = 0;
0840: // int tpHeight = 0;
0841: Insets i = parent.getInsets();
0842: JRootPane root = (JRootPane) parent;
0843:
0844: if (root.getContentPane() != null) {
0845: cpd = root.getContentPane().getMinimumSize();
0846: } else {
0847: cpd = root.getSize();
0848: }
0849: if (cpd != null) {
0850: cpWidth = cpd.width;
0851: cpHeight = cpd.height;
0852: }
0853:
0854: if (root.getJMenuBar() != null) {
0855: mbd = root.getJMenuBar().getMinimumSize();
0856: if (mbd != null) {
0857: mbWidth = mbd.width;
0858: mbHeight = mbd.height;
0859: }
0860: }
0861: if ((root.getWindowDecorationStyle() != JRootPane.NONE)
0862: && (root.getUI() instanceof SubstanceRootPaneUI)) {
0863: JComponent titlePane = ((SubstanceRootPaneUI) root
0864: .getUI()).getTitlePane();
0865: if (titlePane != null) {
0866: tpd = titlePane.getMinimumSize();
0867: if (tpd != null) {
0868: tpWidth = tpd.width;
0869: // tpHeight = tpd.height;
0870: }
0871: }
0872: }
0873:
0874: return new Dimension(Math.max(Math.max(cpWidth, mbWidth),
0875: tpWidth)
0876: + i.left + i.right, cpHeight + mbHeight + tpWidth
0877: + i.top + i.bottom);
0878: }
0879:
0880: /**
0881: * Returns the maximum amount of space the layout can use.
0882: *
0883: *
0884: * aram the Container for which this layout manager is being used
0885: *
0886: * @return a Dimension object containing the layout's maximum size
0887: */
0888: public Dimension maximumLayoutSize(Container target) {
0889: Dimension cpd, mbd, tpd;
0890: int cpWidth = Integer.MAX_VALUE;
0891: int cpHeight = Integer.MAX_VALUE;
0892: int mbWidth = Integer.MAX_VALUE;
0893: int mbHeight = Integer.MAX_VALUE;
0894: int tpWidth = Integer.MAX_VALUE;
0895: int tpHeight = Integer.MAX_VALUE;
0896: Insets i = target.getInsets();
0897: JRootPane root = (JRootPane) target;
0898:
0899: if (root.getContentPane() != null) {
0900: cpd = root.getContentPane().getMaximumSize();
0901: if (cpd != null) {
0902: cpWidth = cpd.width;
0903: cpHeight = cpd.height;
0904: }
0905: }
0906:
0907: if (root.getJMenuBar() != null) {
0908: mbd = root.getJMenuBar().getMaximumSize();
0909: if (mbd != null) {
0910: mbWidth = mbd.width;
0911: mbHeight = mbd.height;
0912: }
0913: }
0914:
0915: if ((root.getWindowDecorationStyle() != JRootPane.NONE)
0916: && (root.getUI() instanceof SubstanceRootPaneUI)) {
0917: JComponent titlePane = ((SubstanceRootPaneUI) root
0918: .getUI()).getTitlePane();
0919: if (titlePane != null) {
0920: tpd = titlePane.getMaximumSize();
0921: if (tpd != null) {
0922: tpWidth = tpd.width;
0923: tpHeight = tpd.height;
0924: }
0925: }
0926: }
0927:
0928: int maxHeight = Math.max(Math.max(cpHeight, mbHeight),
0929: tpHeight);
0930: // Only overflows if 3 real non-MAX_VALUE heights, sum to >
0931: // MAX_VALUE
0932: // Only will happen if sums to more than 2 billion units. Not
0933: // likely.
0934: if (maxHeight != Integer.MAX_VALUE) {
0935: maxHeight = cpHeight + mbHeight + tpHeight + i.top
0936: + i.bottom;
0937: }
0938:
0939: int maxWidth = Math
0940: .max(Math.max(cpWidth, mbWidth), tpWidth);
0941: // Similar overflow comment as above
0942: if (maxWidth != Integer.MAX_VALUE) {
0943: maxWidth += i.left + i.right;
0944: }
0945:
0946: return new Dimension(maxWidth, maxHeight);
0947: }
0948:
0949: /**
0950: * Instructs the layout manager to perform the layout for the specified
0951: * container.
0952: *
0953: *
0954: * aram the Container for which this layout manager is being used
0955: */
0956: public void layoutContainer(Container parent) {
0957: JRootPane root = (JRootPane) parent;
0958: Rectangle b = root.getBounds();
0959: Insets i = root.getInsets();
0960: int nextY = 0;
0961: int w = b.width - i.right - i.left;
0962: int h = b.height - i.top - i.bottom;
0963:
0964: if (root.getLayeredPane() != null) {
0965: root.getLayeredPane().setBounds(i.left, i.top, w, h);
0966: }
0967: if (root.getGlassPane() != null) {
0968: root.getGlassPane().setBounds(i.left, i.top, w, h);
0969: }
0970: // Note: This is laying out the children in the layeredPane,
0971: // technically, these are not our children.
0972: if ((root.getWindowDecorationStyle() != JRootPane.NONE)
0973: && (root.getUI() instanceof SubstanceRootPaneUI)) {
0974: JComponent titlePane = ((SubstanceRootPaneUI) root
0975: .getUI()).getTitlePane();
0976: if (titlePane != null) {
0977: Dimension tpd = titlePane.getPreferredSize();
0978: if (tpd != null) {
0979: int tpHeight = tpd.height;
0980: titlePane.setBounds(0, 0, w, tpHeight);
0981: nextY += tpHeight;
0982: }
0983: }
0984: }
0985: if (root.getJMenuBar() != null) {
0986: Dimension mbd = root.getJMenuBar().getPreferredSize();
0987: root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
0988: nextY += mbd.height;
0989: }
0990: if (root.getContentPane() != null) {
0991: // Dimension cpd = root.getContentPane().getPreferredSize();
0992: root.getContentPane().setBounds(0, nextY, w,
0993: h < nextY ? 0 : h - nextY);
0994: }
0995: }
0996:
0997: public void addLayoutComponent(String name, Component comp) {
0998: }
0999:
1000: public void removeLayoutComponent(Component comp) {
1001: }
1002:
1003: public void addLayoutComponent(Component comp,
1004: Object constraints) {
1005: }
1006:
1007: public float getLayoutAlignmentX(Container target) {
1008: return 0.0f;
1009: }
1010:
1011: public float getLayoutAlignmentY(Container target) {
1012: return 0.0f;
1013: }
1014:
1015: public void invalidateLayout(Container target) {
1016: }
1017: }
1018:
1019: /**
1020: * Maps from positions to cursor type. Refer to calculateCorner and
1021: * calculatePosition for details of this.
1022: */
1023: private static final int[] cursorMapping = new int[] {
1024: Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR,
1025: Cursor.N_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
1026: Cursor.NE_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, 0, 0, 0,
1027: Cursor.NE_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, 0, 0, 0,
1028: Cursor.E_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, 0, 0, 0,
1029: Cursor.SE_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR,
1030: Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
1031: Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR };
1032:
1033: /**
1034: * MouseInputHandler is responsible for handling resize/moving of the
1035: * Window. It sets the cursor directly on the Window when then mouse moves
1036: * over a hot spot.
1037: */
1038: private class MouseInputHandler implements MouseInputListener {
1039: /**
1040: * Set to true if the drag operation is moving the window.
1041: */
1042: private boolean isMovingWindow;
1043:
1044: /**
1045: * Used to determine the corner the resize is occuring from.
1046: */
1047: private int dragCursor;
1048:
1049: /**
1050: * X location the mouse went down on for a drag operation.
1051: */
1052: private int dragOffsetX;
1053:
1054: /**
1055: * Y location the mouse went down on for a drag operation.
1056: */
1057: private int dragOffsetY;
1058:
1059: /**
1060: * Width of the window when the drag started.
1061: */
1062: private int dragWidth;
1063:
1064: /**
1065: * Height of the window when the drag started.
1066: */
1067: private int dragHeight;
1068:
1069: /**
1070: * PrivilegedExceptionAction needed by mouseDragged method to obtain new
1071: * location of window on screen during the drag.
1072: */
1073: private final PrivilegedExceptionAction getLocationAction = new PrivilegedExceptionAction() {
1074: public Object run() throws HeadlessException {
1075: return MouseInfo.getPointerInfo().getLocation();
1076: }
1077: };
1078:
1079: public void mousePressed(MouseEvent ev) {
1080: JRootPane rootPane = SubstanceRootPaneUI.this .getRootPane();
1081:
1082: if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
1083: return;
1084: }
1085: Point dragWindowOffset = ev.getPoint();
1086: Window w = (Window) ev.getSource();
1087: if (w != null) {
1088: w.toFront();
1089: }
1090: Point convertedDragWindowOffset = SwingUtilities
1091: .convertPoint(w, dragWindowOffset,
1092: SubstanceRootPaneUI.this .getTitlePane());
1093:
1094: Frame f = null;
1095: Dialog d = null;
1096:
1097: if (w instanceof Frame) {
1098: f = (Frame) w;
1099: } else if (w instanceof Dialog) {
1100: d = (Dialog) w;
1101: }
1102:
1103: int frameState = (f != null) ? f.getExtendedState() : 0;
1104:
1105: if ((SubstanceRootPaneUI.this .getTitlePane() != null)
1106: && SubstanceRootPaneUI.this .getTitlePane()
1107: .contains(convertedDragWindowOffset)) {
1108: if ((((f != null) && ((frameState & Frame.MAXIMIZED_BOTH) == 0)) || (d != null))
1109: && (dragWindowOffset.y >= SubstanceRootPaneUI.BORDER_DRAG_THICKNESS)
1110: && (dragWindowOffset.x >= SubstanceRootPaneUI.BORDER_DRAG_THICKNESS)
1111: && (dragWindowOffset.x < w.getWidth()
1112: - SubstanceRootPaneUI.BORDER_DRAG_THICKNESS)) {
1113: this .isMovingWindow = true;
1114: this .dragOffsetX = dragWindowOffset.x;
1115: this .dragOffsetY = dragWindowOffset.y;
1116: }
1117: } else if (((f != null) && f.isResizable() && ((frameState & Frame.MAXIMIZED_BOTH) == 0))
1118: || ((d != null) && d.isResizable())) {
1119: this .dragOffsetX = dragWindowOffset.x;
1120: this .dragOffsetY = dragWindowOffset.y;
1121: this .dragWidth = w.getWidth();
1122: this .dragHeight = w.getHeight();
1123: this .dragCursor = this .getCursor(this .calculateCorner(
1124: w, dragWindowOffset.x, dragWindowOffset.y));
1125: }
1126: }
1127:
1128: public void mouseReleased(MouseEvent ev) {
1129: if ((this .dragCursor != 0)
1130: && (SubstanceRootPaneUI.this .window != null)
1131: && !SubstanceRootPaneUI.this .window.isValid()) {
1132: // Some Window systems validate as you resize, others won't,
1133: // thus the check for validity before repainting.
1134: SubstanceRootPaneUI.this .window.validate();
1135: SubstanceRootPaneUI.this .getRootPane().repaint();
1136: }
1137: this .isMovingWindow = false;
1138: this .dragCursor = 0;
1139: }
1140:
1141: public void mouseMoved(MouseEvent ev) {
1142: JRootPane root = SubstanceRootPaneUI.this .getRootPane();
1143:
1144: if (root.getWindowDecorationStyle() == JRootPane.NONE) {
1145: return;
1146: }
1147:
1148: Window w = (Window) ev.getSource();
1149:
1150: Frame f = null;
1151: Dialog d = null;
1152:
1153: if (w instanceof Frame) {
1154: f = (Frame) w;
1155: } else if (w instanceof Dialog) {
1156: d = (Dialog) w;
1157: }
1158:
1159: // Update the cursor
1160: int cursor = this .getCursor(this .calculateCorner(w, ev
1161: .getX(), ev.getY()));
1162:
1163: if ((cursor != 0)
1164: && (((f != null) && (f.isResizable() && ((f
1165: .getExtendedState() & Frame.MAXIMIZED_BOTH) == 0))) || ((d != null) && d
1166: .isResizable()))) {
1167: w.setCursor(Cursor.getPredefinedCursor(cursor));
1168: } else {
1169: w.setCursor(SubstanceRootPaneUI.this .lastCursor);
1170: }
1171: }
1172:
1173: /**
1174: * Adjusts the bounds.
1175: *
1176: * @param bounds
1177: * Original bounds.
1178: * @param min
1179: * Minimum dimension.
1180: * @param deltaX
1181: * Delta X.
1182: * @param deltaY
1183: * Delta Y.
1184: * @param deltaWidth
1185: * Delta width.
1186: * @param deltaHeight
1187: * Delta height.
1188: */
1189: private void adjust(Rectangle bounds, Dimension min,
1190: int deltaX, int deltaY, int deltaWidth, int deltaHeight) {
1191: bounds.x += deltaX;
1192: bounds.y += deltaY;
1193: bounds.width += deltaWidth;
1194: bounds.height += deltaHeight;
1195: if (min != null) {
1196: if (bounds.width < min.width) {
1197: int correction = min.width - bounds.width;
1198: if (deltaX != 0) {
1199: bounds.x -= correction;
1200: }
1201: bounds.width = min.width;
1202: }
1203: if (bounds.height < min.height) {
1204: int correction = min.height - bounds.height;
1205: if (deltaY != 0) {
1206: bounds.y -= correction;
1207: }
1208: bounds.height = min.height;
1209: }
1210: }
1211: }
1212:
1213: @SuppressWarnings("unchecked")
1214: public void mouseDragged(MouseEvent ev) {
1215: Window w = (Window) ev.getSource();
1216: Point pt = ev.getPoint();
1217:
1218: if (this .isMovingWindow) {
1219: Point windowPt;
1220: try {
1221: windowPt = (Point) AccessController
1222: .doPrivileged(this .getLocationAction);
1223: windowPt.x = windowPt.x - this .dragOffsetX;
1224: windowPt.y = windowPt.y - this .dragOffsetY;
1225: w.setLocation(windowPt);
1226: } catch (PrivilegedActionException e) {
1227: }
1228: } else if (this .dragCursor != 0) {
1229: Rectangle r = w.getBounds();
1230: Rectangle startBounds = new Rectangle(r);
1231: Dimension min = w.getMinimumSize();
1232:
1233: switch (this .dragCursor) {
1234: case Cursor.E_RESIZE_CURSOR:
1235: this .adjust(r, min, 0, 0, pt.x
1236: + (this .dragWidth - this .dragOffsetX)
1237: - r.width, 0);
1238: break;
1239: case Cursor.S_RESIZE_CURSOR:
1240: this .adjust(r, min, 0, 0, 0, pt.y
1241: + (this .dragHeight - this .dragOffsetY)
1242: - r.height);
1243: break;
1244: case Cursor.N_RESIZE_CURSOR:
1245: this .adjust(r, min, 0, pt.y - this .dragOffsetY, 0,
1246: -(pt.y - this .dragOffsetY));
1247: break;
1248: case Cursor.W_RESIZE_CURSOR:
1249: this .adjust(r, min, pt.x - this .dragOffsetX, 0,
1250: -(pt.x - this .dragOffsetX), 0);
1251: break;
1252: case Cursor.NE_RESIZE_CURSOR:
1253: this .adjust(r, min, 0, pt.y - this .dragOffsetY,
1254: pt.x + (this .dragWidth - this .dragOffsetX)
1255: - r.width,
1256: -(pt.y - this .dragOffsetY));
1257: break;
1258: case Cursor.SE_RESIZE_CURSOR:
1259: this .adjust(r, min, 0, 0, pt.x
1260: + (this .dragWidth - this .dragOffsetX)
1261: - r.width, pt.y
1262: + (this .dragHeight - this .dragOffsetY)
1263: - r.height);
1264: break;
1265: case Cursor.NW_RESIZE_CURSOR:
1266: this .adjust(r, min, pt.x - this .dragOffsetX, pt.y
1267: - this .dragOffsetY,
1268: -(pt.x - this .dragOffsetX),
1269: -(pt.y - this .dragOffsetY));
1270: break;
1271: case Cursor.SW_RESIZE_CURSOR:
1272: this .adjust(r, min, pt.x - this .dragOffsetX, 0,
1273: -(pt.x - this .dragOffsetX),
1274: pt.y + (this .dragHeight - this .dragOffsetY)
1275: - r.height);
1276: break;
1277: default:
1278: break;
1279: }
1280: if (!r.equals(startBounds)) {
1281: w.setBounds(r);
1282: // Defer repaint/validate on mouseReleased unless dynamic
1283: // layout is active.
1284: if (Toolkit.getDefaultToolkit()
1285: .isDynamicLayoutActive()) {
1286: w.validate();
1287: SubstanceRootPaneUI.this .getRootPane()
1288: .repaint();
1289: }
1290: }
1291: }
1292: }
1293:
1294: public void mouseEntered(MouseEvent ev) {
1295: Window w = (Window) ev.getSource();
1296: // fix for defect 107
1297: SubstanceRootPaneUI.this .lastCursor = w.getCursor();
1298: // Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
1299: this .mouseMoved(ev);
1300: }
1301:
1302: public void mouseExited(MouseEvent ev) {
1303: Window w = (Window) ev.getSource();
1304: w.setCursor(SubstanceRootPaneUI.this .lastCursor);
1305: }
1306:
1307: public void mouseClicked(MouseEvent ev) {
1308: Window w = (Window) ev.getSource();
1309: Frame f = null;
1310:
1311: if (w instanceof Frame) {
1312: f = (Frame) w;
1313: } else {
1314: return;
1315: }
1316:
1317: Point convertedPoint = SwingUtilities.convertPoint(w, ev
1318: .getPoint(), SubstanceRootPaneUI.this
1319: .getTitlePane());
1320:
1321: int state = f.getExtendedState();
1322: if ((SubstanceRootPaneUI.this .getTitlePane() != null)
1323: && SubstanceRootPaneUI.this .getTitlePane()
1324: .contains(convertedPoint)) {
1325: if (((ev.getClickCount() % 2) == 0)
1326: && ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
1327: if (f.isResizable()) {
1328: if ((state & Frame.MAXIMIZED_BOTH) != 0) {
1329: setMaximized();
1330: f.setExtendedState(state
1331: & ~Frame.MAXIMIZED_BOTH);
1332: } else {
1333: setMaximized();
1334: f.setExtendedState(state
1335: | Frame.MAXIMIZED_BOTH);
1336: }
1337: return;
1338: }
1339: }
1340: }
1341: }
1342:
1343: /**
1344: * Returns the corner that contains the point <code>x</code>,
1345: * <code>y</code>, or -1 if the position doesn't match a corner.
1346: *
1347: * @param w
1348: * Window.
1349: * @param x
1350: * X coordinate.
1351: * @param y
1352: * Y coordinate.
1353: * @return Corner that contains the specified point.
1354: */
1355: private int calculateCorner(Window w, int x, int y) {
1356: Insets insets = w.getInsets();
1357: int xPosition = this .calculatePosition(x - insets.left, w
1358: .getWidth()
1359: - insets.left - insets.right);
1360: int yPosition = this .calculatePosition(y - insets.top, w
1361: .getHeight()
1362: - insets.top - insets.bottom);
1363:
1364: if ((xPosition == -1) || (yPosition == -1)) {
1365: return -1;
1366: }
1367: return yPosition * 5 + xPosition;
1368: }
1369:
1370: /**
1371: * Returns the Cursor to render for the specified corner. This returns 0
1372: * if the corner doesn't map to a valid Cursor
1373: *
1374: * @param corner
1375: * Corner
1376: * @return Cursor to render for the specified corner.
1377: */
1378: private int getCursor(int corner) {
1379: if (corner == -1) {
1380: return 0;
1381: }
1382: return SubstanceRootPaneUI.cursorMapping[corner];
1383: }
1384:
1385: /**
1386: * Returns an integer indicating the position of <code>spot</code> in
1387: * <code>width</code>. The return value will be: 0 if <
1388: * BORDER_DRAG_THICKNESS 1 if < CORNER_DRAG_WIDTH 2 if >=
1389: * CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS 3 if >= width -
1390: * CORNER_DRAG_WIDTH 4 if >= width - BORDER_DRAG_THICKNESS 5 otherwise
1391: *
1392: * @param spot
1393: * Spot.
1394: * @param width
1395: * Width.
1396: * @return The position of spot in width.
1397: */
1398: private int calculatePosition(int spot, int width) {
1399: if (spot < SubstanceRootPaneUI.BORDER_DRAG_THICKNESS) {
1400: return 0;
1401: }
1402: if (spot < SubstanceRootPaneUI.CORNER_DRAG_WIDTH) {
1403: return 1;
1404: }
1405: if (spot >= (width - SubstanceRootPaneUI.BORDER_DRAG_THICKNESS)) {
1406: return 4;
1407: }
1408: if (spot >= (width - SubstanceRootPaneUI.CORNER_DRAG_WIDTH)) {
1409: return 3;
1410: }
1411: return 2;
1412: }
1413: }
1414:
1415: /**
1416: * Mouse handler on the title pane.
1417: *
1418: * @author Kirill Grouchnikov
1419: */
1420: private class TitleMouseInputHandler extends MouseInputAdapter {
1421:
1422: /**
1423: * Pointer location when the mouse was pressed for a drag relative to
1424: * the upper-lefthand corner of the window.
1425: */
1426: private Point dragOffset = new Point(0, 0);
1427:
1428: @Override
1429: public void mousePressed(MouseEvent ev) {
1430: JRootPane rootPane = SubstanceRootPaneUI.this .getRootPane();
1431:
1432: if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
1433: return;
1434: }
1435:
1436: Point dragWindowOffset = ev.getPoint();
1437: Component source = (Component) ev.getSource();
1438:
1439: Point convertedDragWindowOffset = SwingUtilities
1440: .convertPoint(source, dragWindowOffset,
1441: getTitlePane());
1442:
1443: dragWindowOffset = SwingUtilities.convertPoint(source,
1444: dragWindowOffset, SubstanceRootPaneUI.this .window);
1445:
1446: if (getTitlePane() != null
1447: && getTitlePane().contains(
1448: convertedDragWindowOffset)) {
1449: if (SubstanceRootPaneUI.this .window != null) {
1450: SubstanceRootPaneUI.this .window.toFront();
1451: dragOffset = dragWindowOffset;
1452: }
1453: }
1454: }
1455:
1456: @Override
1457: public void mouseDragged(MouseEvent ev) {
1458: Component source = (Component) ev.getSource();
1459:
1460: // Point pt = SwingUtilities.convertPoint(source, ev.getPoint(),
1461: // SubstanceRootPaneUI.this.window);
1462:
1463: Point eventLocationOnScreen = null;
1464: if (getLocationOnScreenMethod != null) {
1465: try {
1466: // fix for issue 198 - only available under JDK 6.0+
1467: eventLocationOnScreen = (Point) getLocationOnScreenMethod
1468: .invoke(ev, new Object[0]);
1469: } catch (Exception exc) {
1470: eventLocationOnScreen = null;
1471: }
1472: }
1473: if (eventLocationOnScreen == null) {
1474: eventLocationOnScreen = new Point(ev.getX()
1475: + source.getLocationOnScreen().x, ev.getY()
1476: + source.getLocationOnScreen().y);
1477: }
1478: // Fix for issue 192 - disable dragging maximized frame.
1479: if (SubstanceRootPaneUI.this .window instanceof Frame) {
1480: Frame f = (Frame) SubstanceRootPaneUI.this .window;
1481: int frameState = (f != null) ? f.getExtendedState() : 0;
1482: if ((f != null)
1483: && ((frameState & Frame.MAXIMIZED_BOTH) == 0)) {
1484: SubstanceRootPaneUI.this .window.setLocation(
1485: eventLocationOnScreen.x - dragOffset.x,
1486: eventLocationOnScreen.y - dragOffset.y);
1487: }
1488: } else {
1489: // fix for issue 193 - allow dragging decorated dialogs.
1490: SubstanceRootPaneUI.this .window.setLocation(
1491: eventLocationOnScreen.x - dragOffset.x,
1492: eventLocationOnScreen.y - dragOffset.y);
1493: }
1494:
1495: }
1496:
1497: @Override
1498: public void mouseClicked(MouseEvent ev) {
1499: Frame f = null;
1500:
1501: if (SubstanceRootPaneUI.this .window instanceof Frame) {
1502: f = (Frame) SubstanceRootPaneUI.this .window;
1503: } else {
1504: return;
1505: }
1506:
1507: Point convertedPoint = SwingUtilities.convertPoint(
1508: SubstanceRootPaneUI.this .window, ev.getPoint(),
1509: SubstanceRootPaneUI.this .getTitlePane());
1510:
1511: int state = f.getExtendedState();
1512: if ((SubstanceRootPaneUI.this .getTitlePane() != null)
1513: && SubstanceRootPaneUI.this .getTitlePane()
1514: .contains(convertedPoint)) {
1515: if (((ev.getClickCount() % 2) == 0)
1516: && ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
1517: if (f.isResizable()) {
1518: if ((state & Frame.MAXIMIZED_BOTH) != 0) {
1519: setMaximized();
1520: f.setExtendedState(state
1521: & ~Frame.MAXIMIZED_BOTH);
1522: } else {
1523: setMaximized();
1524: f.setExtendedState(state
1525: | Frame.MAXIMIZED_BOTH);
1526: }
1527: return;
1528: }
1529: }
1530: }
1531: }
1532: }
1533:
1534: /**
1535: * Makes the heap status panel appear / disappear permanently on the
1536: * associated title pane and removes the corresponding check box menu items
1537: * from the system menu.
1538: *
1539: * @param isVisible
1540: * if <code>true</code>, the heap status panel will be
1541: * permanently shown, if <code>false</code>, the heap status
1542: * panel will be permanently hidden.
1543: */
1544: public void setHeapStatusPanePermanentVisibility(boolean isVisible) {
1545: if (this .titlePane instanceof SubstanceTitlePane) {
1546: ((SubstanceTitlePane) this.titlePane)
1547: .setHeapStatusPanePermanentVisibility(isVisible);
1548: }
1549: }
1550: }
|