< prev index next >

src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRootPaneUI.java

Print this page




  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 javax.swing.plaf.metal;
  27 
  28 import java.awt.event.*;
  29 import java.beans.PropertyChangeEvent;
  30 import javax.swing.*;
  31 import javax.swing.event.*;
  32 import javax.swing.plaf.*;
  33 import javax.swing.plaf.basic.*;
  34 import java.awt.*;
  35 
  36 /**
  37  * Provides the metal look and feel implementation of <code>RootPaneUI</code>.
  38  * <p>
  39  * <code>MetalRootPaneUI</code> provides support for the
  40  * <code>windowDecorationStyle</code> property of <code>JRootPane</code>.
  41  * <code>MetalRootPaneUI</code> does this by way of installing a custom
  42  * <code>LayoutManager</code>, a private <code>Component</code> to render
  43  * the appropriate widgets, and a private <code>Border</code>. The
  44  * <code>LayoutManager</code> is always installed, regardless of the value of
  45  * the <code>windowDecorationStyle</code> property, but the
  46  * <code>Border</code> and <code>Component</code> are only installed/added if
  47  * the <code>windowDecorationStyle</code> is other than
  48  * <code>JRootPane.NONE</code>.
  49  * <p>
  50  * <strong>Warning:</strong>
  51  * Serialized objects of this class will not be compatible with
  52  * future Swing releases. The current serialization support is
  53  * appropriate for short term storage or RMI between applications running
  54  * the same version of Swing.  As of 1.4, support for long term storage
  55  * of all JavaBeans&trade;
  56  * has been added to the <code>java.beans</code> package.
  57  * Please see {@link java.beans.XMLEncoder}.
  58  *
  59  * @author Terry Kellerman
  60  * @since 1.4
  61  */
  62 @SuppressWarnings("serial") // Same-version serialization only
  63 public class MetalRootPaneUI extends BasicRootPaneUI
  64 {
  65     /**
  66      * Keys to lookup borders in defaults table.
  67      */
  68     private static final String[] borderKeys = new String[] {
  69         null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
  70         "RootPane.informationDialogBorder",
  71         "RootPane.errorDialogBorder", "RootPane.colorChooserDialogBorder",
  72         "RootPane.fileChooserDialogBorder", "RootPane.questionDialogBorder",
  73         "RootPane.warningDialogBorder"
  74     };
  75     /**
  76      * The amount of space (in pixels) that the cursor is changed on.
  77      */
  78     private static final int CORNER_DRAG_WIDTH = 16;
  79 
  80     /**
  81      * Region from edges that dragging is active from.
  82      */
  83     private static final int BORDER_DRAG_THICKNESS = 5;
  84 
  85     /**
  86      * Window the <code>JRootPane</code> is in.
  87      */
  88     private Window window;
  89 
  90     /**
  91      * <code>JComponent</code> providing window decorations. This will be
  92      * null if not providing window decorations.
  93      */
  94     private JComponent titlePane;
  95 
  96     /**
  97      * <code>MouseInputListener</code> that is added to the parent
  98      * <code>Window</code> the <code>JRootPane</code> is contained in.
  99      */
 100     private MouseInputListener mouseInputListener;
 101 
 102     /**
 103      * The <code>LayoutManager</code> that is set on the
 104      * <code>JRootPane</code>.
 105      */
 106     private LayoutManager layoutManager;
 107 
 108     /**
 109      * <code>LayoutManager</code> of the <code>JRootPane</code> before we
 110      * replaced it.
 111      */
 112     private LayoutManager savedOldLayout;
 113 
 114     /**
 115      * <code>JRootPane</code> providing the look and feel for.
 116      */
 117     private JRootPane root;
 118 
 119     /**
 120      * <code>Cursor</code> used to track the cursor set by the user.
 121      * This is initially <code>Cursor.DEFAULT_CURSOR</code>.
 122      */
 123     private Cursor lastCursor =
 124             Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
 125 
 126     /**
 127      * Creates a UI for a <code>JRootPane</code>.
 128      *
 129      * @param c the JRootPane the RootPaneUI will be created for
 130      * @return the RootPaneUI implementation for the passed in JRootPane
 131      */
 132     public static ComponentUI createUI(JComponent c) {
 133         return new MetalRootPaneUI();
 134     }
 135 
 136     /**
 137      * Invokes supers implementation of <code>installUI</code> to install
 138      * the necessary state onto the passed in <code>JRootPane</code>
 139      * to render the metal look and feel implementation of
 140      * <code>RootPaneUI</code>. If
 141      * the <code>windowDecorationStyle</code> property of the
 142      * <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
 143      * this will add a custom <code>Component</code> to render the widgets to
 144      * <code>JRootPane</code>, as well as installing a custom
 145      * <code>Border</code> and <code>LayoutManager</code> on the
 146      * <code>JRootPane</code>.
 147      *
 148      * @param c the JRootPane to install state onto
 149      */
 150     public void installUI(JComponent c) {
 151         super.installUI(c);
 152         root = (JRootPane)c;
 153         int style = root.getWindowDecorationStyle();
 154         if (style != JRootPane.NONE) {
 155             installClientDecorations(root);
 156         }
 157     }
 158 
 159 
 160     /**
 161      * Invokes supers implementation to uninstall any of its state. This will
 162      * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
 163      * If a <code>Component</code> has been added to the <code>JRootPane</code>
 164      * to render the window decoration style, this method will remove it.
 165      * Similarly, this will revert the Border and LayoutManager of the
 166      * <code>JRootPane</code> to what it was before <code>installUI</code>
 167      * was invoked.
 168      *
 169      * @param c the JRootPane to uninstall state from
 170      */
 171     public void uninstallUI(JComponent c) {
 172         super.uninstallUI(c);
 173         uninstallClientDecorations(root);
 174 
 175         layoutManager = null;
 176         mouseInputListener = null;
 177         root = null;
 178     }
 179 
 180     /**
 181      * Installs the appropriate <code>Border</code> onto the
 182      * <code>JRootPane</code>.
 183      */
 184     void installBorder(JRootPane root) {
 185         int style = root.getWindowDecorationStyle();
 186 
 187         if (style == JRootPane.NONE) {
 188             LookAndFeel.uninstallBorder(root);
 189         }
 190         else {
 191             LookAndFeel.installBorder(root, borderKeys[style]);
 192         }
 193     }
 194 
 195     /**
 196      * Removes any border that may have been installed.
 197      */
 198     private void uninstallBorder(JRootPane root) {
 199         LookAndFeel.uninstallBorder(root);
 200     }
 201 
 202     /**
 203      * Installs the necessary Listeners on the parent <code>Window</code>,
 204      * if there is one.
 205      * <p>
 206      * This takes the parent so that cleanup can be done from
 207      * <code>removeNotify</code>, at which point the parent hasn't been
 208      * reset yet.
 209      *
 210      * @param parent The parent of the JRootPane
 211      */
 212     private void installWindowListeners(JRootPane root, Component parent) {
 213         if (parent instanceof Window) {
 214             window = (Window)parent;
 215         }
 216         else {
 217             window = SwingUtilities.getWindowAncestor(parent);
 218         }
 219         if (window != null) {
 220             if (mouseInputListener == null) {
 221                 mouseInputListener = createWindowMouseInputListener(root);
 222             }
 223             window.addMouseListener(mouseInputListener);
 224             window.addMouseMotionListener(mouseInputListener);
 225         }
 226     }
 227 
 228     /**
 229      * Uninstalls the necessary Listeners on the <code>Window</code> the
 230      * Listeners were last installed on.
 231      */
 232     private void uninstallWindowListeners(JRootPane root) {
 233         if (window != null) {
 234             window.removeMouseListener(mouseInputListener);
 235             window.removeMouseMotionListener(mouseInputListener);
 236         }
 237     }
 238 
 239     /**
 240      * Installs the appropriate LayoutManager on the <code>JRootPane</code>
 241      * to render the window decorations.
 242      */
 243     private void installLayout(JRootPane root) {
 244         if (layoutManager == null) {
 245             layoutManager = createLayoutManager();
 246         }
 247         savedOldLayout = root.getLayout();
 248         root.setLayout(layoutManager);
 249     }
 250 
 251     /**
 252      * Uninstalls the previously installed <code>LayoutManager</code>.
 253      */
 254     private void uninstallLayout(JRootPane root) {
 255         if (savedOldLayout != null) {
 256             root.setLayout(savedOldLayout);
 257             savedOldLayout = null;
 258         }
 259     }
 260 
 261     /**
 262      * Installs the necessary state onto the JRootPane to render client
 263      * decorations. This is ONLY invoked if the <code>JRootPane</code>
 264      * has a decoration style other than <code>JRootPane.NONE</code>.
 265      */
 266     private void installClientDecorations(JRootPane root) {
 267         installBorder(root);
 268 
 269         JComponent titlePane = createTitlePane(root);
 270 
 271         setTitlePane(root, titlePane);
 272         installWindowListeners(root, root.getParent());
 273         installLayout(root);
 274         if (window != null) {
 275             root.revalidate();
 276             root.repaint();
 277         }
 278     }
 279 
 280     /**
 281      * Uninstalls any state that <code>installClientDecorations</code> has
 282      * installed.
 283      * <p>
 284      * NOTE: This may be called if you haven't installed client decorations
 285      * yet (ie before <code>installClientDecorations</code> has been invoked).
 286      */
 287     private void uninstallClientDecorations(JRootPane root) {
 288         uninstallBorder(root);
 289         uninstallWindowListeners(root);
 290         setTitlePane(root, null);
 291         uninstallLayout(root);
 292         // We have to revalidate/repaint root if the style is JRootPane.NONE
 293         // only. When we needs to call revalidate/repaint with other styles
 294         // the installClientDecorations is always called after this method
 295         // imediatly and it will cause the revalidate/repaint at the proper
 296         // time.
 297         int style = root.getWindowDecorationStyle();
 298         if (style == JRootPane.NONE) {
 299             root.repaint();
 300             root.revalidate();
 301         }
 302         // Reset the cursor, as we may have changed it to a resize cursor
 303         if (window != null) {
 304             window.setCursor(Cursor.getPredefinedCursor
 305                              (Cursor.DEFAULT_CURSOR));
 306         }
 307         window = null;
 308     }
 309 
 310     /**
 311      * Returns the <code>JComponent</code> to render the window decoration
 312      * style.
 313      */
 314     private JComponent createTitlePane(JRootPane root) {
 315         return new MetalTitlePane(root, this);
 316     }
 317 
 318     /**
 319      * Returns a <code>MouseListener</code> that will be added to the
 320      * <code>Window</code> containing the <code>JRootPane</code>.
 321      */
 322     private MouseInputListener createWindowMouseInputListener(JRootPane root) {
 323         return new MouseInputHandler();
 324     }
 325 
 326     /**
 327      * Returns a <code>LayoutManager</code> that will be set on the
 328      * <code>JRootPane</code>.
 329      */
 330     private LayoutManager createLayoutManager() {
 331         return new MetalRootLayout();
 332     }
 333 
 334     /**
 335      * Sets the window title pane -- the JComponent used to provide a plaf a
 336      * way to override the native operating system's window title pane with
 337      * one whose look and feel are controlled by the plaf.  The plaf creates
 338      * and sets this value; the default is null, implying a native operating
 339      * system window title pane.
 340      *
 341      * @param content the <code>JComponent</code> to use for the window title pane.
 342      */
 343     private void setTitlePane(JRootPane root, JComponent titlePane) {
 344         JLayeredPane layeredPane = root.getLayeredPane();
 345         JComponent oldTitlePane = getTitlePane();
 346 
 347         if (oldTitlePane != null) {
 348             oldTitlePane.setVisible(false);
 349             layeredPane.remove(oldTitlePane);
 350         }
 351         if (titlePane != null) {
 352             layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
 353             titlePane.setVisible(true);
 354         }
 355         this.titlePane = titlePane;
 356     }
 357 
 358     /**
 359      * Returns the <code>JComponent</code> rendering the title pane. If this
 360      * returns null, it implies there is no need to render window decorations.
 361      *
 362      * @return the current window title pane, or null
 363      * @see #setTitlePane
 364      */
 365     private JComponent getTitlePane() {
 366         return titlePane;
 367     }
 368 
 369     /**
 370      * Returns the <code>JRootPane</code> we're providing the look and
 371      * feel for.
 372      */
 373     private JRootPane getRootPane() {
 374         return root;
 375     }
 376 
 377     /**
 378      * Invoked when a property changes. <code>MetalRootPaneUI</code> is
 379      * primarily interested in events originating from the
 380      * <code>JRootPane</code> it has been installed on identifying the
 381      * property <code>windowDecorationStyle</code>. If the
 382      * <code>windowDecorationStyle</code> has changed to a value other
 383      * than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
 384      * to the <code>JRootPane</code> to render the window decorations, as well
 385      * as installing a <code>Border</code> on the <code>JRootPane</code>.
 386      * On the other hand, if the <code>windowDecorationStyle</code> has
 387      * changed to <code>JRootPane.NONE</code>, this will remove the
 388      * <code>Component</code> that has been added to the <code>JRootPane</code>
 389      * as well resetting the Border to what it was before
 390      * <code>installUI</code> was invoked.
 391      *
 392      * @param e A PropertyChangeEvent object describing the event source
 393      *          and the property that has changed.
 394      */
 395     public void propertyChange(PropertyChangeEvent e) {
 396         super.propertyChange(e);
 397 
 398         String propertyName = e.getPropertyName();
 399         if(propertyName == null) {
 400             return;
 401         }
 402 
 403         if(propertyName.equals("windowDecorationStyle")) {
 404             JRootPane root = (JRootPane) e.getSource();
 405             int style = root.getWindowDecorationStyle();
 406 
 407             // This is potentially more than needs to be done,
 408             // but it rarely happens and makes the install/uninstall process
 409             // simpler. MetalTitlePane also assumes it will be recreated if
 410             // the decoration style changes.


 913 
 914             int state = f.getExtendedState();
 915             if (getTitlePane() != null &&
 916                     getTitlePane().contains(convertedPoint)) {
 917                 if ((ev.getClickCount() % 2) == 0 &&
 918                         ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
 919                     if (f.isResizable()) {
 920                         if ((state & Frame.MAXIMIZED_BOTH) != 0) {
 921                             f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
 922                         }
 923                         else {
 924                             f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
 925                         }
 926                         return;
 927                     }
 928                 }
 929             }
 930         }
 931 
 932         /**
 933          * Returns the corner that contains the point <code>x</code>,
 934          * <code>y</code>, or -1 if the position doesn't match a corner.
 935          */
 936         private int calculateCorner(Window w, int x, int y) {
 937             Insets insets = w.getInsets();
 938             int xPosition = calculatePosition(x - insets.left,
 939                     w.getWidth() - insets.left - insets.right);
 940             int yPosition = calculatePosition(y - insets.top,
 941                     w.getHeight() - insets.top - insets.bottom);
 942 
 943             if (xPosition == -1 || yPosition == -1) {
 944                 return -1;
 945             }
 946             return yPosition * 5 + xPosition;
 947         }
 948 
 949         /**
 950          * Returns the Cursor to render for the specified corner. This returns
 951          * 0 if the corner doesn't map to a valid Cursor
 952          */
 953         private int getCursor(int corner) {
 954             if (corner == -1) {
 955                 return 0;
 956             }
 957             return cursorMapping[corner];
 958         }
 959 
 960         /**
 961          * Returns an integer indicating the position of <code>spot</code>
 962          * in <code>width</code>. The return value will be:
 963          * 0 if < BORDER_DRAG_THICKNESS
 964          * 1 if < CORNER_DRAG_WIDTH
 965          * 2 if >= CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS
 966          * 3 if >= width - CORNER_DRAG_WIDTH
 967          * 4 if >= width - BORDER_DRAG_THICKNESS
 968          * 5 otherwise
 969          */
 970         private int calculatePosition(int spot, int width) {
 971             if (spot < BORDER_DRAG_THICKNESS) {
 972                 return 0;
 973             }
 974             if (spot < CORNER_DRAG_WIDTH) {
 975                 return 1;
 976             }
 977             if (spot >= (width - BORDER_DRAG_THICKNESS)) {
 978                 return 4;
 979             }
 980             if (spot >= (width - CORNER_DRAG_WIDTH)) {
 981                 return 3;
 982             }


  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 javax.swing.plaf.metal;
  27 
  28 import java.awt.event.*;
  29 import java.beans.PropertyChangeEvent;
  30 import javax.swing.*;
  31 import javax.swing.event.*;
  32 import javax.swing.plaf.*;
  33 import javax.swing.plaf.basic.*;
  34 import java.awt.*;
  35 
  36 /**
  37  * Provides the metal look and feel implementation of {@code RootPaneUI}.
  38  * <p>
  39  * {@code MetalRootPaneUI} provides support for the
  40  * {@code windowDecorationStyle} property of {@code JRootPane}.
  41  * {@code MetalRootPaneUI} does this by way of installing a custom
  42  * {@code LayoutManager}, a private {@code Component} to render
  43  * the appropriate widgets, and a private {@code Border}. The
  44  * {@code LayoutManager} is always installed, regardless of the value of
  45  * the {@code windowDecorationStyle} property, but the
  46  * {@code Border} and {@code Component} are only installed/added if
  47  * the {@code windowDecorationStyle} is other than
  48  * {@code JRootPane.NONE}.
  49  * <p>
  50  * <strong>Warning:</strong>
  51  * Serialized objects of this class will not be compatible with
  52  * future Swing releases. The current serialization support is
  53  * appropriate for short term storage or RMI between applications running
  54  * the same version of Swing.  As of 1.4, support for long term storage
  55  * of all JavaBeans&trade;
  56  * has been added to the {@code java.beans} package.
  57  * Please see {@link java.beans.XMLEncoder}.
  58  *
  59  * @author Terry Kellerman
  60  * @since 1.4
  61  */
  62 @SuppressWarnings("serial") // Same-version serialization only
  63 public class MetalRootPaneUI extends BasicRootPaneUI
  64 {
  65     /**
  66      * Keys to lookup borders in defaults table.
  67      */
  68     private static final String[] borderKeys = new String[] {
  69         null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
  70         "RootPane.informationDialogBorder",
  71         "RootPane.errorDialogBorder", "RootPane.colorChooserDialogBorder",
  72         "RootPane.fileChooserDialogBorder", "RootPane.questionDialogBorder",
  73         "RootPane.warningDialogBorder"
  74     };
  75     /**
  76      * The amount of space (in pixels) that the cursor is changed on.
  77      */
  78     private static final int CORNER_DRAG_WIDTH = 16;
  79 
  80     /**
  81      * Region from edges that dragging is active from.
  82      */
  83     private static final int BORDER_DRAG_THICKNESS = 5;
  84 
  85     /**
  86      * Window the {@code JRootPane} is in.
  87      */
  88     private Window window;
  89 
  90     /**
  91      * {@code JComponent} providing window decorations. This will be
  92      * null if not providing window decorations.
  93      */
  94     private JComponent titlePane;
  95 
  96     /**
  97      * {@code MouseInputListener} that is added to the parent
  98      * {@code Window} the {@code JRootPane} is contained in.
  99      */
 100     private MouseInputListener mouseInputListener;
 101 
 102     /**
 103      * The {@code LayoutManager} that is set on the
 104      * {@code JRootPane}.
 105      */
 106     private LayoutManager layoutManager;
 107 
 108     /**
 109      * {@code LayoutManager} of the {@code JRootPane} before we
 110      * replaced it.
 111      */
 112     private LayoutManager savedOldLayout;
 113 
 114     /**
 115      * {@code JRootPane} providing the look and feel for.
 116      */
 117     private JRootPane root;
 118 
 119     /**
 120      * {@code Cursor} used to track the cursor set by the user.
 121      * This is initially {@code Cursor.DEFAULT_CURSOR}.
 122      */
 123     private Cursor lastCursor =
 124             Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
 125 
 126     /**
 127      * Creates a UI for a {@code JRootPane}.
 128      *
 129      * @param c the JRootPane the RootPaneUI will be created for
 130      * @return the RootPaneUI implementation for the passed in JRootPane
 131      */
 132     public static ComponentUI createUI(JComponent c) {
 133         return new MetalRootPaneUI();
 134     }
 135 
 136     /**
 137      * Invokes supers implementation of {@code installUI} to install
 138      * the necessary state onto the passed in {@code JRootPane}
 139      * to render the metal look and feel implementation of
 140      * {@code RootPaneUI}. If
 141      * the {@code windowDecorationStyle} property of the
 142      * {@code JRootPane} is other than {@code JRootPane.NONE},
 143      * this will add a custom {@code Component} to render the widgets to
 144      * {@code JRootPane}, as well as installing a custom
 145      * {@code Border} and {@code LayoutManager} on the
 146      * {@code JRootPane}.
 147      *
 148      * @param c the JRootPane to install state onto
 149      */
 150     public void installUI(JComponent c) {
 151         super.installUI(c);
 152         root = (JRootPane)c;
 153         int style = root.getWindowDecorationStyle();
 154         if (style != JRootPane.NONE) {
 155             installClientDecorations(root);
 156         }
 157     }
 158 
 159 
 160     /**
 161      * Invokes supers implementation to uninstall any of its state. This will
 162      * also reset the {@code LayoutManager} of the {@code JRootPane}.
 163      * If a {@code Component} has been added to the {@code JRootPane}
 164      * to render the window decoration style, this method will remove it.
 165      * Similarly, this will revert the Border and LayoutManager of the
 166      * {@code JRootPane} to what it was before {@code installUI}
 167      * was invoked.
 168      *
 169      * @param c the JRootPane to uninstall state from
 170      */
 171     public void uninstallUI(JComponent c) {
 172         super.uninstallUI(c);
 173         uninstallClientDecorations(root);
 174 
 175         layoutManager = null;
 176         mouseInputListener = null;
 177         root = null;
 178     }
 179 
 180     /**
 181      * Installs the appropriate {@code Border} onto the
 182      * {@code JRootPane}.
 183      */
 184     void installBorder(JRootPane root) {
 185         int style = root.getWindowDecorationStyle();
 186 
 187         if (style == JRootPane.NONE) {
 188             LookAndFeel.uninstallBorder(root);
 189         }
 190         else {
 191             LookAndFeel.installBorder(root, borderKeys[style]);
 192         }
 193     }
 194 
 195     /**
 196      * Removes any border that may have been installed.
 197      */
 198     private void uninstallBorder(JRootPane root) {
 199         LookAndFeel.uninstallBorder(root);
 200     }
 201 
 202     /**
 203      * Installs the necessary Listeners on the parent {@code Window},
 204      * if there is one.
 205      * <p>
 206      * This takes the parent so that cleanup can be done from
 207      * {@code removeNotify}, at which point the parent hasn't been
 208      * reset yet.
 209      *
 210      * @param parent The parent of the JRootPane
 211      */
 212     private void installWindowListeners(JRootPane root, Component parent) {
 213         if (parent instanceof Window) {
 214             window = (Window)parent;
 215         }
 216         else {
 217             window = SwingUtilities.getWindowAncestor(parent);
 218         }
 219         if (window != null) {
 220             if (mouseInputListener == null) {
 221                 mouseInputListener = createWindowMouseInputListener(root);
 222             }
 223             window.addMouseListener(mouseInputListener);
 224             window.addMouseMotionListener(mouseInputListener);
 225         }
 226     }
 227 
 228     /**
 229      * Uninstalls the necessary Listeners on the {@code Window} the
 230      * Listeners were last installed on.
 231      */
 232     private void uninstallWindowListeners(JRootPane root) {
 233         if (window != null) {
 234             window.removeMouseListener(mouseInputListener);
 235             window.removeMouseMotionListener(mouseInputListener);
 236         }
 237     }
 238 
 239     /**
 240      * Installs the appropriate LayoutManager on the {@code JRootPane}
 241      * to render the window decorations.
 242      */
 243     private void installLayout(JRootPane root) {
 244         if (layoutManager == null) {
 245             layoutManager = createLayoutManager();
 246         }
 247         savedOldLayout = root.getLayout();
 248         root.setLayout(layoutManager);
 249     }
 250 
 251     /**
 252      * Uninstalls the previously installed {@code LayoutManager}.
 253      */
 254     private void uninstallLayout(JRootPane root) {
 255         if (savedOldLayout != null) {
 256             root.setLayout(savedOldLayout);
 257             savedOldLayout = null;
 258         }
 259     }
 260 
 261     /**
 262      * Installs the necessary state onto the JRootPane to render client
 263      * decorations. This is ONLY invoked if the {@code JRootPane}
 264      * has a decoration style other than {@code JRootPane.NONE}.
 265      */
 266     private void installClientDecorations(JRootPane root) {
 267         installBorder(root);
 268 
 269         JComponent titlePane = createTitlePane(root);
 270 
 271         setTitlePane(root, titlePane);
 272         installWindowListeners(root, root.getParent());
 273         installLayout(root);
 274         if (window != null) {
 275             root.revalidate();
 276             root.repaint();
 277         }
 278     }
 279 
 280     /**
 281      * Uninstalls any state that {@code installClientDecorations} has
 282      * installed.
 283      * <p>
 284      * NOTE: This may be called if you haven't installed client decorations
 285      * yet (ie before {@code installClientDecorations} has been invoked).
 286      */
 287     private void uninstallClientDecorations(JRootPane root) {
 288         uninstallBorder(root);
 289         uninstallWindowListeners(root);
 290         setTitlePane(root, null);
 291         uninstallLayout(root);
 292         // We have to revalidate/repaint root if the style is JRootPane.NONE
 293         // only. When we needs to call revalidate/repaint with other styles
 294         // the installClientDecorations is always called after this method
 295         // imediatly and it will cause the revalidate/repaint at the proper
 296         // time.
 297         int style = root.getWindowDecorationStyle();
 298         if (style == JRootPane.NONE) {
 299             root.repaint();
 300             root.revalidate();
 301         }
 302         // Reset the cursor, as we may have changed it to a resize cursor
 303         if (window != null) {
 304             window.setCursor(Cursor.getPredefinedCursor
 305                              (Cursor.DEFAULT_CURSOR));
 306         }
 307         window = null;
 308     }
 309 
 310     /**
 311      * Returns the {@code JComponent} to render the window decoration
 312      * style.
 313      */
 314     private JComponent createTitlePane(JRootPane root) {
 315         return new MetalTitlePane(root, this);
 316     }
 317 
 318     /**
 319      * Returns a {@code MouseListener} that will be added to the
 320      * {@code Window} containing the {@code JRootPane}.
 321      */
 322     private MouseInputListener createWindowMouseInputListener(JRootPane root) {
 323         return new MouseInputHandler();
 324     }
 325 
 326     /**
 327      * Returns a {@code LayoutManager} that will be set on the
 328      * {@code JRootPane}.
 329      */
 330     private LayoutManager createLayoutManager() {
 331         return new MetalRootLayout();
 332     }
 333 
 334     /**
 335      * Sets the window title pane -- the JComponent used to provide a plaf a
 336      * way to override the native operating system's window title pane with
 337      * one whose look and feel are controlled by the plaf.  The plaf creates
 338      * and sets this value; the default is null, implying a native operating
 339      * system window title pane.
 340      *
 341      * @param content the {@code JComponent} to use for the window title pane.
 342      */
 343     private void setTitlePane(JRootPane root, JComponent titlePane) {
 344         JLayeredPane layeredPane = root.getLayeredPane();
 345         JComponent oldTitlePane = getTitlePane();
 346 
 347         if (oldTitlePane != null) {
 348             oldTitlePane.setVisible(false);
 349             layeredPane.remove(oldTitlePane);
 350         }
 351         if (titlePane != null) {
 352             layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
 353             titlePane.setVisible(true);
 354         }
 355         this.titlePane = titlePane;
 356     }
 357 
 358     /**
 359      * Returns the {@code JComponent} rendering the title pane. If this
 360      * returns null, it implies there is no need to render window decorations.
 361      *
 362      * @return the current window title pane, or null
 363      * @see #setTitlePane
 364      */
 365     private JComponent getTitlePane() {
 366         return titlePane;
 367     }
 368 
 369     /**
 370      * Returns the {@code JRootPane} we're providing the look and
 371      * feel for.
 372      */
 373     private JRootPane getRootPane() {
 374         return root;
 375     }
 376 
 377     /**
 378      * Invoked when a property changes. {@code MetalRootPaneUI} is
 379      * primarily interested in events originating from the
 380      * {@code JRootPane} it has been installed on identifying the
 381      * property {@code windowDecorationStyle}. If the
 382      * {@code windowDecorationStyle} has changed to a value other
 383      * than {@code JRootPane.NONE}, this will add a {@code Component}
 384      * to the {@code JRootPane} to render the window decorations, as well
 385      * as installing a {@code Border} on the {@code JRootPane}.
 386      * On the other hand, if the {@code windowDecorationStyle} has
 387      * changed to {@code JRootPane.NONE}, this will remove the
 388      * {@code Component} that has been added to the {@code JRootPane}
 389      * as well resetting the Border to what it was before
 390      * {@code installUI} was invoked.
 391      *
 392      * @param e A PropertyChangeEvent object describing the event source
 393      *          and the property that has changed.
 394      */
 395     public void propertyChange(PropertyChangeEvent e) {
 396         super.propertyChange(e);
 397 
 398         String propertyName = e.getPropertyName();
 399         if(propertyName == null) {
 400             return;
 401         }
 402 
 403         if(propertyName.equals("windowDecorationStyle")) {
 404             JRootPane root = (JRootPane) e.getSource();
 405             int style = root.getWindowDecorationStyle();
 406 
 407             // This is potentially more than needs to be done,
 408             // but it rarely happens and makes the install/uninstall process
 409             // simpler. MetalTitlePane also assumes it will be recreated if
 410             // the decoration style changes.


 913 
 914             int state = f.getExtendedState();
 915             if (getTitlePane() != null &&
 916                     getTitlePane().contains(convertedPoint)) {
 917                 if ((ev.getClickCount() % 2) == 0 &&
 918                         ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
 919                     if (f.isResizable()) {
 920                         if ((state & Frame.MAXIMIZED_BOTH) != 0) {
 921                             f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
 922                         }
 923                         else {
 924                             f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
 925                         }
 926                         return;
 927                     }
 928                 }
 929             }
 930         }
 931 
 932         /**
 933          * Returns the corner that contains the point {@code x},
 934          * {@code y}, or -1 if the position doesn't match a corner.
 935          */
 936         private int calculateCorner(Window w, int x, int y) {
 937             Insets insets = w.getInsets();
 938             int xPosition = calculatePosition(x - insets.left,
 939                     w.getWidth() - insets.left - insets.right);
 940             int yPosition = calculatePosition(y - insets.top,
 941                     w.getHeight() - insets.top - insets.bottom);
 942 
 943             if (xPosition == -1 || yPosition == -1) {
 944                 return -1;
 945             }
 946             return yPosition * 5 + xPosition;
 947         }
 948 
 949         /**
 950          * Returns the Cursor to render for the specified corner. This returns
 951          * 0 if the corner doesn't map to a valid Cursor
 952          */
 953         private int getCursor(int corner) {
 954             if (corner == -1) {
 955                 return 0;
 956             }
 957             return cursorMapping[corner];
 958         }
 959 
 960         /**
 961          * Returns an integer indicating the position of {@code spot}
 962          * in {@code width}. The return value will be:
 963          * 0 if < BORDER_DRAG_THICKNESS
 964          * 1 if < CORNER_DRAG_WIDTH
 965          * 2 if >= CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS
 966          * 3 if >= width - CORNER_DRAG_WIDTH
 967          * 4 if >= width - BORDER_DRAG_THICKNESS
 968          * 5 otherwise
 969          */
 970         private int calculatePosition(int spot, int width) {
 971             if (spot < BORDER_DRAG_THICKNESS) {
 972                 return 0;
 973             }
 974             if (spot < CORNER_DRAG_WIDTH) {
 975                 return 1;
 976             }
 977             if (spot >= (width - BORDER_DRAG_THICKNESS)) {
 978                 return 4;
 979             }
 980             if (spot >= (width - CORNER_DRAG_WIDTH)) {
 981                 return 3;
 982             }
< prev index next >