KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > metal > MetalRootPaneUI


1 /*
2  * @(#)MetalRootPaneUI.java 1.20 04/04/27
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.metal;
9
10 import java.awt.event.*;
11 import java.beans.PropertyChangeEvent JavaDoc;
12 import java.beans.PropertyChangeListener JavaDoc;
13 import javax.swing.*;
14 import javax.swing.border.*;
15 import javax.swing.event.*;
16 import javax.swing.plaf.*;
17 import javax.swing.plaf.basic.*;
18 import java.awt.*;
19 import java.io.*;
20 import java.security.*;
21
22 /**
23  * Provides the metal look and feel implementation of <code>RootPaneUI</code>.
24  * <p>
25  * <code>MetalRootPaneUI</code> provides support for the
26  * <code>windowDecorationStyle</code> property of <code>JRootPane</code>.
27  * <code>MetalRootPaneUI</code> does this by way of installing a custom
28  * <code>LayoutManager</code>, a private <code>Component</code> to render
29  * the appropriate widgets, and a private <code>Border</code>. The
30  * <code>LayoutManager</code> is always installed, regardless of the value of
31  * the <code>windowDecorationStyle</code> property, but the
32  * <code>Border</code> and <code>Component</code> are only installed/added if
33  * the <code>windowDecorationStyle</code> is other than
34  * <code>JRootPane.NONE</code>.
35  * <p>
36  * <strong>Warning:</strong>
37  * Serialized objects of this class will not be compatible with
38  * future Swing releases. The current serialization support is
39  * appropriate for short term storage or RMI between applications running
40  * the same version of Swing. As of 1.4, support for long term storage
41  * of all JavaBeans<sup><font size="-2">TM</font></sup>
42  * has been added to the <code>java.beans</code> package.
43  * Please see {@link java.beans.XMLEncoder}.
44  *
45  * @version 1.20 04/27/04
46  * @author Terry Kellerman
47  * @since 1.4
48  */

