001: /*
002: * SmoothGradientRootPaneUI.java
003: *
004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package org.underworldlabs.swing.plaf.smoothgradient;
023:
024: import java.awt.Component;
025: import java.awt.Container;
026: import java.awt.Cursor;
027: import java.awt.Dialog;
028: import java.awt.Dimension;
029: import java.awt.Frame;
030: import java.awt.Insets;
031: import java.awt.LayoutManager;
032: import java.awt.LayoutManager2;
033: import java.awt.Point;
034: import java.awt.Rectangle;
035: import java.awt.Toolkit;
036: import java.awt.Window;
037: import java.awt.event.InputEvent;
038: import java.awt.event.MouseEvent;
039:
040: import java.beans.PropertyChangeEvent;
041:
042: import javax.swing.JComponent;
043: import javax.swing.JLayeredPane;
044: import javax.swing.JRootPane;
045: import javax.swing.LookAndFeel;
046: import javax.swing.SwingUtilities;
047: import javax.swing.event.MouseInputListener;
048: import javax.swing.plaf.ComponentUI;
049: import javax.swing.plaf.basic.BasicRootPaneUI;
050:
051: // Modified to inlcude the PolishedTitlePane instead of MetalTitlePane.
052: // Except for those references, all other code is the same as for MetalRootPaneUI
053: /* ----------------------------------------------------------
054: * CVS NOTE: Changes to the CVS repository prior to the
055: * release of version 3.0.0beta1 has meant a
056: * resetting of CVS revision numbers.
057: * ----------------------------------------------------------
058: */
059:
060: /**
061: *
062: * @author Takis Diakoumis
063: * @version $Revision: 1.4 $
064: * @date $Date: 2006/05/14 06:56:07 $
065: */
066: public class SmoothGradientRootPaneUI extends BasicRootPaneUI {
067:
068: /** Keys to lookup borders in defaults table. */
069: private static final String[] borderKeys = new String[] { null,
070: "RootPane.frameBorder", "RootPane.plainDialogBorder",
071: "RootPane.informationDialogBorder",
072: "RootPane.errorDialogBorder",
073: "RootPane.colorChooserDialogBorder",
074: "RootPane.fileChooserDialogBorder",
075: "RootPane.questionDialogBorder",
076: "RootPane.warningDialogBorder" };
077:
078: /** The amount of space (in pixels) that the cursor is changed on. */
079: private static final int CORNER_DRAG_WIDTH = 16;
080:
081: /** Region from edges that dragging is active from. */
082: private static final int BORDER_DRAG_THICKNESS = 5;
083:
084: /** Window the <code>JRootPane</code> is in. */
085: private Window window;
086:
087: /** <code>JComponent</code> providing window decorations. This will be
088: * null if not providing window decorations. */
089: private JComponent titlePane;
090:
091: /** <code>MouseInputListener</code> that is added to the parent
092: * <code>Window</code> the <code>JRootPane</code> is contained in. */
093: private MouseInputListener mouseInputListener;
094:
095: /** The <code>LayoutManager</code> that is set on the <code>JRootPane</code>. */
096: private LayoutManager layoutManager;
097:
098: /** <code>LayoutManager</code> of the <code>JRootPane</code> before we replaced it. */
099: private LayoutManager savedOldLayout;
100:
101: /** <code>JRootPane</code> providing the look and feel for. */
102: private JRootPane root;
103:
104: /** <code>Cursor</code> used to track the cursor set by the user.
105: * This is initially <code>Cursor.DEFAULT_CURSOR</code>. */
106: private Cursor lastCursor = Cursor
107: .getPredefinedCursor(Cursor.DEFAULT_CURSOR);
108:
109: /**
110: * Creates a UI for a <code>JRootPane</code>.
111: *
112: * @param c the JRootPane the RootPaneUI will be created for
113: * @return the RootPaneUI implementation for the passed in JRootPane
114: */
115: public static ComponentUI createUI(JComponent c) {
116: return new SmoothGradientRootPaneUI();
117: }
118:
119: /**
120: * Invokes supers implementation of <code>installUI</code> to install
121: * the necessary state onto the passed in <code>JRootPane</code>
122: * to render the metal look and feel implementation of
123: * <code>RootPaneUI</code>. If
124: * the <code>windowDecorationStyle</code> property of the
125: * <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
126: * this will add a custom <code>Component</code> to render the widgets to
127: * <code>JRootPane</code>, as well as installing a custom
128: * <code>Border</code> and <code>LayoutManager</code> on the
129: * <code>JRootPane</code>.
130: *
131: * @param c the JRootPane to install state onto
132: */
133: public void installUI(JComponent c) {
134: super .installUI(c);
135: root = (JRootPane) c;
136: int style = root.getWindowDecorationStyle();
137: if (style != JRootPane.NONE) {
138: installClientDecorations(root);
139: }
140: }
141:
142: /**
143: * Invokes supers implementation to uninstall any of its state. This will
144: * also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
145: * If a <code>Component</code> has been added to the <code>JRootPane</code>
146: * to render the window decoration style, this method will remove it.
147: * Similarly, this will revert the Border and LayoutManager of the
148: * <code>JRootPane</code> to what it was before <code>installUI</code>
149: * was invoked.
150: *
151: * @param c the JRootPane to uninstall state from
152: */
153: public void uninstallUI(JComponent c) {
154: super .uninstallUI(c);
155: uninstallClientDecorations(root);
156:
157: layoutManager = null;
158: mouseInputListener = null;
159: root = null;
160: }
161:
162: /**
163: * Installs the appropriate <code>Border</code> onto the
164: * <code>JRootPane</code>.
165: */
166: void installBorder(JRootPane root) {
167: int style = root.getWindowDecorationStyle();
168:
169: if (style == JRootPane.NONE) {
170: LookAndFeel.uninstallBorder(root);
171: } else {
172: LookAndFeel.installBorder(root, borderKeys[style]);
173: }
174: }
175:
176: /**
177: * Removes any border that may have been installed.
178: */
179: private void uninstallBorder(JRootPane root) {
180: LookAndFeel.uninstallBorder(root);
181: }
182:
183: /**
184: * Installs the necessary Listeners on the parent <code>Window</code>,
185: * if there is one.
186: * <p>
187: * This takes the parent so that cleanup can be done from
188: * <code>removeNotify</code>, at which point the parent hasn't been
189: * reset yet.
190: *
191: * @param parent The parent of the JRootPane
192: */
193: private void installWindowListeners(JRootPane root, Component parent) {
194: if (parent instanceof Window) {
195: window = (Window) parent;
196: } else {
197: window = SwingUtilities.getWindowAncestor(parent);
198: }
199: if (window != null) {
200: if (mouseInputListener == null) {
201: mouseInputListener = createWindowMouseInputListener(root);
202: }
203: window.addMouseListener(mouseInputListener);
204: window.addMouseMotionListener(mouseInputListener);
205: }
206: }
207:
208: /**
209: * Uninstalls the necessary Listeners on the <code>Window</code> the
210: * Listeners were last installed on.
211: */
212: private void uninstallWindowListeners(JRootPane root) {
213: if (window != null) {
214: window.removeMouseListener(mouseInputListener);
215: window.removeMouseMotionListener(mouseInputListener);
216: }
217: }
218:
219: /**
220: * Installs the appropriate LayoutManager on the <code>JRootPane</code>
221: * to render the window decorations.
222: */
223: private void installLayout(JRootPane root) {
224: if (layoutManager == null) {
225: layoutManager = createLayoutManager();
226: }
227: savedOldLayout = root.getLayout();
228: root.setLayout(layoutManager);
229: }
230:
231: /**
232: * Uninstalls the previously installed <code>LayoutManager</code>.
233: */
234: private void uninstallLayout(JRootPane root) {
235: if (savedOldLayout != null) {
236: root.setLayout(savedOldLayout);
237: savedOldLayout = null;
238: }
239: }
240:
241: /**
242: * Installs the necessary state onto the JRootPane to render client
243: * decorations. This is ONLY invoked if the <code>JRootPane</code>
244: * has a decoration style other than <code>JRootPane.NONE</code>.
245: */
246: private void installClientDecorations(JRootPane root) {
247: installBorder(root);
248:
249: JComponent titlePane = createTitlePane(root);
250:
251: setTitlePane(root, titlePane);
252: installWindowListeners(root, root.getParent());
253: installLayout(root);
254: if (window != null) {
255: root.revalidate();
256: root.repaint();
257: }
258: }
259:
260: /**
261: * Uninstalls any state that <code>installClientDecorations</code> has
262: * installed.
263: * <p>
264: * NOTE: This may be called if you haven't installed client decorations
265: * yet (ie before <code>installClientDecorations</code> has been invoked).
266: */
267: private void uninstallClientDecorations(JRootPane root) {
268: uninstallBorder(root);
269: uninstallWindowListeners(root);
270: setTitlePane(root, null);
271: uninstallLayout(root);
272: // We have to revalidate/repaint root if the style is JRootPane.NONE
273: // only. When we needs to call revalidate/repaint with other styles
274: // the installClientDecorations is always called after this method
275: // imediatly and it will cause the revalidate/repaint at the proper
276: // time.
277: int style = root.getWindowDecorationStyle();
278: if (style == JRootPane.NONE) {
279: root.repaint();
280: root.revalidate();
281: }
282: // Reset the cursor, as we may have changed it to a resize cursor
283: if (window != null) {
284: window.setCursor(Cursor
285: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
286: }
287: window = null;
288: }
289:
290: /**
291: * Returns the <code>JComponent</code> to render the window decoration
292: * style.
293: */
294: private JComponent createTitlePane(JRootPane root) {
295: return new SmoothGradientTitlePane(root, this );
296: }
297:
298: /**
299: * Returns a <code>MouseListener</code> that will be added to the
300: * <code>Window</code> containing the <code>JRootPane</code>.
301: */
302: private MouseInputListener createWindowMouseInputListener(
303: JRootPane root) {
304: return new MouseInputHandler();
305: }
306:
307: /**
308: * Returns a <code>LayoutManager</code> that will be set on the
309: * <code>JRootPane</code>.
310: */
311: private LayoutManager createLayoutManager() {
312: return new MetalRootLayout();
313: }
314:
315: /**
316: * Sets the window title pane -- the JComponent used to provide a plaf a
317: * way to override the native operating system's window title pane with
318: * one whose look and feel are controlled by the plaf. The plaf creates
319: * and sets this value; the default is null, implying a native operating
320: * system window title pane.
321: *
322: * @param content the <code>JComponent</code> to use for the window title pane.
323: */
324: private void setTitlePane(JRootPane root, JComponent titlePane) {
325: JLayeredPane layeredPane = root.getLayeredPane();
326: JComponent oldTitlePane = getTitlePane();
327:
328: if (oldTitlePane != null) {
329: oldTitlePane.setVisible(false);
330: layeredPane.remove(oldTitlePane);
331: }
332: if (titlePane != null) {
333: layeredPane
334: .add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
335: titlePane.setVisible(true);
336: }
337: this .titlePane = titlePane;
338: }
339:
340: /**
341: * Returns the <code>JComponent</code> rendering the title pane. If this
342: * returns null, it implies there is no need to render window decorations.
343: *
344: * @return the current window title pane, or null
345: * @see #setTitlePane
346: */
347: private JComponent getTitlePane() {
348: return titlePane;
349: }
350:
351: /**
352: * Returns the <code>JRootPane</code> we're providing the look and
353: * feel for.
354: */
355: private JRootPane getRootPane() {
356: return root;
357: }
358:
359: /**
360: * Invoked when a property changes. <code>PolishedRootPaneUI</code> is
361: * primarily interested in events originating from the
362: * <code>JRootPane</code> it has been installed on identifying the
363: * property <code>windowDecorationStyle</code>. If the
364: * <code>windowDecorationStyle</code> has changed to a value other
365: * than <code>JRootPane.NONE</code>, this will add a <code>Component</code>
366: * to the <code>JRootPane</code> to render the window decorations, as well
367: * as installing a <code>Border</code> on the <code>JRootPane</code>.
368: * On the other hand, if the <code>windowDecorationStyle</code> has
369: * changed to <code>JRootPane.NONE</code>, this will remove the
370: * <code>Component</code> that has been added to the <code>JRootPane</code>
371: * as well resetting the Border to what it was before
372: * <code>installUI</code> was invoked.
373: *
374: * @param e A PropertyChangeEvent object describing the event source
375: * and the property that has changed.
376: */
377: public void propertyChange(PropertyChangeEvent e) {
378: super .propertyChange(e);
379:
380: String propertyName = e.getPropertyName();
381: if (propertyName == null) {
382: return;
383: }
384:
385: if (propertyName.equals("windowDecorationStyle")) {
386: JRootPane root = (JRootPane) e.getSource();
387: int style = root.getWindowDecorationStyle();
388:
389: // This is potentially more than needs to be done,
390: // but it rarely happens and makes the install/uninstall process
391: // simpler. MetalTitlePane also assumes it will be recreated if
392: // the decoration style changes.
393: uninstallClientDecorations(root);
394: if (style != JRootPane.NONE) {
395: installClientDecorations(root);
396: }
397: } else if (propertyName.equals("ancestor")) {
398: uninstallWindowListeners(root);
399: if (((JRootPane) e.getSource()).getWindowDecorationStyle() != JRootPane.NONE) {
400: installWindowListeners(root, root.getParent());
401: }
402: }
403: return;
404: }
405:
406: /**
407: * A custom layout manager that is responsible for the layout of
408: * layeredPane, glassPane, menuBar and titlePane, if one has been
409: * installed.
410: */
411: // NOTE: Ideally this would extends JRootPane.RootLayout, but that
412: // would force this to be non-static.
413: private static class MetalRootLayout implements LayoutManager2 {
414: /**
415: * Returns the amount of space the layout would like to have.
416: *
417: * @param the Container for which this layout manager is being used
418: * @return a Dimension object containing the layout's preferred size
419: */
420: public Dimension preferredLayoutSize(Container parent) {
421: Dimension cpd, mbd, tpd;
422: int cpWidth = 0;
423: int cpHeight = 0;
424: int mbWidth = 0;
425: int mbHeight = 0;
426: int tpWidth = 0;
427: int tpHeight = 0;
428: Insets i = parent.getInsets();
429: JRootPane root = (JRootPane) parent;
430:
431: if (root.getContentPane() != null) {
432: cpd = root.getContentPane().getPreferredSize();
433: } else {
434: cpd = root.getSize();
435: }
436: if (cpd != null) {
437: cpWidth = cpd.width;
438: cpHeight = cpd.height;
439: }
440:
441: if (root.getJMenuBar() != null) {
442: mbd = root.getJMenuBar().getPreferredSize();
443: if (mbd != null) {
444: mbWidth = mbd.width;
445: mbHeight = mbd.height;
446: }
447: }
448:
449: if (root.getWindowDecorationStyle() != JRootPane.NONE
450: && (root.getUI() instanceof SmoothGradientRootPaneUI)) {
451: JComponent titlePane = ((SmoothGradientRootPaneUI) root
452: .getUI()).getTitlePane();
453: if (titlePane != null) {
454: tpd = titlePane.getPreferredSize();
455: if (tpd != null) {
456: tpWidth = tpd.width;
457: tpHeight = tpd.height;
458: }
459: }
460: }
461:
462: return new Dimension(Math.max(Math.max(cpWidth, mbWidth),
463: tpWidth)
464: + i.left + i.right, cpHeight + mbHeight + tpWidth
465: + i.top + i.bottom);
466: }
467:
468: /**
469: * Returns the minimum amount of space the layout needs.
470: *
471: * @param the Container for which this layout manager is being used
472: * @return a Dimension object containing the layout's minimum size
473: */
474: public Dimension minimumLayoutSize(Container parent) {
475: Dimension cpd, mbd, tpd;
476: int cpWidth = 0;
477: int cpHeight = 0;
478: int mbWidth = 0;
479: int mbHeight = 0;
480: int tpWidth = 0;
481: int tpHeight = 0;
482: Insets i = parent.getInsets();
483: JRootPane root = (JRootPane) parent;
484:
485: if (root.getContentPane() != null) {
486: cpd = root.getContentPane().getMinimumSize();
487: } else {
488: cpd = root.getSize();
489: }
490: if (cpd != null) {
491: cpWidth = cpd.width;
492: cpHeight = cpd.height;
493: }
494:
495: if (root.getJMenuBar() != null) {
496: mbd = root.getJMenuBar().getMinimumSize();
497: if (mbd != null) {
498: mbWidth = mbd.width;
499: mbHeight = mbd.height;
500: }
501: }
502: if (root.getWindowDecorationStyle() != JRootPane.NONE
503: && (root.getUI() instanceof SmoothGradientRootPaneUI)) {
504: JComponent titlePane = ((SmoothGradientRootPaneUI) root
505: .getUI()).getTitlePane();
506: if (titlePane != null) {
507: tpd = titlePane.getMinimumSize();
508: if (tpd != null) {
509: tpWidth = tpd.width;
510: tpHeight = tpd.height;
511: }
512: }
513: }
514:
515: return new Dimension(Math.max(Math.max(cpWidth, mbWidth),
516: tpWidth)
517: + i.left + i.right, cpHeight + mbHeight + tpWidth
518: + i.top + i.bottom);
519: }
520:
521: /**
522: * Returns the maximum amount of space the layout can use.
523: *
524: * @param the Container for which this layout manager is being used
525: * @return a Dimension object containing the layout's maximum size
526: */
527: public Dimension maximumLayoutSize(Container target) {
528: Dimension cpd, mbd, tpd;
529: int cpWidth = Integer.MAX_VALUE;
530: int cpHeight = Integer.MAX_VALUE;
531: int mbWidth = Integer.MAX_VALUE;
532: int mbHeight = Integer.MAX_VALUE;
533: int tpWidth = Integer.MAX_VALUE;
534: int tpHeight = Integer.MAX_VALUE;
535: Insets i = target.getInsets();
536: JRootPane root = (JRootPane) target;
537:
538: if (root.getContentPane() != null) {
539: cpd = root.getContentPane().getMaximumSize();
540: if (cpd != null) {
541: cpWidth = cpd.width;
542: cpHeight = cpd.height;
543: }
544: }
545:
546: if (root.getJMenuBar() != null) {
547: mbd = root.getJMenuBar().getMaximumSize();
548: if (mbd != null) {
549: mbWidth = mbd.width;
550: mbHeight = mbd.height;
551: }
552: }
553:
554: if (root.getWindowDecorationStyle() != JRootPane.NONE
555: && (root.getUI() instanceof SmoothGradientRootPaneUI)) {
556: JComponent titlePane = ((SmoothGradientRootPaneUI) root
557: .getUI()).getTitlePane();
558: if (titlePane != null) {
559: tpd = titlePane.getMaximumSize();
560: if (tpd != null) {
561: tpWidth = tpd.width;
562: tpHeight = tpd.height;
563: }
564: }
565: }
566:
567: int maxHeight = Math.max(Math.max(cpHeight, mbHeight),
568: tpHeight);
569: // Only overflows if 3 real non-MAX_VALUE heights, sum to > MAX_VALUE
570: // Only will happen if sums to more than 2 billion units. Not likely.
571: if (maxHeight != Integer.MAX_VALUE) {
572: maxHeight = cpHeight + mbHeight + tpHeight + i.top
573: + i.bottom;
574: }
575:
576: int maxWidth = Math
577: .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 SmoothGradientRootPaneUI)) {
610: JComponent titlePane = ((SmoothGradientRootPaneUI) root
611: .getUI()).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.getJMenuBar() != null) {
622: Dimension mbd = root.getJMenuBar().getPreferredSize();
623: root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
624: nextY += mbd.height;
625: }
626: if (root.getContentPane() != null) {
627: Dimension cpd = root.getContentPane()
628: .getPreferredSize();
629: root.getContentPane().setBounds(0, nextY, w,
630: h < nextY ? 0 : h - nextY);
631: }
632: }
633:
634: public void addLayoutComponent(String name, Component comp) {
635: }
636:
637: public void removeLayoutComponent(Component comp) {
638: }
639:
640: public void addLayoutComponent(Component comp,
641: Object constraints) {
642: }
643:
644: public float getLayoutAlignmentX(Container target) {
645: return 0.0f;
646: }
647:
648: public float getLayoutAlignmentY(Container target) {
649: return 0.0f;
650: }
651:
652: public void invalidateLayout(Container target) {
653: }
654: }
655:
656: /**
657: * Maps from positions to cursor type. Refer to calculateCorner and
658: * calculatePosition for details of this.
659: */
660: private static final int[] cursorMapping = new int[] {
661: Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR,
662: Cursor.N_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
663: Cursor.NE_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, 0, 0, 0,
664: Cursor.NE_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, 0, 0, 0,
665: Cursor.E_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, 0, 0, 0,
666: Cursor.SE_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR,
667: Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
668: Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR };
669:
670: /**
671: * MouseInputHandler is responsible for handling resize/moving of
672: * the Window. It sets the cursor directly on the Window when then
673: * mouse moves over a hot spot.
674: */
675: private class MouseInputHandler implements MouseInputListener {
676: /**
677: * Set to true if the drag operation is moving the window.
678: */
679: private boolean isMovingWindow;
680:
681: /**
682: * Used to determine the corner the resize is occuring from.
683: */
684: private int dragCursor;
685:
686: /**
687: * X location the mouse went down on for a drag operation.
688: */
689: private int dragOffsetX;
690:
691: /**
692: * Y location the mouse went down on for a drag operation.
693: */
694: private int dragOffsetY;
695:
696: /**
697: * Width of the window when the drag started.
698: */
699: private int dragWidth;
700:
701: /**
702: * Height of the window when the drag started.
703: */
704: private int dragHeight;
705:
706: public void mousePressed(MouseEvent ev) {
707: JRootPane rootPane = getRootPane();
708:
709: if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
710: return;
711: }
712: Point dragWindowOffset = ev.getPoint();
713: Window w = (Window) ev.getSource();
714: if (w != null) {
715: w.toFront();
716: }
717: Point convertedDragWindowOffset = SwingUtilities
718: .convertPoint(w, dragWindowOffset, getTitlePane());
719:
720: Frame f = null;
721: Dialog d = null;
722:
723: if (w instanceof Frame) {
724: f = (Frame) w;
725: } else if (w instanceof Dialog) {
726: d = (Dialog) w;
727: }
728:
729: int frameState = (f != null) ? f.getExtendedState() : 0;
730:
731: if (getTitlePane() != null
732: && getTitlePane().contains(
733: convertedDragWindowOffset)) {
734: if ((f != null
735: && ((frameState & Frame.MAXIMIZED_BOTH) == 0) || (d != null))
736: && dragWindowOffset.y >= BORDER_DRAG_THICKNESS
737: && dragWindowOffset.x >= BORDER_DRAG_THICKNESS
738: && dragWindowOffset.x < w.getWidth()
739: - BORDER_DRAG_THICKNESS) {
740: isMovingWindow = true;
741: dragOffsetX = dragWindowOffset.x;
742: dragOffsetY = dragWindowOffset.y;
743: }
744: } else if (f != null && f.isResizable()
745: && ((frameState & Frame.MAXIMIZED_BOTH) == 0)
746: || (d != null && d.isResizable())) {
747: dragOffsetX = dragWindowOffset.x;
748: dragOffsetY = dragWindowOffset.y;
749: dragWidth = w.getWidth();
750: dragHeight = w.getHeight();
751: dragCursor = getCursor(calculateCorner(w,
752: dragWindowOffset.x, dragWindowOffset.y));
753: }
754: }
755:
756: public void mouseReleased(MouseEvent ev) {
757: if (dragCursor != 0 && window != null && !window.isValid()) {
758: // Some Window systems validate as you resize, others won't,
759: // thus the check for validity before repainting.
760: window.validate();
761: getRootPane().repaint();
762: }
763: isMovingWindow = false;
764: dragCursor = 0;
765: }
766:
767: public void mouseMoved(MouseEvent ev) {
768: JRootPane root = getRootPane();
769:
770: if (root.getWindowDecorationStyle() == JRootPane.NONE) {
771: return;
772: }
773:
774: Window w = (Window) ev.getSource();
775:
776: Frame f = null;
777: Dialog d = null;
778:
779: if (w instanceof Frame) {
780: f = (Frame) w;
781: } else if (w instanceof Dialog) {
782: d = (Dialog) w;
783: }
784:
785: // Update the cursor
786: int cursor = getCursor(calculateCorner(w, ev.getX(), ev
787: .getY()));
788:
789: if (cursor != 0
790: && ((f != null && (f.isResizable() && (f
791: .getExtendedState() & Frame.MAXIMIZED_BOTH) == 0)) || (d != null && d
792: .isResizable()))) {
793: w.setCursor(Cursor.getPredefinedCursor(cursor));
794: } else {
795: w.setCursor(lastCursor);
796: }
797: }
798:
799: private void adjust(Rectangle bounds, Dimension min,
800: int deltaX, int deltaY, int deltaWidth, int deltaHeight) {
801: bounds.x += deltaX;
802: bounds.y += deltaY;
803: bounds.width += deltaWidth;
804: bounds.height += deltaHeight;
805: if (min != null) {
806: if (bounds.width < min.width) {
807: int correction = min.width - bounds.width;
808: if (deltaX != 0) {
809: bounds.x -= correction;
810: }
811: bounds.width = min.width;
812: }
813: if (bounds.height < min.height) {
814: int correction = min.height - bounds.height;
815: if (deltaY != 0) {
816: bounds.y -= correction;
817: }
818: bounds.height = min.height;
819: }
820: }
821: }
822:
823: public void mouseDragged(MouseEvent ev) {
824: Window w = (Window) ev.getSource();
825: Point pt = ev.getPoint();
826:
827: if (isMovingWindow) {
828: Point windowPt = w.getLocationOnScreen();
829:
830: windowPt.x += pt.x - dragOffsetX;
831: windowPt.y += pt.y - dragOffsetY;
832: w.setLocation(windowPt);
833: } else if (dragCursor != 0) {
834: Rectangle r = w.getBounds();
835: Rectangle startBounds = new Rectangle(r);
836: Dimension min = w.getMinimumSize();
837:
838: switch (dragCursor) {
839: case Cursor.E_RESIZE_CURSOR:
840: adjust(r, min, 0, 0, pt.x
841: + (dragWidth - dragOffsetX) - r.width, 0);
842: break;
843: case Cursor.S_RESIZE_CURSOR:
844: adjust(r, min, 0, 0, 0, pt.y
845: + (dragHeight - dragOffsetY) - r.height);
846: break;
847: case Cursor.N_RESIZE_CURSOR:
848: adjust(r, min, 0, pt.y - dragOffsetY, 0,
849: -(pt.y - dragOffsetY));
850: break;
851: case Cursor.W_RESIZE_CURSOR:
852: adjust(r, min, pt.x - dragOffsetX, 0,
853: -(pt.x - dragOffsetX), 0);
854: break;
855: case Cursor.NE_RESIZE_CURSOR:
856: adjust(r, min, 0, pt.y - dragOffsetY, pt.x
857: + (dragWidth - dragOffsetX) - r.width,
858: -(pt.y - dragOffsetY));
859: break;
860: case Cursor.SE_RESIZE_CURSOR:
861: adjust(r, min, 0, 0, pt.x
862: + (dragWidth - dragOffsetX) - r.width, pt.y
863: + (dragHeight - dragOffsetY) - r.height);
864: break;
865: case Cursor.NW_RESIZE_CURSOR:
866: adjust(r, min, pt.x - dragOffsetX, pt.y
867: - dragOffsetY, -(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), pt.y
873: + (dragHeight - dragOffsetY)
874: - r.height);
875: break;
876: default:
877: break;
878: }
879: if (!r.equals(startBounds)) {
880: w.setBounds(r);
881: // Defer repaint/validate on mouseReleased unless dynamic
882: // layout is active.
883: if (Toolkit.getDefaultToolkit()
884: .isDynamicLayoutActive()) {
885: w.validate();
886: getRootPane().repaint();
887: }
888: }
889: }
890: }
891:
892: public void mouseEntered(MouseEvent ev) {
893: Window w = (Window) ev.getSource();
894: lastCursor = w.getCursor();
895: mouseMoved(ev);
896: }
897:
898: public void mouseExited(MouseEvent ev) {
899: Window w = (Window) ev.getSource();
900: w.setCursor(lastCursor);
901: }
902:
903: public void mouseClicked(MouseEvent ev) {
904: Window w = (Window) ev.getSource();
905: Frame f = null;
906:
907: if (w instanceof Frame) {
908: f = (Frame) w;
909: } else {
910: return;
911: }
912:
913: Point convertedPoint = SwingUtilities.convertPoint(w, ev
914: .getPoint(), getTitlePane());
915:
916: int state = f.getExtendedState();
917: if (getTitlePane() != null
918: && getTitlePane().contains(convertedPoint)) {
919: if ((ev.getClickCount() % 2) == 0
920: && ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
921: if (f.isResizable()) {
922: if ((state & Frame.MAXIMIZED_BOTH) != 0) {
923: f.setExtendedState(state
924: & ~Frame.MAXIMIZED_BOTH);
925: } else {
926: f.setExtendedState(state
927: | Frame.MAXIMIZED_BOTH);
928: }
929: return;
930: }
931: }
932: }
933: }
934:
935: /**
936: * Returns the corner that contains the point <code>x</code>,
937: * <code>y</code>, or -1 if the position doesn't match a corner.
938: */
939: private int calculateCorner(Component c, int x, int y) {
940: int xPosition = calculatePosition(x, c.getWidth());
941: int yPosition = calculatePosition(y, c.getHeight());
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: }
|