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™ 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™ 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 } |