49 public class MetalRootPaneUI extends BasicRootPaneUI
50 {
51     /**
52      * Keys to lookup borders in defaults table.
53      */

54     private static final String JavaDoc[] borderKeys = new String JavaDoc[] {
55         null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
56         "RootPane.informationDialogBorder",
57         "RootPane.errorDialogBorder", "RootPane.colorChooserDialogBorder",
58         "RootPane.fileChooserDialogBorder", "RootPane.questionDialogBorder",
59         "RootPane.warningDialogBorder"
60     };
61     /**
62      * The amount of space (in pixels) that the cursor is changed on.
63      */

64     private static final int CORNER_DRAG_WIDTH = 16;
65
66     /**
67      * Region from edges that dragging is active from.
68      */

69     private static final int BORDER_DRAG_THICKNESS = 5;
70
71     /**
72      * Window the <code>JRootPane</code> is in.
73      */

74     private Window window;
75
76     /**
77      * <code>JComponent</code> providing window decorations. This will be
78      * null if not providing window decorations.
79      */

80     private JComponent titlePane;
81
82     /**
83      * <code>MouseInputListener</code> that is added to the parent
84      * <code>Window</code> the <code>JRootPane</code> is contained in.
85      */

86     private MouseInputListener mouseInputListener;
87
88     /**
89      * The <code>LayoutManager</code> that is set on the
90      * <code>JRootPane</code>.
91      */

92     private LayoutManager layoutManager;
93
94     /**
95      * <code>LayoutManager</code> of the <code>JRootPane</code> before we
96      * replaced it.
97      */

98     private LayoutManager savedOldLayout;
99
100     /**
101      * <code>JRootPane</code> providing the look and feel for.
102      */

103     private JRootPane root;
104
105     /**
106      * <code>Cursor</code> used to track the cursor set by the user.
107      * This is initially <code>Cursor.DEFAULT_CURSOR</code>.
108      */

109     private Cursor lastCursor =
110             Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
111
112     /**
113      * Creates a UI for a <code>JRootPane</code>.
114      *
115      * @param c the JRootPane the RootPaneUI will be created for
116      * @return the RootPaneUI implementation for the passed in JRootPane
117      */

118     public static ComponentUI createUI(JComponent c) {
119         return new MetalRootPaneUI JavaDoc();
120     }
121
122     /**
123      * Invokes supers implementation of <code>installUI</code> to install
124      * the necessary state onto the passed in <code>JRootPane</code>
125      * to render the metal look and feel implementation of
126      * <code>RootPaneUI</code>. If
127      * the <code>windowDecorationStyle</code> property of the
128      * <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
129      * this will add a custom <code>Component</code> to render the widgets to
130      * <code>JRootPane</code>, as well as installing a custom
131      * <code>Border</code> and <code>LayoutManager</code> on the
132      * <code>JRootPane</code>.
133      *
134      * @param c the JRootPane to install state onto
135      */

136     public void installUI(JComponent c) {
137         super.installUI(c);
138         root = (JRootPane)c;
139         int style = root.getWindowDecorationStyle();
140         if (style != JRootPane.NONE) {
141             installClientDecorations(root);
142         }
143     }
144
145
146     /**
147      * Invokes supers implementation to uninstall any of its state. This will
148      * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
149      * If a <code>Component</code> has been added to the <code>JRootPane</code>
150      * to render the window decoration style, this method will remove it.
151      * Similarly, this will revert the Border and LayoutManager of the
152      * <code>JRootPane</code> to what it was before <code>installUI</code>
153      * was invoked.
154      *
155      * @param c the JRootPane to uninstall state from
156      */

157     public void uninstallUI(JComponent c) {
158         super.uninstallUI(c);
159         uninstallClientDecorations(root);
160
161         layoutManager = null;
162         mouseInputListener = null;
163         root = null;
164     }
165
166     /**
167      * Installs the appropriate <code>Border</code> onto the
168      * <code>JRootPane</code>.
169      */

170     void installBorder(JRootPane root) {
171         int style = root.getWindowDecorationStyle();
172
173         if (style == JRootPane.NONE) {
174             LookAndFeel.uninstallBorder(root);
175         }
176         else {
177             LookAndFeel.installBorder(root, borderKeys[style]);
178         }
179     }
180
181     /**
182      * Removes any border that may have been installed.
183      */

184     private void uninstallBorder(JRootPane root) {
185         LookAndFeel.uninstallBorder(root);
186     }
187
188     /**
189      * Installs the necessary Listeners on the parent <code>Window</code>,
190      * if there is one.
191      * <p>
192      * This takes the parent so that cleanup can be done from
193      * <code>removeNotify</code>, at which point the parent hasn't been
194      * reset yet.
195      *
196      * @param parent The parent of the JRootPane
197      */

198     private void installWindowListeners(JRootPane root, Component parent) {
199         if (parent instanceof Window) {
200             window = (Window)parent;
201         }
202         else {
203             window = SwingUtilities.getWindowAncestor(parent);
204         }
205         if (window != null) {
206             if (mouseInputListener == null) {
207                 mouseInputListener = createWindowMouseInputListener(root);
208             }
209             window.addMouseListener(mouseInputListener);
210             window.addMouseMotionListener(mouseInputListener);
211         }
212     }
213
214     /**
215      * Uninstalls the necessary Listeners on the <code>Window</code> the
216      * Listeners were last installed on.
217      */

218     private void uninstallWindowListeners(JRootPane root) {
219         if (window != null) {
220             window.removeMouseListener(mouseInputListener);
221             window.removeMouseMotionListener(mouseInputListener);
222         }
223     }
224
225     /**
226      * Installs the appropriate LayoutManager on the <code>JRootPane</code>
227      * to render the window decorations.
228      */

229     private void installLayout(JRootPane root) {
230         if (layoutManager == null) {
231             layoutManager = createLayoutManager();
232         }
233         savedOldLayout = root.getLayout();
234         root.setLayout(layoutManager);
235     }
236
237     /**
238      * Uninstalls the previously installed <code>LayoutManager</code>.
239      */

240     private void uninstallLayout(JRootPane root) {
241         if (savedOldLayout != null) {
242             root.setLayout(savedOldLayout);
243             savedOldLayout = null;
244         }
245     }
246
247     /**
248      * Installs the necessary state onto the JRootPane to render client
249      * decorations. This is ONLY invoked if the <code>JRootPane</code>
250      * has a decoration style other than <code>JRootPane.NONE</code>.
251      */

252     private void installClientDecorations(JRootPane root) {
253         installBorder(root);
254
255         JComponent titlePane = createTitlePane(root);
256
257         setTitlePane(root, titlePane);
258         installWindowListeners(root, root.getParent());
259         installLayout(root);
260         if (window != null) {
261             root.revalidate();
262             root.repaint();
263         }
264     }
265
266     /**
267      * Uninstalls any state that <code>installClientDecorations</code> has
268      * installed.
269      * <p>
270      * NOTE: This may be called if you haven't installed client decorations
271      * yet (ie before <code>installClientDecorations</code> has been invoked).
272      */

273     private void uninstallClientDecorations(JRootPane root) {
274         uninstallBorder(root);
275         uninstallWindowListeners(root);
276         setTitlePane(root, null);
277         uninstallLayout(root);
278     // We have to revalidate/repaint root if the style is JRootPane.NONE
279
// only. When we needs to call revalidate/repaint with other styles
280
// the installClientDecorations is always called after this method
281
// imediatly and it will cause the revalidate/repaint at the proper
282
// time.
283
int style = root.getWindowDecorationStyle();
284         if (style == JRootPane.NONE) {
285         root.repaint();
286         root.revalidate();
287     }
288         // Reset the cursor, as we may have changed it to a resize cursor
289
if (window != null) {
290             window.setCursor(Cursor.getPredefinedCursor
291                              (Cursor.DEFAULT_CURSOR));
292         }
293         window = null;
294     }
295
296     /**
297      * Returns the <code>JComponent</code> to render the window decoration
298      * style.
299      */

300     private JComponent createTitlePane(JRootPane root) {
301         return new MetalTitlePane JavaDoc(root, this);
302     }
303
304     /**
305      * Returns a <code>MouseListener</code> that will be added to the
306      * <code>Window</code> containing the <code>JRootPane</code>.
307      */

308     private MouseInputListener createWindowMouseInputListener(JRootPane root) {
309         return new MouseInputHandler();
310     }
311
312     /**
313      * Returns a <code>LayoutManager</code> that will be set on the
314      * <code>JRootPane</code>.
315      */

316     private LayoutManager createLayoutManager() {
317         return new MetalRootLayout();
318     }
319
320     /**
321      * Sets the window title pane -- the JComponent used to provide a plaf a
322      * way to override the native operating system's window title pane with
323      * one whose look and feel are controlled by the plaf. The plaf creates
324      * and sets this value; the default is null, implying a native operating
325      * system window title pane.
326      *
327      * @param content the <code>JComponent</code> to use for the window title pane.
328      */

329     private void setTitlePane(JRootPane root, JComponent titlePane) {
330         JLayeredPane layeredPane = root.getLayeredPane();
331         JComponent oldTitlePane = getTitlePane();
332
333         if (oldTitlePane != null) {
334             oldTitlePane.setVisible(false);
335             layeredPane.remove(oldTitlePane);
336         }
337         if (titlePane != null) {
338             layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
339             titlePane.setVisible(true);
340         }
341         this.titlePane = titlePane;
342     }
343
344     /**
345      * Returns the <code>JComponent</code> rendering the title pane. If this
346      * returns null, it implies there is no need to render window decorations.
347      *
348      * @return the current window title pane, or null
349      * @see #setTitlePane
350      */

351     private JComponent getTitlePane() {
352         return titlePane;
353     }
354
355     /**
356      * Returns the <code>JRootPane</code> we're providing the look and
357      * feel for.
358      */

359     private JRootPane getRootPane() {
360         return root;
361     }
362
363     /**
364      * Invoked when a property changes. <code>MetalRootPaneUI</code> is
365      * primarily interested in events originating from the
366      * <code>JRootPane</code> it has been installed on identifying the
367      * property <code>windowDecorationStyle</code>. If the
368      * <code>windowDecorationStyle</code> has changed to a value other
369      * than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
370      * to the <code>JRootPane</code> to render the window decorations, as well
371      * as installing a <code>Border</code> on the <code>JRootPane</code>.
372      * On the other hand, if the <code>windowDecorationStyle</code> has
373      * changed to <code>JRootPane.NONE</code>, this will remove the
374      * <code>Component</code> that has been added to the <code>JRootPane</code>
375      * as well resetting the Border to what it was before
376      * <code>installUI</code> was invoked.
377      *
378      * @param e A PropertyChangeEvent object describing the event source
379      * and the property that has changed.
380      */

381     public void propertyChange(PropertyChangeEvent JavaDoc e) {
382         super.propertyChange(e);
383         
384         String JavaDoc propertyName = e.getPropertyName();
385         if(propertyName == null) {
386             return;
387         }
388     
389         if(propertyName.equals("windowDecorationStyle")) {
390             JRootPane root = (JRootPane) e.getSource();
391             int style = root.getWindowDecorationStyle();
392
393             // This is potentially more than needs to be done,
394
// but it rarely happens and makes the install/uninstall process
395
// simpler. MetalTitlePane also assumes it will be recreated if
396
// the decoration style changes.
397
uninstallClientDecorations(root);
398             if (style != JRootPane.NONE) {
399                 installClientDecorations(root);
400             }
401         }
402         else if (propertyName.equals("ancestor")) {
403             uninstallWindowListeners(root);
404             if (((JRootPane)e.getSource()).getWindowDecorationStyle() !=
405                                            JRootPane.NONE) {
406                 installWindowListeners(root, root.getParent());
407             }
408         }
409         return;
410     }
411
412     /**
413      * A custom layout manager that is responsible for the layout of
414      * layeredPane, glassPane, menuBar and titlePane, if one has been
415      * installed.
416      */

417     // NOTE: Ideally this would extends JRootPane.RootLayout, but that
418
// would force this to be non-static.
419
private static class MetalRootLayout implements LayoutManager2 {
420         /**
421          * Returns the amount of space the layout would like to have.
422          *
423          * @param the Container for which this layout manager is being used
424          * @return a Dimension object containing the layout's preferred size
425          */

426         public Dimension preferredLayoutSize(Container parent) {
427             Dimension cpd, mbd, tpd;
428             int cpWidth = 0;
429             int cpHeight = 0;
430             int mbWidth = 0;
431             int mbHeight = 0;
432             int tpWidth = 0;
433             int tpHeight = 0;
434             Insets i = parent.getInsets();
435             JRootPane root = (JRootPane) parent;
436     
437             if(root.getContentPane() != null) {
438                 cpd = root.getContentPane().getPreferredSize();
439             } else {
440                 cpd = root.getSize();
441             }
442             if (cpd != null) {
443                 cpWidth = cpd.width;
444                 cpHeight = cpd.height;
445             }
446
447             if(root.getMenuBar() != null) {
448                 mbd = root.getMenuBar().getPreferredSize();
449                 if (mbd != null) {
450                     mbWidth = mbd.width;
451                     mbHeight = mbd.height;
452                 }
453             }
454
455             if (root.getWindowDecorationStyle() != JRootPane.NONE &&
456                      (root.getUI() instanceof MetalRootPaneUI JavaDoc)) {
457                 JComponent titlePane = ((MetalRootPaneUI JavaDoc)root.getUI()).
458                                        getTitlePane();
459                 if (titlePane != null) {
460                     tpd = titlePane.getPreferredSize();
461                     if (tpd != null) {
462                         tpWidth = tpd.width;
463                         tpHeight = tpd.height;
464                     }
465                 }
466             }
467
468             return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
469                                  cpHeight + mbHeight + tpWidth + i.top + i.bottom);
470         }
471
472         /**
473          * Returns the minimum amount of space the layout needs.
474          *
475          * @param the Container for which this layout manager is being used
476          * @return a Dimension object containing the layout's minimum size
477          */

478         public Dimension minimumLayoutSize(Container parent) {
479             Dimension cpd, mbd, tpd;
480             int cpWidth = 0;
481             int cpHeight = 0;
482             int mbWidth = 0;
483             int mbHeight = 0;
484             int tpWidth = 0;
485             int tpHeight = 0;
486             Insets i = parent.getInsets();
487             JRootPane root = (JRootPane) parent;
488         
489             if(root.getContentPane() != null) {
490                 cpd = root.getContentPane().getMinimumSize();
491             } else {
492                 cpd = root.getSize();
493             }
494             if (cpd != null) {
495                 cpWidth = cpd.width;
496                 cpHeight = cpd.height;
497             }
498
499             if(root.getMenuBar() != null) {
500                 mbd = root.getMenuBar().getMinimumSize();
501                 if (mbd != null) {
502                     mbWidth = mbd.width;
503                     mbHeight = mbd.height;
504                 }
505             }
506             if (root.getWindowDecorationStyle() != JRootPane.NONE &&
507                      (root.getUI() instanceof MetalRootPaneUI JavaDoc)) {
508                 JComponent titlePane = ((MetalRootPaneUI JavaDoc)root.getUI()).
509                                        getTitlePane();
510                 if (titlePane != null) {
511                     tpd = titlePane.getMinimumSize();
512                     if (tpd != null) {
513                         tpWidth = tpd.width;
514                         tpHeight = tpd.height;
515                     }
516                 }
517             }
518
519             return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
520                                  cpHeight + mbHeight + tpWidth + i.top + i.bottom);
521         }
522
523         /**
524          * Returns the maximum amount of space the layout can use.
525          *
526          * @param the Container for which this layout manager is being used
527          * @return a Dimension object containing the layout's maximum size
528          */

