1 /*
2  * Adapted from MetalRootPaneUI comments below were copied from that class
3  */
4 
5 package com.l2fprod.gui.plaf.skin;
6 
7 import com.l2fprod.util.AccessUtils;
8 
9 import java.awt.*;
10 import java.awt.event.ComponentEvent;
11 import java.awt.event.InputEvent;
12 import java.awt.event.MouseEvent;
13 import java.awt.image.BufferedImage;
14 import java.beans.PropertyChangeEvent;
15 
16 import javax.swing.Icon;
17 import javax.swing.JComponent;
18 import javax.swing.JFrame;
19 import javax.swing.JLayeredPane;
20 import javax.swing.JMenu;
21 import javax.swing.JRootPane;
22 import javax.swing.LookAndFeel;
23 import javax.swing.SwingUtilities;
24 import javax.swing.UIManager;
25 import javax.swing.event.MouseInputListener;
26 import javax.swing.plaf.ColorUIResource;
27 import javax.swing.plaf.ComponentUI;
28 import javax.swing.plaf.basic.BasicRootPaneUI;
29 
30 /**
31  *
32  * Provides the metal look and feel implementation of <code>RootPaneUI</code>.
33  * <p>
34  * <code>MetalRootPaneUI</code> provides support for the <code>windowDecorationStyle</code>
35  * property of <code>JRootPane</code>.<code>MetalRootPaneUI</code> does
36  * this by way of installing a custom <code>LayoutManager</code>, a private
37  * <code>Component</code> to render the appropriate widgets, and a private
38  * <code>Border</code>. The <code>LayoutManager</code> is always
39  * installed, regardless of the value of the <code>windowDecorationStyle</code>
40  * property, but the <code>Border</code> and <code>Component</code> are
41  * only installed/added if the <code>windowDecorationStyle</code> is other
42  * than <code>JRootPane.NONE</code>.
43  * <p>
44  * <strong>Warning:</strong> Serialized objects of this class will not be
45  * compatible with future Swing releases. The current serialization support is
46  * appropriate for short term storage or RMI between applications running the
47  * same version of Swing. As of 1.4, support for long term storage of all
48  * JavaBeans <sup><font size="-2">TM</font></sup> has been added to the
49  * <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
50  *
51  * @version 1.16 02/04/02
52  * @author Terry Kellerman
53  * @since 1.4
54  */
55 public final class SkinRootPaneUI extends BasicRootPaneUI {
56 
57   // TO MAKE THE CODE COMPILE WITH JDK < 1.4
58   public final static int Frame_MAXIMIZED_BOTH =
59     AccessUtils.getIntValue(JFrame.class, "MAXIMIZED_BOTH");
60 
61   public final static int JRootPane_NONE =
62     AccessUtils.getIntValue(JRootPane.class, "NONE");
63 
getExtendedState(Frame p_Frame)64   public static int getExtendedState(Frame p_Frame) {
65     return AccessUtils.getAsInt(p_Frame, "getExtendedState");
66   }
67 
setExtendedState(Frame p_Frame, int p_Value)68   public static void setExtendedState(Frame p_Frame, int p_Value) {
69     AccessUtils.setAsInt(p_Frame, "setExtendedState", p_Value);
70   }
71 
getWindowDecorationStyle(JRootPane p_Pane)72   public static int getWindowDecorationStyle(JRootPane p_Pane) {
73     return AccessUtils.getAsInt(p_Pane, "getWindowDecorationStyle");
74   }
75 
76   private Skin skin = SkinLookAndFeel.getSkin();
77   private Window.FrameWindow title = null;
78 
79   /**
80    * The amount of space (in pixels) that the cursor is changed on.
81    */
82   private static final int CORNER_DRAG_WIDTH = 16;
83 
84   /**
85    * Region from edges that dragging is active from.
86    */
87   private static final int BORDER_DRAG_THICKNESS = 5;
88 
89   /**
90    * Window the <code>JRootPane</code> is in.
91    */
92   private java.awt.Window window;
93 
94   /**
95    * <code>JComponent</code> providing window decorations. This will be null
96    * if not providing window decorations.
97    */
98   private SkinTitlePane titlePane;
99 
100   /**
101    * <code>MouseInputListener</code> that is added to the parent <code>Window</code>
102    * the <code>JRootPane</code> is contained in.
103    */
104   private MouseInputListener mouseInputListener;
105   /**
106    * The <code>LayoutManager</code> that is set on the <code>JRootPane</code>.
107    */
108   private LayoutManager layoutManager;
109 
110   /**
111    * <code>LayoutManager</code> of the <code>JRootPane</code> before we
112    * replaced it.
113    */
114   private LayoutManager savedOldLayout;
115 
116   /**
117    * <code>JRootPane</code> providing the look and feel for.
118    */
119   private JRootPane root;
120 
121   /**
122    * <code>Cursor</code> used to track the cursor set by the user. This is
123    * initially <code>Cursor.DEFAULT_CURSOR</code>.
124    */
125   private Cursor lastCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
126 
127   /**
128    * Creates a UI for a <code>JRootPane</code>.
129    *
130    * @param c the JRootPane the RootPaneUI will be created for
131    * @return the RootPaneUI implementation for the passed in JRootPane
132    */
createUI(JComponent c)133   public static ComponentUI createUI(JComponent c) {
134     return new SkinRootPaneUI();
135   }
136 
translateSource(MouseEvent ev)137   private static java.awt.Window translateSource(MouseEvent ev) {
138     Object source = ev.getSource();
139     java.awt.Window w;
140 
141     if (source.getClass() == SkinTitlePane.class) {
142       SkinTitlePane titleSource = (SkinTitlePane)source;
143       Window.FrameWindow win = (Window.FrameWindow)titleSource.getWindow();
144       w = win.getMainFrame();
145     } else
146       w = (java.awt.Window)ev.getSource();
147     return w;
148   }
149 
150   /**
151    * Invokes supers implementation of <code>installUI</code> to install the
152    * necessary state onto the passed in <code>JRootPane</code> to render the
153    * metal look and feel implementation of <code>RootPaneUI</code>. If the
154    * <code>windowDecorationStyle</code> property of the <code>JRootPane</code>
155    * is other than <code>JRootPane.NONE</code>, this will add a custom
156    * <code>Component</code> to render the widgets to <code>JRootPane</code>,
157    * as well as installing a custom <code>Border</code> and <code>LayoutManager</code>
158    * on the <code>JRootPane</code>.
159    *
160    * @param c the JRootPane to install state onto
161    */
installUI(JComponent c)162   public void installUI(JComponent c) {
163     super.installUI(c);
164     root = (JRootPane)c;
165     int style = getWindowDecorationStyle(root);
166     if (style != JRootPane_NONE) {
167       installClientDecorations(root);
168 
169     }
170     //skin.getFrame().installSkin(c);
171 
172   }
173 
174   /**
175    * Invokes supers implementation to uninstall any of its state. This will
176    * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
177    * If a <code>Component</code> has been added to the <code>JRootPane</code>
178    * to render the window decoration style, this method will remove it.
179    * Similarly, this will revert the Border and LayoutManager of the <code>JRootPane</code>
180    * to what it was before <code>installUI</code> was invoked.
181    *
182    * @param c the JRootPane to uninstall state from
183    */
uninstallUI(JComponent c)184   public void uninstallUI(JComponent c) {
185     super.uninstallUI(c);
186     uninstallClientDecorations(root);
187 
188     layoutManager = null;
189     mouseInputListener = null;
190     root = null;
191   }
192 
193   /**
194    * Installs the appropriate <code>Border</code> onto the <code>JRootPane</code>.
195    */
installBorder(JRootPane root)196   void installBorder(JRootPane root) {
197     int style = getWindowDecorationStyle(root);
198 
199     if (style == JRootPane_NONE) {
200       LookAndFeel.uninstallBorder(root);
201     } else {
202 
203       //LookAndFeel.installBorder(root, borderKeys[style]);
204       skin.getFrame().installSkin(root);
205     }
206   }
207 
208   /**
209    * Removes any border that may have been installed.
210    */
uninstallBorder(JRootPane root)211   private void uninstallBorder(JRootPane root) {
212     LookAndFeel.uninstallBorder(root);
213   }
214 
215   /**
216    * Installs the necessary Listeners on the parent <code>Window</code>, if
217    * there is one.
218    * <p>
219    * This takes the parent so that cleanup can be done from <code>removeNotify</code>,
220    * at which point the parent hasn't been reset yet.
221    *
222    * @param parent The parent of the JRootPane
223    */
installWindowListeners(JRootPane root, Component parent)224   private void installWindowListeners(JRootPane root, Component parent) {
225     if (parent instanceof java.awt.Window) {
226       window = (java.awt.Window)parent;
227     } else {
228       window = SwingUtilities.getWindowAncestor(parent);
229     }
230     if (window != null) {
231 
232       if (mouseInputListener == null) {
233         mouseInputListener = createWindowMouseInputListener(root);
234       }
235 
236       window.addMouseListener(mouseInputListener);
237       window.addMouseMotionListener(mouseInputListener);
238     }
239   }
240 
241   /**
242    * Uninstalls the necessary Listeners on the <code>Window</code> the
243    * Listeners were last installed on.
244    */
uninstallWindowListeners(JRootPane root)245   private void uninstallWindowListeners(JRootPane root) {
246     if (window != null) {
247 
248       window.removeMouseListener(mouseInputListener);
249       window.removeMouseMotionListener(mouseInputListener);
250     }
251   }
252 
253   /**
254    * Installs the appropriate LayoutManager on the <code>JRootPane</code> to
255    * render the window decorations.
256    */
installLayout(JRootPane root)257   private void installLayout(JRootPane root) {
258     if (layoutManager == null) {
259       layoutManager = createLayoutManager();
260     }
261     savedOldLayout = root.getLayout();
262     root.setLayout(layoutManager);
263   }
264 
265   /**
266    * Uninstalls the previously installed <code>LayoutManager</code>.
267    */
uninstallLayout(JRootPane root)268   private void uninstallLayout(JRootPane root) {
269     if (savedOldLayout != null) {
270       root.setLayout(savedOldLayout);
271       savedOldLayout = null;
272     }
273   }
274 
275   /**
276    * Installs the necessary state onto the JRootPane to render client
277    * decorations. This is ONLY invoked if the <code>JRootPane</code> has a
278    * decoration style other than <code>JRootPane.NONE</code>.
279    */
installClientDecorations(JRootPane root)280   private void installClientDecorations(JRootPane root) {
281     installBorder(root);
282 
283     JComponent titlePane = createTitlePane(root);
284 
285     setTitlePane(root, titlePane);
286     installWindowListeners(root, root.getParent());
287     installLayout(root);
288     adjustIconAndBackground();
289     if (window != null) {
290       root.revalidate();
291       root.repaint();
292 
293     }
294   }
295 
adjustIconAndBackground()296   private void adjustIconAndBackground() {
297     if (window != null) {
298 
299       title.setFrame(window);
300 
301       // the titlePane may not have been updated yet as it was created with a
302       // empty FrameWindow so update its actions.
303       titlePane.enableActions();
304       // callback to notify the titlePane the FrameWindow was set
305       titlePane.windowSet();
306 
307       String[] colors = skin.getColors();
308       boolean cont = true;
309       for (int i = 0; i < colors.length && cont; i++) {
310         String string = colors[i];
311         if (string.equalsIgnoreCase("desktop")) {
312           cont = false;
313           try {
314             Color background = new ColorUIResource(Color.decode(colors[i + 1]));
315             window.setBackground(background);
316           } catch (NumberFormatException e) {
317           }
318         }
319       }
320       java.awt.Window target = title.getMainFrame();
321       if (target instanceof JFrame) {
322         JFrame current = (JFrame)target;
323         Icon provided = titlePane.getWindow().getFrameIcon();
324         Icon general = UIManager.getIcon("InternalFrame.icon");
325         Icon toWork = provided;
326         if (provided == null)
327           toWork = general;
328         if (toWork != null) {
329           BufferedImage theIcon =
330             new BufferedImage(
331               toWork.getIconWidth(),
332               toWork.getIconHeight(),
333               BufferedImage.TYPE_INT_ARGB);
334           Graphics graph = theIcon.getGraphics();
335           toWork.paintIcon(null, graph, 0, 0);
336           current.setIconImage(theIcon);
337         }
338       }
339     }
340 
341   }
342 
resetIconAndBackground()343   private void resetIconAndBackground() {
344     if (window != null) {
345 
346       window.setBackground(null);
347       title.setFrame(null);
348     }
349 
350   }
351 
352   /**
353    * Uninstalls any state that <code>installClientDecorations</code> has
354    * installed.
355    * <p>
356    * NOTE: This may be called if you haven't installed client decorations yet
357    * (ie before <code>installClientDecorations</code> has been invoked).
358    */
uninstallClientDecorations(JRootPane root)359   private void uninstallClientDecorations(JRootPane root) {
360     uninstallBorder(root);
361     uninstallWindowListeners(root);
362     setTitlePane(root, null);
363     uninstallLayout(root);
364     resetIconAndBackground();
365     // We have to revalidate/repaint root if the style is JRootPane.NONE
366     // only. When we needs to call revalidate/repaint with other styles
367     // the installClientDecorations is always called after this method
368     // imediatly and it will cause the revalidate/repaint at the proper
369     // time.
370     int style = getWindowDecorationStyle(root);
371     if (style == JRootPane_NONE) {
372       root.repaint();
373       root.revalidate();
374     }
375     // Reset the cursor, as we may have changed it to a resize cursor
376     if (window != null) {
377       window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
378     }
379     window = null;
380   }
381 
382   /**
383    * Returns the <code>JComponent</code> to render the window decoration
384    * style.
385    */
createTitlePane(JRootPane root)386   private JComponent createTitlePane(JRootPane root) {
387     JComponent titlePane =
388       new SkinTitlePane(title = new Window.FrameWindow()) {
389         // overriden to set the popup to not be lightweight. it
390     // resulted in the popupmenu being hidden, not correctly
391     // z-ordered. Using a JWindow does the trick
392   protected JMenu createSystemMenu() {
393         JMenu menu = new JMenu("    ");
394         menu.getPopupMenu().setLightWeightPopupEnabled(false);
395         return menu;
396       }
397     };
398     titlePane.setOpaque(false);
399     return titlePane;
400   }
401 
402   /**
403    * Returns a <code>MouseListener</code> that will be added to the <code>Window</code>
404    * containing the <code>JRootPane</code>.
405    */
createWindowMouseInputListener(JRootPane root)406   private MouseInputListener createWindowMouseInputListener(JRootPane root) {
407     return new MouseInputHandler();
408   }
409 
410   /**
411    * Returns a <code>LayoutManager</code> that will be set on the <code>JRootPane</code>.
412    */
createLayoutManager()413   private LayoutManager createLayoutManager() {
414     return new MetalRootLayout();
415   }
416 
417   /**
418    * Sets the window title pane -- the JComponent used to provide a plaf a way
419    * to override the native operating system's window title pane with one whose
420    * look and feel are controlled by the plaf. The plaf creates and sets this
421    * value; the default is null, implying a native operating system window
422    * title pane.
423    */
setTitlePane(JRootPane root, JComponent titlePane)424   private void setTitlePane(JRootPane root, JComponent titlePane) {
425     JLayeredPane layeredPane = root.getLayeredPane();
426     JComponent oldTitlePane = getTitlePane();
427 
428     if (oldTitlePane != null) {
429       oldTitlePane.setVisible(false);
430       layeredPane.remove(oldTitlePane);
431     }
432     if (titlePane != null) {
433       layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
434       titlePane.setVisible(true);
435     }
436     this.titlePane = (SkinTitlePane)titlePane;
437   }
438 
439   /**
440    * Returns the <code>JComponent</code> rendering the title pane. If this
441    * returns null, it implies there is no need to render window decorations.
442    *
443    * @return the current window title pane, or null
444    * @see #setTitlePane
445    */
getTitlePane()446   private JComponent getTitlePane() {
447     return titlePane;
448   }
449 
getFrameWindow()450   private Window.FrameWindow getFrameWindow() {
451     return title;
452   }
getMainWindow()453   private java.awt.Window getMainWindow() {
454     return window;
455   }
456 
457   /**
458    * Returns the <code>JRootPane</code> we're providing the look and feel
459    * for.
460    */
getRootPane()461   private JRootPane getRootPane() {
462     return root;
463   }
464 
465   /**
466    * Invoked when a property changes. <code>MetalRootPaneUI</code> is
467    * primarily interested in events originating from the <code>JRootPane</code>
468    * it has been installed on identifying the property <code>windowDecorationStyle</code>.
469    * If the <code>windowDecorationStyle</code> has changed to a value other
470    * than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
471    * to the <code>JRootPane</code> to render the window decorations, as well
472    * as installing a <code>Border</code> on the <code>JRootPane</code>. On
473    * the other hand, if the <code>windowDecorationStyle</code> has changed to
474    * <code>JRootPane.NONE</code>, this will remove the <code>Component</code>
475    * that has been added to the <code>JRootPane</code> as well resetting the
476    * Border to what it was before <code>installUI</code> was invoked.
477    *
478    * @param e A PropertyChangeEvent object describing the event source and the
479    *          property that has changed.
480    */
propertyChange(PropertyChangeEvent e)481   public void propertyChange(PropertyChangeEvent e) {
482     super.propertyChange(e);
483 
484     String propertyName = e.getPropertyName();
485     if (propertyName == null) {
486       return;
487     }
488 
489     if (propertyName.equals("windowDecorationStyle")) {
490       JRootPane root = (JRootPane)e.getSource();
491       int style = getWindowDecorationStyle(root);
492 
493       // This is potentially more than needs to be done,
494       // but it rarely happens and makes the install/uninstall process
495       // simpler. MetalTitlePane also assumes it will be recreated if
496       // the decoration style changes.
497       uninstallClientDecorations(root);
498       if (style != JRootPane_NONE) {
499         installClientDecorations(root);
500       }
501     } else if (propertyName.equals("ancestor")) {
502       uninstallWindowListeners(root);
503       if (getWindowDecorationStyle((JRootPane)e.getSource())
504         != JRootPane_NONE) {
505         installWindowListeners(root, root.getParent());
506       }
507     }
508     return;
509   }
510 
adjust( Rectangle bounds, Dimension min, int deltaX, int deltaY, int deltaWidth, int deltaHeight)511   public static void adjust(
512     Rectangle bounds,
513     Dimension min,
514     int deltaX,
515     int deltaY,
516     int deltaWidth,
517     int deltaHeight) {
518     bounds.x += deltaX;
519     bounds.y += deltaY;
520     bounds.width += deltaWidth;
521     bounds.height += deltaHeight;
522     if (min != null) {
523       if (bounds.width < min.width) {
524         int correction = min.width - bounds.width;
525         if (deltaX != 0) {
526           bounds.x -= correction;
527         }
528         bounds.width = min.width;
529       }
530       if (bounds.height < min.height) {
531         int correction = min.height - bounds.height;
532         if (deltaY != 0) {
533           bounds.y -= correction;
534         }
535         bounds.height = min.height;
536       }
537     }
538   }
539 
540   /**
541    * Returns the corner that contains the point <code>x</code>,<code>y</code>,
542    * or -1 if the position doesn't match a corner.
543    */
calculateCorner(Component c, int x, int y)544   public static int calculateCorner(Component c, int x, int y) {
545     int xPosition = calculatePosition(x, c.getWidth());
546     int yPosition = calculatePosition(y, c.getHeight());
547 
548     if (xPosition == -1 || yPosition == -1) {
549       return -1;
550     }
551     return yPosition * 5 + xPosition;
552   }
553 
554   /**
555    * Returns the Cursor to render for the specified corner. This returns 0 if
556    * the corner doesn't map to a valid Cursor
557    */
getCursor(int corner)558   public static int getCursor(int corner) {
559     if (corner == -1) {
560       return 0;
561     }
562     return SkinRootPaneUI.cursorMapping[corner];
563   }
564 
565   /**
566    * Returns an integer indicating the position of <code>spot</code> in
567    * <code>width</code>. The return value will be: 0 if
568    * < BORDER_DRAG_THICKNESS 1 if < CORNER_DRAG_WIDTH 2 if >=
569    * CORNER_DRAG_WIDTH &&< width - BORDER_DRAG_THICKNESS 3 if >= width -
570    * CORNER_DRAG_WIDTH 4 if >= width - BORDER_DRAG_THICKNESS 5 otherwise
571    */
calculatePosition(int spot, int width)572   public static int calculatePosition(int spot, int width) {
573     if (spot < SkinRootPaneUI.BORDER_DRAG_THICKNESS) {
574       return 0;
575     }
576     if (spot < SkinRootPaneUI.CORNER_DRAG_WIDTH) {
577       return 1;
578     }
579     if (spot >= (width - SkinRootPaneUI.BORDER_DRAG_THICKNESS)) {
580       return 4;
581     }
582     if (spot >= (width - SkinRootPaneUI.CORNER_DRAG_WIDTH)) {
583       return 3;
584     }
585     return 2;
586   }
587 
588   /**
589    * A custom layout manager that is responsible for the layout of layeredPane,
590    * glassPane, menuBar and titlePane, if one has been installed.
591    */
592   // NOTE: Ideally this would extends JRootPane.RootLayout, but that
593   //       would force this to be non-static.
594   private static class MetalRootLayout implements LayoutManager2 {
595     /**
596      * Returns the amount of space the layout would like to have.
597      *
598      * @param parent the Container for which this layout manager is being used
599      * @return a Dimension object containing the layout's preferred size
600      */
preferredLayoutSize(Container parent)601     public Dimension preferredLayoutSize(Container parent) {
602       Dimension cpd, mbd, tpd;
603       int cpWidth = 0;
604       int cpHeight = 0;
605       int mbWidth = 0;
606       int mbHeight = 0;
607       int tpWidth = 0;
608       int tpHeight = 0;
609       Insets i = parent.getInsets();
610       JRootPane root = (JRootPane)parent;
611 
612       if (root.getContentPane() != null) {
613         cpd = root.getContentPane().getPreferredSize();
614       } else {
615         cpd = root.getSize();
616       }
617       if (cpd != null) {
618         cpWidth = cpd.width;
619         cpHeight = cpd.height;
620       }
621 
622       if (root.getJMenuBar() != null) {
623         mbd = root.getJMenuBar().getPreferredSize();
624         if (mbd != null) {
625           mbWidth = mbd.width;
626           mbHeight = mbd.height;
627         }
628       }
629 
630       if (getWindowDecorationStyle(root) != JRootPane_NONE
631         && (root.getUI() instanceof SkinRootPaneUI)) {
632         JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane();
633         if (titlePane != null) {
634           tpd = titlePane.getPreferredSize();
635           if (tpd != null) {
636             tpWidth = tpd.width;
637             tpHeight = tpd.height;
638           }
639         }
640       }
641 
642       return new Dimension(
643         Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
644         cpHeight + mbHeight + tpHeight + i.top + i.bottom);
645     }
646 
647     /**
648      * Returns the minimum amount of space the layout needs.
649      *
650      * @param parent the Container for which this layout manager is being used
651      * @return a Dimension object containing the layout's minimum size
652      */
minimumLayoutSize(Container parent)653     public Dimension minimumLayoutSize(Container parent) {
654       Dimension cpd, mbd, tpd;
655       int cpWidth = 0;
656       int cpHeight = 0;
657       int mbWidth = 0;
658       int mbHeight = 0;
659       int tpWidth = 0;
660       int tpHeight = 0;
661       Insets i = parent.getInsets();
662       JRootPane root = (JRootPane)parent;
663 
664       // This code is enabled only if the parent is not a window with
665       // look and feel decorations. Otherwise the look and feel honors
666       // the minimum size and the window can not be resized to a small
667       // size in the case where a component in the component hierarchy
668       // returns a wrong/big minimum size. In such case, the minimum
669       // size will be the one of the title pane.
670       if (!(root.getParent() instanceof java.awt.Window
671         && getWindowDecorationStyle(root) != JRootPane_NONE)) {
672         if (root.getContentPane() != null) {
673           cpd = root.getContentPane().getMinimumSize();
674         } else {
675           cpd = root.getSize();
676         }
677         if (cpd != null) {
678           cpWidth = cpd.width;
679           cpHeight = cpd.height;
680         }
681       }
682 
683       if (root.getJMenuBar() != null) {
684         mbd = root.getJMenuBar().getMinimumSize();
685         if (mbd != null) {
686           mbWidth = mbd.width;
687           mbHeight = mbd.height;
688         }
689       }
690       if (getWindowDecorationStyle(root) != JRootPane_NONE
691         && (root.getUI() instanceof SkinRootPaneUI)) {
692         JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane();
693         if (titlePane != null) {
694           tpd = titlePane.getMinimumSize();
695           if (tpd != null) {
696             tpWidth = tpd.width;
697             tpHeight = tpd.height;
698           }
699         }
700       }
701 
702       // FYI, MetalRootLayout has a bug in the dimension as it uses
703       // tpWidth in the height calculation
704       // Now logged as
705       // http://developer.java.sun.com/developer/bugParade/bugs/4916923.html
706       return new Dimension(
707         Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
708         cpHeight + mbHeight + tpHeight + i.top + i.bottom);
709     }
710 
711     /**
712      * Returns the maximum amount of space the layout can use.
713      *
714      * @param target the Container for which this layout manager is being used
715      * @return a Dimension object containing the layout's maximum size
716      */
maximumLayoutSize(Container target)717     public Dimension maximumLayoutSize(Container target) {
718       Dimension cpd, mbd, tpd;
719       int cpWidth = Integer.MAX_VALUE;
720       int cpHeight = Integer.MAX_VALUE;
721       int mbWidth = Integer.MAX_VALUE;
722       int mbHeight = Integer.MAX_VALUE;
723       int tpWidth = Integer.MAX_VALUE;
724       int tpHeight = Integer.MAX_VALUE;
725       Insets i = target.getInsets();
726       JRootPane root = (JRootPane)target;
727 
728       if (root.getContentPane() != null) {
729         cpd = root.getContentPane().getMaximumSize();
730         if (cpd != null) {
731           cpWidth = cpd.width;
732           cpHeight = cpd.height;
733         }
734       }
735 
736       if (root.getJMenuBar() != null) {
737         mbd = root.getJMenuBar().getMaximumSize();
738         if (mbd != null) {
739           mbWidth = mbd.width;
740           mbHeight = mbd.height;
741         }
742       }
743 
744       if (getWindowDecorationStyle(root) != JRootPane_NONE
745         && (root.getUI() instanceof SkinRootPaneUI)) {
746         JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane();
747         if (titlePane != null) {
748           tpd = titlePane.getMaximumSize();
749           if (tpd != null) {
750             tpWidth = tpd.width;
751             tpHeight = tpd.height;
752           }
753         }
754       }
755 
756       int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
757       // Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE
758       // Only will happen if sums to more than 2 billion units. Not likely.
759       if (maxHeight != Integer.MAX_VALUE) {
760         maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
761       }
762 
763       int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
764       // Similar overflow comment as above
765       if (maxWidth != Integer.MAX_VALUE) {
766         maxWidth += i.left + i.right;
767       }
768 
769       return new Dimension(maxWidth, maxHeight);
770     }
771 
772     /**
773      * Instructs the layout manager to perform the layout for the specified
774      * container.
775      *
776      * @param parent the Container for which this layout manager is being used
777      */
layoutContainer(Container parent)778     public void layoutContainer(Container parent) {
779       JRootPane root = (JRootPane)parent;
780       Rectangle b = root.getBounds();
781       Insets i = root.getInsets();
782       int nextY = 0;
783       int w = b.width - i.right - i.left;
784       int h = b.height - i.top - i.bottom;
785 
786       if (root.getLayeredPane() != null) {
787         root.getLayeredPane().setBounds(i.left, i.top, w, h);
788       }
789       if (root.getGlassPane() != null) {
790         root.getGlassPane().setBounds(i.left, i.top, w, h);
791       }
792       // Note: This is laying out the children in the layeredPane,
793       // technically, these are not our children.
794       if (getWindowDecorationStyle(root) != JRootPane_NONE
795         && (root.getUI() instanceof SkinRootPaneUI)) {
796         JComponent titlePane = ((SkinRootPaneUI)root.getUI()).getTitlePane();
797         if (titlePane != null) {
798           Dimension tpd = titlePane.getPreferredSize();
799           if (tpd != null) {
800             int tpHeight = tpd.height;
801             titlePane.setBounds(0, 0, w, tpHeight);
802             nextY += tpHeight;
803           }
804         }
805       }
806       if (root.getJMenuBar() != null) {
807         Dimension mbd = root.getJMenuBar().getPreferredSize();
808         root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
809         nextY += mbd.height;
810       }
811       if (root.getContentPane() != null) {
812         root.getContentPane().setBounds(0, nextY, w, h < nextY ? 0 : h - nextY);
813       }
814     }
815 
addLayoutComponent(String name, Component comp)816     public void addLayoutComponent(String name, Component comp) {
817     }
removeLayoutComponent(Component comp)818     public void removeLayoutComponent(Component comp) {
819     }
addLayoutComponent(Component comp, Object constraints)820     public void addLayoutComponent(Component comp, Object constraints) {
821     }
getLayoutAlignmentX(Container target)822     public float getLayoutAlignmentX(Container target) {
823       return 0.0f;
824     }
getLayoutAlignmentY(Container target)825     public float getLayoutAlignmentY(Container target) {
826       return 0.0f;
827     }
invalidateLayout(Container target)828     public void invalidateLayout(Container target) {
829     }
830   }
831 
832   /**
833    * Maps from positions to cursor type. Refer to calculateCorner and
834    * calculatePosition for details of this.
835    */
836   private static final int[] cursorMapping =
837     new int[] {
838       Cursor.NW_RESIZE_CURSOR,
839       Cursor.NW_RESIZE_CURSOR,
840       Cursor.N_RESIZE_CURSOR,
841       Cursor.NE_RESIZE_CURSOR,
842       Cursor.NE_RESIZE_CURSOR,
843       Cursor.NW_RESIZE_CURSOR,
844       0,
845       0,
846       0,
847       Cursor.NE_RESIZE_CURSOR,
848       Cursor.W_RESIZE_CURSOR,
849       0,
850       0,
851       0,
852       Cursor.E_RESIZE_CURSOR,
853       Cursor.SW_RESIZE_CURSOR,
854       0,
855       0,
856       0,
857       Cursor.SE_RESIZE_CURSOR,
858       Cursor.SW_RESIZE_CURSOR,
859       Cursor.SW_RESIZE_CURSOR,
860       Cursor.S_RESIZE_CURSOR,
861       Cursor.SE_RESIZE_CURSOR,
862       Cursor.SE_RESIZE_CURSOR };
863 
864   /**
865    * MouseInputHandler is responsible for handling resize/moving of the Window.
866    * It sets the cursor directly on the Window when then mouse moves over a hot
867    * spot.
868    */
869   private class MouseInputHandler implements MouseInputListener {
870     /**
871      * Set to true if the drag operation is moving the window.
872      */
873     private boolean isMovingWindow;
874 
875     /**
876      * Used to determine the corner the resize is occuring from.
877      */
878     private int dragCursor;
879 
880     /**
881      * X location the mouse went down on for a drag operation.
882      */
883     private int dragOffsetX;
884 
885     /**
886      * Y location the mouse went down on for a drag operation.
887      */
888     private int dragOffsetY;
889 
890     /**
891      * Width of the window when the drag started.
892      */
893     private int dragWidth;
894 
895     /**
896      * Height of the window when the drag started.
897      */
898     private int dragHeight;
899 
900     /**
901      * Sometimes mouse entered events generated twice, without proper mouse exit event. If we dont check for this
902      * possibility, this will cause strange mouse icons. For example the resize cursor appears in the whole window.
903      */
904     private boolean mouseAlreadyEntered = false;
905 
mousePressed(MouseEvent ev)906     public void mousePressed(MouseEvent ev) {
907       if (root==null || getWindowDecorationStyle(root) == JRootPane_NONE) {
908         return;
909       }
910       Point dragWindowOffset = ev.getPoint();
911       java.awt.Window w = translateSource(ev);
912       if (w != null) {
913         w.toFront();
914       }
915       Point convertedDragWindowOffset =
916         SwingUtilities.convertPoint(w, dragWindowOffset, getTitlePane());
917 
918       if (getTitlePane() != null
919         && getTitlePane().contains(convertedDragWindowOffset)) {
920         if (!getFrameWindow().isMaximum()
921           && dragWindowOffset.y >= BORDER_DRAG_THICKNESS
922           && dragWindowOffset.x >= BORDER_DRAG_THICKNESS
923           && dragWindowOffset.x < w.getWidth() - BORDER_DRAG_THICKNESS) {
924           isMovingWindow = true;
925           dragOffsetX = dragWindowOffset.x;
926           dragOffsetY = dragWindowOffset.y;
927         }
928       }
929       //this was an else if before but the title panel would not
930       //cause a resize
931       if (getFrameWindow().isResizable()
932         && !getFrameWindow().isShaded()
933         && !getFrameWindow().isMaximum()) {
934         //&& ((frameState & Frame_MAXIMIZED_BOTH) == 0)
935         //|| (d != null && d.isResizable())) {
936         dragOffsetX = dragWindowOffset.x;
937         dragOffsetY = dragWindowOffset.y;
938         dragWidth = w.getWidth();
939         dragHeight = w.getHeight();
940         //System.out.println("Calulation cursor");
941         dragCursor =
942           SkinRootPaneUI.getCursor(
943             SkinRootPaneUI.calculateCorner(
944               w,
945               dragWindowOffset.x,
946               dragWindowOffset.y));
947       }
948     }
949 
mouseReleased(MouseEvent ev)950     public void mouseReleased(MouseEvent ev) {
951       if (dragCursor != 0 && window != null && !window.isValid()) {
952         // Some Window systems validate as you resize, others won't,
953         // thus the check for validity before repainting.
954         window.validate();
955         getRootPane().repaint();
956       }
957       isMovingWindow = false;
958       dragCursor = 0;
959     }
960 
mouseMoved(MouseEvent ev)961     public void mouseMoved(MouseEvent ev) {
962       JRootPane root = getRootPane();
963 
964       if (root!=null && getWindowDecorationStyle(root) == JRootPane_NONE) {
965         return;
966       }
967 
968       java.awt.Window w = translateSource(ev);
969 
970       // Update the cursor
971       int cursor =
972         SkinRootPaneUI.getCursor(
973           SkinRootPaneUI.calculateCorner(w, ev.getX(), ev.getY()));
974 
975       if (cursor != 0
976         && getFrameWindow().isResizable()
977         && !getFrameWindow().isShaded()
978         && !getFrameWindow().isMaximum()) {
979         	w.setCursor(Cursor.getPredefinedCursor(cursor));
980       } else {
981         	w.setCursor(lastCursor);
982       }
983     }
984 
mouseDragged(MouseEvent ev)985     public void mouseDragged(MouseEvent ev) {
986       java.awt.Window w = translateSource(ev);
987       Point pt = ev.getPoint();
988       //			System.out.println("MovingWindow:"+isMovingWindow+"
989       // dragcursor:"+dragCursor);
990       if (isMovingWindow) {
991         Point windowPt = w.getLocationOnScreen();
992 
993         windowPt.x += pt.x - dragOffsetX;
994         windowPt.y += pt.y - dragOffsetY;
995         w.setLocation(windowPt);
996       } else if (dragCursor != 0) {
997         Rectangle r = w.getBounds();
998         Rectangle startBounds = new Rectangle(r);
999         Dimension min = w.getMinimumSize();
1000 
1001         switch (dragCursor) {
1002           case Cursor.E_RESIZE_CURSOR :
1003             SkinRootPaneUI.adjust(
1004               r,
1005               min,
1006               0,
1007               0,
1008               pt.x + (dragWidth - dragOffsetX) - r.width,
1009               0);
1010             break;
1011           case Cursor.S_RESIZE_CURSOR :
1012             SkinRootPaneUI.adjust(
1013               r,
1014               min,
1015               0,
1016               0,
1017               0,
1018               pt.y + (dragHeight - dragOffsetY) - r.height);
1019             break;
1020           case Cursor.N_RESIZE_CURSOR :
1021             SkinRootPaneUI.adjust(
1022               r,
1023               min,
1024               0,
1025               pt.y - dragOffsetY,
1026               0,
1027               - (pt.y - dragOffsetY));
1028             break;
1029           case Cursor.W_RESIZE_CURSOR :
1030             SkinRootPaneUI.adjust(
1031               r,
1032               min,
1033               pt.x - dragOffsetX,
1034               0,
1035               - (pt.x - dragOffsetX),
1036               0);
1037             break;
1038           case Cursor.NE_RESIZE_CURSOR :
1039             SkinRootPaneUI.adjust(
1040               r,
1041               min,
1042               0,
1043               pt.y - dragOffsetY,
1044               pt.x + (dragWidth - dragOffsetX) - r.width,
1045               - (pt.y - dragOffsetY));
1046             break;
1047           case Cursor.SE_RESIZE_CURSOR :
1048             SkinRootPaneUI.adjust(
1049               r,
1050               min,
1051               0,
1052               0,
1053               pt.x + (dragWidth - dragOffsetX) - r.width,
1054               pt.y + (dragHeight - dragOffsetY) - r.height);
1055             break;
1056           case Cursor.NW_RESIZE_CURSOR :
1057             SkinRootPaneUI.adjust(
1058               r,
1059               min,
1060               pt.x - dragOffsetX,
1061               pt.y - dragOffsetY,
1062               - (pt.x - dragOffsetX),
1063               - (pt.y - dragOffsetY));
1064             break;
1065           case Cursor.SW_RESIZE_CURSOR :
1066             SkinRootPaneUI.adjust(
1067               r,
1068               min,
1069               pt.x - dragOffsetX,
1070               0,
1071               - (pt.x - dragOffsetX),
1072               pt.y + (dragHeight - dragOffsetY) - r.height);
1073             break;
1074           default :
1075             break;
1076         }
1077         if (!r.equals(startBounds)) {
1078           if (getFrameWindow().isResizable()) {
1079             w.setBounds(r);
1080             // Defer repaint/validate on mouseReleased unless dynamic
1081             // layout is active.
1082             if (Boolean
1083               .TRUE
1084               .equals(
1085                 AccessUtils.invoke(
1086                   Toolkit.getDefaultToolkit(),
1087                   "isDynamicLayoutActive",
1088                   null,
1089                   null))) {
1090               w.validate();
1091               getRootPane().repaint();
1092 
1093             }
1094             getFrameWindow().dispatchEvent(
1095               new ComponentEvent(
1096                 getMainWindow(),
1097                 ComponentEvent.COMPONENT_RESIZED));
1098           }
1099         }
1100       }
1101     }
1102 
mouseEntered(MouseEvent ev)1103     public void mouseEntered(MouseEvent ev) {
1104       java.awt.Window w = translateSource(ev);
1105       if (!mouseAlreadyEntered) {
1106           lastCursor = w.getCursor();
1107       }
1108       mouseAlreadyEntered = true;
1109       mouseMoved(ev);
1110     }
1111 
mouseExited(MouseEvent ev)1112     public void mouseExited(MouseEvent ev) {
1113       java.awt.Window w = translateSource(ev);
1114       mouseAlreadyEntered = false;
1115       w.setCursor(lastCursor);
1116     }
1117 
mouseClicked(MouseEvent ev)1118     public void mouseClicked(MouseEvent ev) {
1119       java.awt.Window w = translateSource(ev);
1120       Frame f;
1121 
1122       if (w instanceof Frame) {
1123         f = (Frame)w;
1124       } else {
1125         return;
1126       }
1127 
1128       Point convertedPoint =
1129         SwingUtilities.convertPoint(w, ev.getPoint(), getTitlePane());
1130 
1131       int state = getExtendedState(f);
1132       if (getTitlePane() != null && getTitlePane().contains(convertedPoint)) {
1133         if ((ev.getClickCount() % 2) == 0
1134           && ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
1135           if (f.isResizable()) {
1136             if ((state & Frame_MAXIMIZED_BOTH) != 0) {
1137               setExtendedState(f, state & ~Frame_MAXIMIZED_BOTH);
1138             } else {
1139               setExtendedState(f, state | Frame_MAXIMIZED_BOTH);
1140             }
1141             return;
1142           }
1143         }
1144       }
1145     }
1146 
1147   }
1148 }
1149