Save This Page
Home » openjdk-7 » javax » swing » plaf » metal » [javadoc | source]
    1   /*
    2    * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing.plaf.metal;
   27   
   28   import java.awt.event;
   29   import java.beans.PropertyChangeEvent;
   30   import java.beans.PropertyChangeListener;
   31   import javax.swing;
   32   import javax.swing.border;
   33   import javax.swing.event;
   34   import javax.swing.plaf;
   35   import javax.swing.plaf.basic;
   36   import java.awt;
   37   import java.io;
   38   import java.security;
   39   
   40   /**
   41    * Provides the metal look and feel implementation of <code>RootPaneUI</code>.
   42    * <p>
   43    * <code>MetalRootPaneUI</code> provides support for the
   44    * <code>windowDecorationStyle</code> property of <code>JRootPane</code>.
   45    * <code>MetalRootPaneUI</code> does this by way of installing a custom
   46    * <code>LayoutManager</code>, a private <code>Component</code> to render
   47    * the appropriate widgets, and a private <code>Border</code>. The
   48    * <code>LayoutManager</code> is always installed, regardless of the value of
   49    * the <code>windowDecorationStyle</code> property, but the
   50    * <code>Border</code> and <code>Component</code> are only installed/added if
   51    * the <code>windowDecorationStyle</code> is other than
   52    * <code>JRootPane.NONE</code>.
   53    * <p>
   54    * <strong>Warning:</strong>
   55    * Serialized objects of this class will not be compatible with
   56    * future Swing releases. The current serialization support is
   57    * appropriate for short term storage or RMI between applications running
   58    * the same version of Swing.  As of 1.4, support for long term storage
   59    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   60    * has been added to the <code>java.beans</code> package.
   61    * Please see {@link java.beans.XMLEncoder}.
   62    *
   63    * @author Terry Kellerman
   64    * @since 1.4
   65    */
   66   public class MetalRootPaneUI extends BasicRootPaneUI
   67   {
   68       /**
   69        * Keys to lookup borders in defaults table.
   70        */
   71       private static final String[] borderKeys = new String[] {
   72           null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
   73           "RootPane.informationDialogBorder",
   74           "RootPane.errorDialogBorder", "RootPane.colorChooserDialogBorder",
   75           "RootPane.fileChooserDialogBorder", "RootPane.questionDialogBorder",
   76           "RootPane.warningDialogBorder"
   77       };
   78       /**
   79        * The amount of space (in pixels) that the cursor is changed on.
   80        */
   81       private static final int CORNER_DRAG_WIDTH = 16;
   82   
   83       /**
   84        * Region from edges that dragging is active from.
   85        */
   86       private static final int BORDER_DRAG_THICKNESS = 5;
   87   
   88       /**
   89        * Window the <code>JRootPane</code> is in.
   90        */
   91       private Window window;
   92   
   93       /**
   94        * <code>JComponent</code> providing window decorations. This will be
   95        * null if not providing window decorations.
   96        */
   97       private JComponent titlePane;
   98   
   99       /**
  100        * <code>MouseInputListener</code> that is added to the parent
  101        * <code>Window</code> the <code>JRootPane</code> is contained in.
  102        */
  103       private MouseInputListener mouseInputListener;
  104   
  105       /**
  106        * The <code>LayoutManager</code> that is set on the
  107        * <code>JRootPane</code>.
  108        */
  109       private LayoutManager layoutManager;
  110   
  111       /**
  112        * <code>LayoutManager</code> of the <code>JRootPane</code> before we
  113        * replaced it.
  114        */
  115       private LayoutManager savedOldLayout;
  116   
  117       /**
  118        * <code>JRootPane</code> providing the look and feel for.
  119        */
  120       private JRootPane root;
  121   
  122       /**
  123        * <code>Cursor</code> used to track the cursor set by the user.
  124        * This is initially <code>Cursor.DEFAULT_CURSOR</code>.
  125        */
  126       private Cursor lastCursor =
  127               Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
  128   
  129       /**
  130        * Creates a UI for a <code>JRootPane</code>.
  131        *
  132        * @param c the JRootPane the RootPaneUI will be created for
  133        * @return the RootPaneUI implementation for the passed in JRootPane
  134        */
  135       public static ComponentUI createUI(JComponent c) {
  136           return new MetalRootPaneUI();
  137       }
  138   
  139       /**
  140        * Invokes supers implementation of <code>installUI</code> to install
  141        * the necessary state onto the passed in <code>JRootPane</code>
  142        * to render the metal look and feel implementation of
  143        * <code>RootPaneUI</code>. If
  144        * the <code>windowDecorationStyle</code> property of the
  145        * <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
  146        * this will add a custom <code>Component</code> to render the widgets to
  147        * <code>JRootPane</code>, as well as installing a custom
  148        * <code>Border</code> and <code>LayoutManager</code> on the
  149        * <code>JRootPane</code>.
  150        *
  151        * @param c the JRootPane to install state onto
  152        */
  153       public void installUI(JComponent c) {
  154           super.installUI(c);
  155           root = (JRootPane)c;
  156           int style = root.getWindowDecorationStyle();
  157           if (style != JRootPane.NONE) {
  158               installClientDecorations(root);
  159           }
  160       }
  161   
  162   
  163       /**
  164        * Invokes supers implementation to uninstall any of its state. This will
  165        * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
  166        * If a <code>Component</code> has been added to the <code>JRootPane</code>
  167        * to render the window decoration style, this method will remove it.
  168        * Similarly, this will revert the Border and LayoutManager of the
  169        * <code>JRootPane</code> to what it was before <code>installUI</code>
  170        * was invoked.
  171        *
  172        * @param c the JRootPane to uninstall state from
  173        */
  174       public void uninstallUI(JComponent c) {
  175           super.uninstallUI(c);
  176           uninstallClientDecorations(root);
  177   
  178           layoutManager = null;
  179           mouseInputListener = null;
  180           root = null;
  181       }
  182   
  183       /**
  184        * Installs the appropriate <code>Border</code> onto the
  185        * <code>JRootPane</code>.
  186        */
  187       void installBorder(JRootPane root) {
  188           int style = root.getWindowDecorationStyle();
  189   
  190           if (style == JRootPane.NONE) {
  191               LookAndFeel.uninstallBorder(root);
  192           }
  193           else {
  194               LookAndFeel.installBorder(root, borderKeys[style]);
  195           }
  196       }
  197   
  198       /**
  199        * Removes any border that may have been installed.
  200        */
  201       private void uninstallBorder(JRootPane root) {
  202           LookAndFeel.uninstallBorder(root);
  203       }
  204   
  205       /**
  206        * Installs the necessary Listeners on the parent <code>Window</code>,
  207        * if there is one.
  208        * <p>
  209        * This takes the parent so that cleanup can be done from
  210        * <code>removeNotify</code>, at which point the parent hasn't been
  211        * reset yet.
  212        *
  213        * @param parent The parent of the JRootPane
  214        */
  215       private void installWindowListeners(JRootPane root, Component parent) {
  216           if (parent instanceof Window) {
  217               window = (Window)parent;
  218           }
  219           else {
  220               window = SwingUtilities.getWindowAncestor(parent);
  221           }
  222           if (window != null) {
  223               if (mouseInputListener == null) {
  224                   mouseInputListener = createWindowMouseInputListener(root);
  225               }
  226               window.addMouseListener(mouseInputListener);
  227               window.addMouseMotionListener(mouseInputListener);
  228           }
  229       }
  230   
  231       /**
  232        * Uninstalls the necessary Listeners on the <code>Window</code> the
  233        * Listeners were last installed on.
  234        */
  235       private void uninstallWindowListeners(JRootPane root) {
  236           if (window != null) {
  237               window.removeMouseListener(mouseInputListener);
  238               window.removeMouseMotionListener(mouseInputListener);
  239           }
  240       }
  241   
  242       /**
  243        * Installs the appropriate LayoutManager on the <code>JRootPane</code>
  244        * to render the window decorations.
  245        */
  246       private void installLayout(JRootPane root) {
  247           if (layoutManager == null) {
  248               layoutManager = createLayoutManager();
  249           }
  250           savedOldLayout = root.getLayout();
  251           root.setLayout(layoutManager);
  252       }
  253   
  254       /**
  255        * Uninstalls the previously installed <code>LayoutManager</code>.
  256        */
  257       private void uninstallLayout(JRootPane root) {
  258           if (savedOldLayout != null) {
  259               root.setLayout(savedOldLayout);
  260               savedOldLayout = null;
  261           }
  262       }
  263   
  264       /**
  265        * Installs the necessary state onto the JRootPane to render client
  266        * decorations. This is ONLY invoked if the <code>JRootPane</code>
  267        * has a decoration style other than <code>JRootPane.NONE</code>.
  268        */
  269       private void installClientDecorations(JRootPane root) {
  270           installBorder(root);
  271   
  272           JComponent titlePane = createTitlePane(root);
  273   
  274           setTitlePane(root, titlePane);
  275           installWindowListeners(root, root.getParent());
  276           installLayout(root);
  277           if (window != null) {
  278               root.revalidate();
  279               root.repaint();
  280           }
  281       }
  282   
  283       /**
  284        * Uninstalls any state that <code>installClientDecorations</code> has
  285        * installed.
  286        * <p>
  287        * NOTE: This may be called if you haven't installed client decorations
  288        * yet (ie before <code>installClientDecorations</code> has been invoked).
  289        */
  290       private void uninstallClientDecorations(JRootPane root) {
  291           uninstallBorder(root);
  292           uninstallWindowListeners(root);
  293           setTitlePane(root, null);
  294           uninstallLayout(root);
  295           // We have to revalidate/repaint root if the style is JRootPane.NONE
  296           // only. When we needs to call revalidate/repaint with other styles
  297           // the installClientDecorations is always called after this method
  298           // imediatly and it will cause the revalidate/repaint at the proper
  299           // time.
  300           int style = root.getWindowDecorationStyle();
  301           if (style == JRootPane.NONE) {
  302               root.repaint();
  303               root.revalidate();
  304           }
  305           // Reset the cursor, as we may have changed it to a resize cursor
  306           if (window != null) {
  307               window.setCursor(Cursor.getPredefinedCursor
  308                                (Cursor.DEFAULT_CURSOR));
  309           }
  310           window = null;
  311       }
  312   
  313       /**
  314        * Returns the <code>JComponent</code> to render the window decoration
  315        * style.
  316        */
  317       private JComponent createTitlePane(JRootPane root) {
  318           return new MetalTitlePane(root, this);
  319       }
  320   
  321       /**
  322        * Returns a <code>MouseListener</code> that will be added to the
  323        * <code>Window</code> containing the <code>JRootPane</code>.
  324        */
  325       private MouseInputListener createWindowMouseInputListener(JRootPane root) {
  326           return new MouseInputHandler();
  327       }
  328   
  329       /**
  330        * Returns a <code>LayoutManager</code> that will be set on the
  331        * <code>JRootPane</code>.
  332        */
  333       private LayoutManager createLayoutManager() {
  334           return new MetalRootLayout();
  335       }
  336   
  337       /**
  338        * Sets the window title pane -- the JComponent used to provide a plaf a
  339        * way to override the native operating system's window title pane with
  340        * one whose look and feel are controlled by the plaf.  The plaf creates
  341        * and sets this value; the default is null, implying a native operating
  342        * system window title pane.
  343        *
  344        * @param content the <code>JComponent</code> to use for the window title pane.
  345        */
  346       private void setTitlePane(JRootPane root, JComponent titlePane) {
  347           JLayeredPane layeredPane = root.getLayeredPane();
  348           JComponent oldTitlePane = getTitlePane();
  349   
  350           if (oldTitlePane != null) {
  351               oldTitlePane.setVisible(false);
  352               layeredPane.remove(oldTitlePane);
  353           }
  354           if (titlePane != null) {
  355               layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
  356               titlePane.setVisible(true);
  357           }
  358           this.titlePane = titlePane;
  359       }
  360   
  361       /**
  362        * Returns the <code>JComponent</code> rendering the title pane. If this
  363        * returns null, it implies there is no need to render window decorations.
  364        *
  365        * @return the current window title pane, or null
  366        * @see #setTitlePane
  367        */
  368       private JComponent getTitlePane() {
  369           return titlePane;
  370       }
  371   
  372       /**
  373        * Returns the <code>JRootPane</code> we're providing the look and
  374        * feel for.
  375        */
  376       private JRootPane getRootPane() {
  377           return root;
  378       }
  379   
  380       /**
  381        * Invoked when a property changes. <code>MetalRootPaneUI</code> is
  382        * primarily interested in events originating from the
  383        * <code>JRootPane</code> it has been installed on identifying the
  384        * property <code>windowDecorationStyle</code>. If the
  385        * <code>windowDecorationStyle</code> has changed to a value other
  386        * than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
  387        * to the <code>JRootPane</code> to render the window decorations, as well
  388        * as installing a <code>Border</code> on the <code>JRootPane</code>.
  389        * On the other hand, if the <code>windowDecorationStyle</code> has
  390        * changed to <code>JRootPane.NONE</code>, this will remove the
  391        * <code>Component</code> that has been added to the <code>JRootPane</code>
  392        * as well resetting the Border to what it was before
  393        * <code>installUI</code> was invoked.
  394        *
  395        * @param e A PropertyChangeEvent object describing the event source
  396        *          and the property that has changed.
  397        */
  398       public void propertyChange(PropertyChangeEvent e) {
  399           super.propertyChange(e);
  400   
  401           String propertyName = e.getPropertyName();
  402           if(propertyName == null) {
  403               return;
  404           }
  405   
  406           if(propertyName.equals("windowDecorationStyle")) {
  407               JRootPane root = (JRootPane) e.getSource();
  408               int style = root.getWindowDecorationStyle();
  409   
  410               // This is potentially more than needs to be done,
  411               // but it rarely happens and makes the install/uninstall process
  412               // simpler. MetalTitlePane also assumes it will be recreated if
  413               // the decoration style changes.
  414               uninstallClientDecorations(root);
  415               if (style != JRootPane.NONE) {
  416                   installClientDecorations(root);
  417               }
  418           }
  419           else if (propertyName.equals("ancestor")) {
  420               uninstallWindowListeners(root);
  421               if (((JRootPane)e.getSource()).getWindowDecorationStyle() !=
  422                                              JRootPane.NONE) {
  423                   installWindowListeners(root, root.getParent());
  424               }
  425           }
  426           return;
  427       }
  428   
  429       /**
  430        * A custom layout manager that is responsible for the layout of
  431        * layeredPane, glassPane, menuBar and titlePane, if one has been
  432        * installed.
  433        */
  434       // NOTE: Ideally this would extends JRootPane.RootLayout, but that
  435       //       would force this to be non-static.
  436       private static class MetalRootLayout implements LayoutManager2 {
  437           /**
  438            * Returns the amount of space the layout would like to have.
  439            *
  440            * @param the Container for which this layout manager is being used
  441            * @return a Dimension object containing the layout's preferred size
  442            */
  443           public Dimension preferredLayoutSize(Container parent) {
  444               Dimension cpd, mbd, tpd;
  445               int cpWidth = 0;
  446               int cpHeight = 0;
  447               int mbWidth = 0;
  448               int mbHeight = 0;
  449               int tpWidth = 0;
  450               int tpHeight = 0;
  451               Insets i = parent.getInsets();
  452               JRootPane root = (JRootPane) parent;
  453   
  454               if(root.getContentPane() != null) {
  455                   cpd = root.getContentPane().getPreferredSize();
  456               } else {
  457                   cpd = root.getSize();
  458               }
  459               if (cpd != null) {
  460                   cpWidth = cpd.width;
  461                   cpHeight = cpd.height;
  462               }
  463   
  464               if(root.getMenuBar() != null) {
  465                   mbd = root.getMenuBar().getPreferredSize();
  466                   if (mbd != null) {
  467                       mbWidth = mbd.width;
  468                       mbHeight = mbd.height;
  469                   }
  470               }
  471   
  472               if (root.getWindowDecorationStyle() != JRootPane.NONE &&
  473                        (root.getUI() instanceof MetalRootPaneUI)) {
  474                   JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
  475                                          getTitlePane();
  476                   if (titlePane != null) {
  477                       tpd = titlePane.getPreferredSize();
  478                       if (tpd != null) {
  479                           tpWidth = tpd.width;
  480                           tpHeight = tpd.height;
  481                       }
  482                   }
  483               }
  484   
  485               return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
  486                                    cpHeight + mbHeight + tpWidth + i.top + i.bottom);
  487           }
  488   
  489           /**
  490            * Returns the minimum amount of space the layout needs.
  491            *
  492            * @param the Container for which this layout manager is being used
  493            * @return a Dimension object containing the layout's minimum size
  494            */
  495           public Dimension minimumLayoutSize(Container parent) {
  496               Dimension cpd, mbd, tpd;
  497               int cpWidth = 0;
  498               int cpHeight = 0;
  499               int mbWidth = 0;
  500               int mbHeight = 0;
  501               int tpWidth = 0;
  502               int tpHeight = 0;
  503               Insets i = parent.getInsets();
  504               JRootPane root = (JRootPane) parent;
  505   
  506               if(root.getContentPane() != null) {
  507                   cpd = root.getContentPane().getMinimumSize();
  508               } else {
  509                   cpd = root.getSize();
  510               }
  511               if (cpd != null) {
  512                   cpWidth = cpd.width;
  513                   cpHeight = cpd.height;
  514               }
  515   
  516               if(root.getMenuBar() != null) {
  517                   mbd = root.getMenuBar().getMinimumSize();
  518                   if (mbd != null) {
  519                       mbWidth = mbd.width;
  520                       mbHeight = mbd.height;
  521                   }
  522               }
  523               if (root.getWindowDecorationStyle() != JRootPane.NONE &&
  524                        (root.getUI() instanceof MetalRootPaneUI)) {
  525                   JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
  526                                          getTitlePane();
  527                   if (titlePane != null) {
  528                       tpd = titlePane.getMinimumSize();
  529                       if (tpd != null) {
  530                           tpWidth = tpd.width;
  531                           tpHeight = tpd.height;
  532                       }
  533                   }
  534               }
  535   
  536               return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
  537                                    cpHeight + mbHeight + tpWidth + i.top + i.bottom);
  538           }
  539   
  540           /**
  541            * Returns the maximum amount of space the layout can use.
  542            *
  543            * @param the Container for which this layout manager is being used
  544            * @return a Dimension object containing the layout's maximum size
  545            */
  546           public Dimension maximumLayoutSize(Container target) {
  547               Dimension cpd, mbd, tpd;
  548               int cpWidth = Integer.MAX_VALUE;
  549               int cpHeight = Integer.MAX_VALUE;
  550               int mbWidth = Integer.MAX_VALUE;
  551               int mbHeight = Integer.MAX_VALUE;
  552               int tpWidth = Integer.MAX_VALUE;
  553               int tpHeight = Integer.MAX_VALUE;
  554               Insets i = target.getInsets();
  555               JRootPane root = (JRootPane) target;
  556   
  557               if(root.getContentPane() != null) {
  558                   cpd = root.getContentPane().getMaximumSize();
  559                   if (cpd != null) {
  560                       cpWidth = cpd.width;
  561                       cpHeight = cpd.height;
  562                   }
  563               }
  564   
  565               if(root.getMenuBar() != null) {
  566                   mbd = root.getMenuBar().getMaximumSize();
  567                   if (mbd != null) {
  568                       mbWidth = mbd.width;
  569                       mbHeight = mbd.height;
  570                   }
  571               }
  572   
  573               if (root.getWindowDecorationStyle() != JRootPane.NONE &&
  574                        (root.getUI() instanceof MetalRootPaneUI)) {
  575                   JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
  576                                          getTitlePane();
  577                   if (titlePane != null)
  578                   {
  579                       tpd = titlePane.getMaximumSize();
  580                       if (tpd != null) {
  581                           tpWidth = tpd.width;
  582                           tpHeight = tpd.height;
  583                       }
  584                   }
  585               }
  586   
  587               int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
  588               // Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE
  589               // Only will happen if sums to more than 2 billion units.  Not likely.
  590               if (maxHeight != Integer.MAX_VALUE) {
  591                   maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
  592               }
  593   
  594               int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
  595               // Similar overflow comment as above
  596               if (maxWidth != Integer.MAX_VALUE) {
  597                   maxWidth += i.left + i.right;
  598               }
  599   
  600               return new Dimension(maxWidth, maxHeight);
  601           }
  602   
  603           /**
  604            * Instructs the layout manager to perform the layout for the specified
  605            * container.
  606            *
  607            * @param the Container for which this layout manager is being used
  608            */
  609           public void layoutContainer(Container parent) {
  610               JRootPane root = (JRootPane) parent;
  611               Rectangle b = root.getBounds();
  612               Insets i = root.getInsets();
  613               int nextY = 0;
  614               int w = b.width - i.right - i.left;
  615               int h = b.height - i.top - i.bottom;
  616   
  617               if(root.getLayeredPane() != null) {
  618                   root.getLayeredPane().setBounds(i.left, i.top, w, h);
  619               }
  620               if(root.getGlassPane() != null) {
  621                   root.getGlassPane().setBounds(i.left, i.top, w, h);
  622               }
  623               // Note: This is laying out the children in the layeredPane,
  624               // technically, these are not our children.
  625               if (root.getWindowDecorationStyle() != JRootPane.NONE &&
  626                        (root.getUI() instanceof MetalRootPaneUI)) {
  627                   JComponent titlePane = ((MetalRootPaneUI)root.getUI()).
  628                                          getTitlePane();
  629                   if (titlePane != null) {
  630                       Dimension tpd = titlePane.getPreferredSize();
  631                       if (tpd != null) {
  632                           int tpHeight = tpd.height;
  633                           titlePane.setBounds(0, 0, w, tpHeight);
  634                           nextY += tpHeight;
  635                       }
  636                   }
  637               }
  638               if(root.getMenuBar() != null) {
  639                   Dimension mbd = root.getMenuBar().getPreferredSize();
  640                   root.getMenuBar().setBounds(0, nextY, w, mbd.height);
  641                   nextY += mbd.height;
  642               }
  643               if(root.getContentPane() != null) {
  644                   Dimension cpd = root.getContentPane().getPreferredSize();
  645                   root.getContentPane().setBounds(0, nextY, w,
  646                   h < nextY ? 0 : h - nextY);
  647               }
  648           }
  649   
  650           public void addLayoutComponent(String name, Component comp) {}
  651           public void removeLayoutComponent(Component comp) {}
  652           public void addLayoutComponent(Component comp, Object constraints) {}
  653           public float getLayoutAlignmentX(Container target) { return 0.0f; }
  654           public float getLayoutAlignmentY(Container target) { return 0.0f; }
  655           public void invalidateLayout(Container target) {}
  656       }
  657   
  658   
  659       /**
  660        * Maps from positions to cursor type. Refer to calculateCorner and
  661        * calculatePosition for details of this.
  662        */
  663       private static final int[] cursorMapping = new int[]
  664       { Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR,
  665                Cursor.NE_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
  666         Cursor.NW_RESIZE_CURSOR, 0, 0, 0, Cursor.NE_RESIZE_CURSOR,
  667         Cursor.W_RESIZE_CURSOR, 0, 0, 0, Cursor.E_RESIZE_CURSOR,
  668         Cursor.SW_RESIZE_CURSOR, 0, 0, 0, Cursor.SE_RESIZE_CURSOR,
  669         Cursor.SW_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
  670                Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR
  671       };
  672   
  673       /**
  674        * MouseInputHandler is responsible for handling resize/moving of
  675        * the Window. It sets the cursor directly on the Window when then
  676        * mouse moves over a hot spot.
  677        */
  678       private class MouseInputHandler implements MouseInputListener {
  679           /**
  680            * Set to true if the drag operation is moving the window.
  681            */
  682           private boolean isMovingWindow;
  683   
  684           /**
  685            * Used to determine the corner the resize is occuring from.
  686            */
  687           private int dragCursor;
  688   
  689           /**
  690            * X location the mouse went down on for a drag operation.
  691            */
  692           private int dragOffsetX;
  693   
  694           /**
  695            * Y location the mouse went down on for a drag operation.
  696            */
  697           private int dragOffsetY;
  698   
  699           /**
  700            * Width of the window when the drag started.
  701            */
  702           private int dragWidth;
  703   
  704           /**
  705            * Height of the window when the drag started.
  706            */
  707           private int dragHeight;
  708   
  709           public void mousePressed(MouseEvent ev) {
  710               JRootPane rootPane = getRootPane();
  711   
  712               if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
  713                   return;
  714               }
  715               Point dragWindowOffset = ev.getPoint();
  716               Window w = (Window)ev.getSource();
  717               if (w != null) {
  718                   w.toFront();
  719               }
  720               Point convertedDragWindowOffset = SwingUtilities.convertPoint(
  721                              w, dragWindowOffset, getTitlePane());
  722   
  723               Frame f = null;
  724               Dialog d = null;
  725   
  726               if (w instanceof Frame) {
  727                   f = (Frame)w;
  728               } else if (w instanceof Dialog) {
  729                   d = (Dialog)w;
  730               }
  731   
  732               int frameState = (f != null) ? f.getExtendedState() : 0;
  733   
  734               if (getTitlePane() != null &&
  735                           getTitlePane().contains(convertedDragWindowOffset)) {
  736                   if ((f != null && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
  737                           || (d != null))
  738                           && dragWindowOffset.y >= BORDER_DRAG_THICKNESS
  739                           && dragWindowOffset.x >= BORDER_DRAG_THICKNESS
  740                           && dragWindowOffset.x < w.getWidth()
  741                               - BORDER_DRAG_THICKNESS) {
  742                       isMovingWindow = true;
  743                       dragOffsetX = dragWindowOffset.x;
  744                       dragOffsetY = dragWindowOffset.y;
  745                   }
  746               }
  747               else if (f != null && f.isResizable()
  748                       && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
  749                       || (d != null && d.isResizable())) {
  750                   dragOffsetX = dragWindowOffset.x;
  751                   dragOffsetY = dragWindowOffset.y;
  752                   dragWidth = w.getWidth();
  753                   dragHeight = w.getHeight();
  754                   dragCursor = getCursor(calculateCorner(
  755                                w, dragWindowOffset.x, dragWindowOffset.y));
  756               }
  757           }
  758   
  759           public void mouseReleased(MouseEvent ev) {
  760               if (dragCursor != 0 && window != null && !window.isValid()) {
  761                   // Some Window systems validate as you resize, others won't,
  762                   // thus the check for validity before repainting.
  763                   window.validate();
  764                   getRootPane().repaint();
  765               }
  766               isMovingWindow = false;
  767               dragCursor = 0;
  768           }
  769   
  770           public void mouseMoved(MouseEvent ev) {
  771               JRootPane root = getRootPane();
  772   
  773               if (root.getWindowDecorationStyle() == JRootPane.NONE) {
  774                   return;
  775               }
  776   
  777               Window w = (Window)ev.getSource();
  778   
  779               Frame f = null;
  780               Dialog d = null;
  781   
  782               if (w instanceof Frame) {
  783                   f = (Frame)w;
  784               } else if (w instanceof Dialog) {
  785                   d = (Dialog)w;
  786               }
  787   
  788               // Update the cursor
  789               int cursor = getCursor(calculateCorner(w, ev.getX(), ev.getY()));
  790   
  791               if (cursor != 0 && ((f != null && (f.isResizable() &&
  792                       (f.getExtendedState() & Frame.MAXIMIZED_BOTH) == 0))
  793                       || (d != null && d.isResizable()))) {
  794                   w.setCursor(Cursor.getPredefinedCursor(cursor));
  795               }
  796               else {
  797                   w.setCursor(lastCursor);
  798               }
  799           }
  800   
  801           private void adjust(Rectangle bounds, Dimension min, int deltaX,
  802                               int deltaY, int deltaWidth, int deltaHeight) {
  803               bounds.x += deltaX;
  804               bounds.y += deltaY;
  805               bounds.width += deltaWidth;
  806               bounds.height += deltaHeight;
  807               if (min != null) {
  808                   if (bounds.width < min.width) {
  809                       int correction = min.width - bounds.width;
  810                       if (deltaX != 0) {
  811                           bounds.x -= correction;
  812                       }
  813                       bounds.width = min.width;
  814                   }
  815                   if (bounds.height < min.height) {
  816                       int correction = min.height - bounds.height;
  817                       if (deltaY != 0) {
  818                           bounds.y -= correction;
  819                       }
  820                       bounds.height = min.height;
  821                   }
  822               }
  823           }
  824   
  825           public void mouseDragged(MouseEvent ev) {
  826               Window w = (Window)ev.getSource();
  827               Point pt = ev.getPoint();
  828   
  829               if (isMovingWindow) {
  830                   Point eventLocationOnScreen = ev.getLocationOnScreen();
  831                   w.setLocation(eventLocationOnScreen.x - dragOffsetX,
  832                                 eventLocationOnScreen.y - dragOffsetY);
  833               }
  834               else if (dragCursor != 0) {
  835                   Rectangle r = w.getBounds();
  836                   Rectangle startBounds = new Rectangle(r);
  837                   Dimension min = w.getMinimumSize();
  838   
  839                   switch (dragCursor) {
  840                   case Cursor.E_RESIZE_CURSOR:
  841                       adjust(r, min, 0, 0, pt.x + (dragWidth - dragOffsetX) -
  842                              r.width, 0);
  843                       break;
  844                   case Cursor.S_RESIZE_CURSOR:
  845                       adjust(r, min, 0, 0, 0, pt.y + (dragHeight - dragOffsetY) -
  846                              r.height);
  847                       break;
  848                   case Cursor.N_RESIZE_CURSOR:
  849                       adjust(r, min, 0, pt.y -dragOffsetY, 0,
  850                              -(pt.y - dragOffsetY));
  851                       break;
  852                   case Cursor.W_RESIZE_CURSOR:
  853                       adjust(r, min, pt.x - dragOffsetX, 0,
  854                              -(pt.x - dragOffsetX), 0);
  855                       break;
  856                   case Cursor.NE_RESIZE_CURSOR:
  857                       adjust(r, min, 0, pt.y - dragOffsetY,
  858                              pt.x + (dragWidth - dragOffsetX) - r.width,
  859                              -(pt.y - dragOffsetY));
  860                       break;
  861                   case Cursor.SE_RESIZE_CURSOR:
  862                       adjust(r, min, 0, 0,
  863                              pt.x + (dragWidth - dragOffsetX) - r.width,
  864                              pt.y + (dragHeight - dragOffsetY) -
  865                              r.height);
  866                       break;
  867                   case Cursor.NW_RESIZE_CURSOR:
  868                       adjust(r, min, pt.x - dragOffsetX,
  869                              pt.y - dragOffsetY,
  870                              -(pt.x - dragOffsetX),
  871                              -(pt.y - dragOffsetY));
  872                       break;
  873                   case Cursor.SW_RESIZE_CURSOR:
  874                       adjust(r, min, pt.x - dragOffsetX, 0,
  875                              -(pt.x - dragOffsetX),
  876                              pt.y + (dragHeight - dragOffsetY) - r.height);
  877                       break;
  878                   default:
  879                       break;
  880                   }
  881                   if (!r.equals(startBounds)) {
  882                       w.setBounds(r);
  883                       // Defer repaint/validate on mouseReleased unless dynamic
  884                       // layout is active.
  885                       if (Toolkit.getDefaultToolkit().isDynamicLayoutActive()) {
  886                           w.validate();
  887                           getRootPane().repaint();
  888                       }
  889                   }
  890               }
  891           }
  892   
  893           public void mouseEntered(MouseEvent ev) {
  894               Window w = (Window)ev.getSource();
  895               lastCursor = w.getCursor();
  896               mouseMoved(ev);
  897           }
  898   
  899           public void mouseExited(MouseEvent ev) {
  900               Window w = (Window)ev.getSource();
  901               w.setCursor(lastCursor);
  902           }
  903   
  904           public void mouseClicked(MouseEvent ev) {
  905               Window w = (Window)ev.getSource();
  906               Frame f = null;
  907   
  908               if (w instanceof Frame) {
  909                   f = (Frame)w;
  910               } else {
  911                   return;
  912               }
  913   
  914               Point convertedPoint = SwingUtilities.convertPoint(
  915                              w, ev.getPoint(), getTitlePane());
  916   
  917               int state = f.getExtendedState();
  918               if (getTitlePane() != null &&
  919                       getTitlePane().contains(convertedPoint)) {
  920                   if ((ev.getClickCount() % 2) == 0 &&
  921                           ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
  922                       if (f.isResizable()) {
  923                           if ((state & Frame.MAXIMIZED_BOTH) != 0) {
  924                               f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
  925                           }
  926                           else {
  927                               f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
  928                           }
  929                           return;
  930                       }
  931                   }
  932               }
  933           }
  934   
  935           /**
  936            * Returns the corner that contains the point <code>x</code>,
  937            * <code>y</code>, or -1 if the position doesn't match a corner.
  938            */
  939           private int calculateCorner(Window w, int x, int y) {
  940               Insets insets = w.getInsets();
  941               int xPosition = calculatePosition(x - insets.left,
  942                       w.getWidth() - insets.left - insets.right);
  943               int yPosition = calculatePosition(y - insets.top,
  944                       w.getHeight() - insets.top - insets.bottom);
  945   
  946               if (xPosition == -1 || yPosition == -1) {
  947                   return -1;
  948               }
  949               return yPosition * 5 + xPosition;
  950           }
  951   
  952           /**
  953            * Returns the Cursor to render for the specified corner. This returns
  954            * 0 if the corner doesn't map to a valid Cursor
  955            */
  956           private int getCursor(int corner) {
  957               if (corner == -1) {
  958                   return 0;
  959               }
  960               return cursorMapping[corner];
  961           }
  962   
  963           /**
  964            * Returns an integer indicating the position of <code>spot</code>
  965            * in <code>width</code>. The return value will be:
  966            * 0 if < BORDER_DRAG_THICKNESS
  967            * 1 if < CORNER_DRAG_WIDTH
  968            * 2 if >= CORNER_DRAG_WIDTH && < width - BORDER_DRAG_THICKNESS
  969            * 3 if >= width - CORNER_DRAG_WIDTH
  970            * 4 if >= width - BORDER_DRAG_THICKNESS
  971            * 5 otherwise
  972            */
  973           private int calculatePosition(int spot, int width) {
  974               if (spot < BORDER_DRAG_THICKNESS) {
  975                   return 0;
  976               }
  977               if (spot < CORNER_DRAG_WIDTH) {
  978                   return 1;
  979               }
  980               if (spot >= (width - BORDER_DRAG_THICKNESS)) {
  981                   return 4;
  982               }
  983               if (spot >= (width - CORNER_DRAG_WIDTH)) {
  984                   return 3;
  985               }
  986               return 2;
  987           }
  988       }
  989   }

Save This Page
Home » openjdk-7 » javax » swing » plaf » metal » [javadoc | source]