529         public Dimension maximumLayoutSize(Container target) {
530             Dimension cpd, mbd, tpd;
531             int cpWidth = Integer.MAX_VALUE;
532             int cpHeight = Integer.MAX_VALUE;
533             int mbWidth = Integer.MAX_VALUE;
534             int mbHeight = Integer.MAX_VALUE;
535             int tpWidth = Integer.MAX_VALUE;
536             int tpHeight = Integer.MAX_VALUE;
537             Insets i = target.getInsets();
538             JRootPane root = (JRootPane) target;
539         
540             if(root.getContentPane() != null) {
541                 cpd = root.getContentPane().getMaximumSize();
542                 if (cpd != null) {
543                     cpWidth = cpd.width;
544                     cpHeight = cpd.height;
545                 }
546             }
547
548             if(root.getMenuBar() != null) {
549                 mbd = root.getMenuBar().getMaximumSize();
550                 if (mbd != null) {
551                     mbWidth = mbd.width;
552                     mbHeight = mbd.height;
553                 }
554             }
555
556             if (root.getWindowDecorationStyle() != JRootPane.NONE &&
557                      (root.getUI() instanceof MetalRootPaneUI JavaDoc)) {
558                 JComponent titlePane = ((MetalRootPaneUI JavaDoc)root.getUI()).
559                                        getTitlePane();
560                 if (titlePane != null)
561                 {
562                     tpd = titlePane.getMaximumSize();
563                     if (tpd != null) {
564                         tpWidth = tpd.width;
565                         tpHeight = tpd.height;
566                     }
567                 }
568             }
569
570             int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
571             // Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE
572
// Only will happen if sums to more than 2 billion units. Not likely.
573
if (maxHeight != Integer.MAX_VALUE) {
574                 maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
575             }
576     
577             int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
578             // Similar overflow comment as above
579
if (maxWidth != Integer.MAX_VALUE) {
580                 maxWidth += i.left + i.right;
581             }
582
583             return new Dimension(maxWidth, maxHeight);
584         }
585     
586         /**
587          * Instructs the layout manager to perform the layout for the specified
588          * container.
589          *
590          * @param the Container for which this layout manager is being used
591          */

592         public void layoutContainer(Container parent) {
593             JRootPane root = (JRootPane) parent;
594             Rectangle b = root.getBounds();
595             Insets i = root.getInsets();
596             int nextY = 0;
597             int w = b.width - i.right - i.left;
598             int h = b.height - i.top - i.bottom;
599     
600             if(root.getLayeredPane() != null) {
601                 root.getLayeredPane().setBounds(i.left, i.top, w, h);
602             }
603             if(root.getGlassPane() != null) {
604                 root.getGlassPane().setBounds(i.left, i.top, w, h);
605             }
606             // Note: This is laying out the children in the layeredPane,
607
// technically, these are not our children.
608
if (root.getWindowDecorationStyle() != JRootPane.NONE &&
609                      (root.getUI() instanceof MetalRootPaneUI JavaDoc)) {
610                 JComponent titlePane = ((MetalRootPaneUI JavaDoc)root.getUI()).
611                                        getTitlePane();
612                 if (titlePane != null) {
613                     Dimension tpd = titlePane.getPreferredSize();
614                     if (tpd != null) {
615                         int tpHeight = tpd.height;
616                         titlePane.setBounds(0, 0, w, tpHeight);
617                         nextY += tpHeight;
618                     }
619                 }
620             }
621             if(root.getMenuBar() != null) {
622                 Dimension mbd = root.getMenuBar().getPreferredSize();
623                 root.getMenuBar().setBounds(0, nextY, w, mbd.height);
624                 nextY += mbd.height;
625             }
626             if(root.getContentPane() != null) {
627                 Dimension cpd = root.getContentPane().getPreferredSize();
628                 root.getContentPane().setBounds(0, nextY, w,
629                 h < nextY ? 0 : h - nextY);
630             }
631         }
632     
633         public void addLayoutComponent(String JavaDoc name, Component comp) {}
634         public void removeLayoutComponent(Component comp) {}
635         public void addLayoutComponent(Component comp, Object JavaDoc constraints) {}
636         public float getLayoutAlignmentX(Container target) { return 0.0f; }
637         public float getLayoutAlignmentY(Container target) { return 0.0f; }
638         public void invalidateLayout(Container target) {}
639     }
640
641
642     /**
643      * Maps from positions to cursor type. Refer to calculateCorner and
644      * calculatePosition for details of this.
645      */

646     private static final int[] cursorMapping = new int[]
647     { Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR,
648              Cursor.NE_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
649       Cursor.NW_RESIZE_CURSOR, 0, 0, 0, Cursor.NE_RESIZE_CURSOR,
650       Cursor.W_RESIZE_CURSOR, 0, 0, 0, Cursor.E_RESIZE_CURSOR,
651       Cursor.SW_RESIZE_CURSOR, 0, 0, 0, Cursor.SE_RESIZE_CURSOR,
652       Cursor.SW_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
653              Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR
654     };
655
656     /**
657      * MouseInputHandler is responsible for handling resize/moving of
658      * the Window. It sets the cursor directly on the Window when then
659      * mouse moves over a hot spot.
660      */

661     private class MouseInputHandler implements MouseInputListener {
662         /**
663          * Set to true if the drag operation is moving the window.
664          */

665         private boolean isMovingWindow;
666
667         /**
668          * Used to determine the corner the resize is occuring from.
669          */

670         private int dragCursor;
671
672         /**
673          * X location the mouse went down on for a drag operation.
674          */

675         private int dragOffsetX;
676
677         /**
678          * Y location the mouse went down on for a drag operation.
679          */

680         private int dragOffsetY;
681
682         /**
683          * Width of the window when the drag started.
684          */

685         private int dragWidth;
686
687         /**
688          * Height of the window when the drag started.
689          */

690         private int dragHeight;
691
692         /*
693          * PrivilegedExceptionAction needed by mouseDragged method to
694          * obtain new location of window on screen during the drag.
695          */

696         private final PrivilegedExceptionAction getLocationAction = new PrivilegedExceptionAction(){
697                 public Object JavaDoc run() throws HeadlessException{
698                     return MouseInfo.getPointerInfo().getLocation();
699                 }};
700
701         public void mousePressed(MouseEvent ev) {
702             JRootPane rootPane = getRootPane();
703
704             if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
705                 return;
706             }
707             Point dragWindowOffset = ev.getPoint();
708             Window w = (Window)ev.getSource();
709             if (w != null) {
710                 w.toFront();
711             }
712             Point convertedDragWindowOffset = SwingUtilities.convertPoint(
713                            w, dragWindowOffset, getTitlePane());
714
715             Frame f = null;
716             Dialog d = null;
717
718             if (w instanceof Frame) {
719                 f = (Frame)w;
720             } else if (w instanceof Dialog) {
721                 d = (Dialog)w;
722             }
723
724             int frameState = (f != null) ? f.getExtendedState() : 0;
725
726             if (getTitlePane() != null &&
727                         getTitlePane().contains(convertedDragWindowOffset)) {
728                 if ((f != null && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
729                         || (d != null))
730                         && dragWindowOffset.y >= BORDER_DRAG_THICKNESS
731                         && dragWindowOffset.x >= BORDER_DRAG_THICKNESS
732                         && dragWindowOffset.x < w.getWidth()
733                             - BORDER_DRAG_THICKNESS) {
734                     isMovingWindow = true;
735                     dragOffsetX = dragWindowOffset.x;
736                     dragOffsetY = dragWindowOffset.y;
737                 }
738             }
739             else if (f != null && f.isResizable()
740                     && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
741                     || (d != null && d.isResizable())) {
742                 dragOffsetX = dragWindowOffset.x;
743                 dragOffsetY = dragWindowOffset.y;
744                 dragWidth = w.getWidth();
745                 dragHeight = w.getHeight();
746                 dragCursor = getCursor(calculateCorner(
747                              w, dragWindowOffset.x, dragWindowOffset.y));
748             }
749         }
750
751         public void mouseReleased(MouseEvent ev) {
752             if (dragCursor != 0 && window != null && !window.isValid()) {
753                 // Some Window systems validate as you resize, others won't,
754
// thus the check for validity before repainting.
755
window.validate();
756                 getRootPane().repaint();
757             }
758             isMovingWindow = false;
759             dragCursor = 0;
760         }
761
762         public void mouseMoved(MouseEvent ev) {
763             JRootPane root = getRootPane();
764
765             if (root.getWindowDecorationStyle() == JRootPane.NONE) {
766                 return;
767             }
768
769             Window w = (Window)ev.getSource();
770
771             Frame f = null;
772             Dialog d = null;
773
774             if (w instanceof Frame) {
775                 f = (Frame)w;
776             } else if (w instanceof Dialog) {
777                 d = (Dialog)w;
778             }
779
780             // Update the cursor
781
int cursor = getCursor(calculateCorner(w, ev.getX(), ev.getY()));
782
783             if (cursor != 0 && ((f != null && (f.isResizable() &&
784                     (f.getExtendedState() & Frame.MAXIMIZED_BOTH) == 0))
785                     || (d != null && d.isResizable()))) {
786                 w.setCursor(Cursor.getPredefinedCursor(cursor));
787             }
788             else {
789                 w.setCursor(lastCursor);
790             }
791         }
792
793         private void adjust(Rectangle bounds, Dimension min, int deltaX,
794                             int deltaY, int deltaWidth, int deltaHeight) {
795             bounds.x += deltaX;
796             bounds.y += deltaY;
797             bounds.width += deltaWidth;
798             bounds.height += deltaHeight;
799             if (min != null) {
800                 if (bounds.width < min.width) {
801                     int correction = min.width - bounds.width;
802                     if (deltaX != 0) {
803                         bounds.x -= correction;
804                     }
805                     bounds.width = min.width;
806                 }
807                 if (bounds.height < min.height) {
808                     int correction = min.height - bounds.height;
809                     if (deltaY != 0) {
810                         bounds.y -= correction;
811                     }
812                     bounds.height = min.height;
813                 }
814             }
815         }
816
817         public void mouseDragged(MouseEvent ev) {
818             Window w = (Window)ev.getSource();
819             Point pt = ev.getPoint();
820
821             if (isMovingWindow) {
822                 Point windowPt;
823                 try {
824                     windowPt = (Point) AccessController.doPrivileged(getLocationAction);
825                     windowPt.x = windowPt.x - dragOffsetX;
826                     windowPt.y = windowPt.y - dragOffsetY;
827                     w.setLocation(windowPt);
828                 }catch (PrivilegedActionException e) {
829                 }
830             }
831             else if (dragCursor != 0) {
832                 Rectangle r = w.getBounds();
833                 Rectangle startBounds = new Rectangle(r);
834                 Dimension min = w.getMinimumSize();
835
836                 switch (dragCursor) {
837                 case Cursor.E_RESIZE_CURSOR:
838                     adjust(r, min, 0, 0, pt.x + (dragWidth - dragOffsetX) -
839                            r.width, 0);
840                     break;
841                 case Cursor.S_RESIZE_CURSOR:
842                     adjust(r, min, 0, 0, 0, pt.y + (dragHeight - dragOffsetY) -
843                            r.height);
844                     break;
845                 case Cursor.N_RESIZE_CURSOR:
846                     adjust(r, min, 0, pt.y -dragOffsetY, 0,
847                            -(pt.y - dragOffsetY));
848                     break;
849                 case Cursor.W_RESIZE_CURSOR:
850                     adjust(r, min, pt.x - dragOffsetX, 0,
851                            -(pt.x - dragOffsetX), 0);
852                     break;
853                 case Cursor.NE_RESIZE_CURSOR:
854                     adjust(r, min, 0, pt.y - dragOffsetY,
855                            pt.x + (dragWidth - dragOffsetX) - r.width,
856                            -(pt.y - dragOffsetY));
857                     break;
858                 case Cursor.SE_RESIZE_CURSOR:
859                     adjust(r, min, 0, 0,
860                            pt.x + (dragWidth - dragOffsetX) - r.width,
861                            pt.y + (dragHeight - dragOffsetY) -
862                            r.height);
863                     break;
864                 case Cursor.NW_RESIZE_CURSOR:
865                     adjust(r, min, pt.x - dragOffsetX,
866                            pt.y - dragOffsetY,
867                            -(pt.x - dragOffsetX),
868                            -(pt.y - dragOffsetY));
869                     break;
870                 case Cursor.SW_RESIZE_CURSOR:
871                     adjust(r, min, pt.x - dragOffsetX, 0,
872                            -(pt.x - dragOffsetX),
873                            pt.y + (dragHeight - dragOffsetY) - r.height);
874                     break;
875                 default:
876                     break;
877                 }
878                 if (!r.equals(startBounds)) {
879                     w.setBounds(r);
880                     // Defer repaint/validate on mouseReleased unless dynamic
881
// layout is active.
882
if (Toolkit.getDefaultToolkit().isDynamicLayoutActive()) {
883                         w.validate();
884                         getRootPane().repaint();
885                     }
886                 }
887             }
888         }
889
890         public void mouseEntered(MouseEvent ev) {
891             Window w = (Window)ev.getSource();
892             lastCursor = w.getCursor();
893             mouseMoved(ev);
894         }
895
896         public void mouseExited(MouseEvent ev) {
897             Window w = (Window)ev.getSource();
898             w.setCursor(lastCursor);
899         }
900
901         public void mouseClicked(MouseEvent ev) {
902             Window w = (Window)ev.getSource();
903             Frame f = null;
904
905             if (w instanceof Frame) {
906                 f = (Frame)w;
907             } else {
908                 return;
909             }
910
911             Point convertedPoint = SwingUtilities.convertPoint(
912                            w, ev.getPoint(), getTitlePane());
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             }
983             return 2;
984         }
985     }
986 }
987
Popular Tags