001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Vadim L. Bogdanov
020: * @version $Revision$
021: */package javax.swing.plaf.metal;
022:
023: import java.awt.Component;
024: import java.awt.Container;
025: import java.awt.Dimension;
026: import java.awt.Frame;
027: import java.awt.LayoutManager;
028: import java.awt.LayoutManager2;
029: import java.awt.Point;
030: import java.awt.Rectangle;
031: import java.awt.Window;
032:
033: import java.awt.event.MouseEvent;
034:
035: import java.beans.PropertyChangeEvent;
036: import java.security.AccessController;
037: import java.security.PrivilegedAction;
038:
039: import javax.swing.JComponent;
040: import javax.swing.JLayeredPane;
041: import javax.swing.LookAndFeel;
042: import javax.swing.JRootPane;
043: import javax.swing.SwingConstants;
044: import javax.swing.SwingUtilities;
045:
046: import javax.swing.event.MouseInputAdapter;
047:
048: import javax.swing.plaf.ComponentUI;
049:
050: import javax.swing.plaf.basic.BasicRootPaneUI;
051:
052: import org.apache.harmony.x.swing.ComponentDragImplHelper;
053: import org.apache.harmony.x.swing.Utilities;
054:
055: public class MetalRootPaneUI extends BasicRootPaneUI {
056: // keys to UIDefaults
057: private static final String BORDER_KEYS[] = { null,
058: "RootPane.frameBorder", "RootPane.plainDialogBorder",
059: "RootPane.informationDialogBorder",
060: "RootPane.errorDialogBorder",
061: "RootPane.colorChooserDialogBorder",
062: "RootPane.fileChooserDialogBorder",
063: "RootPane.questionDialogBorder",
064: "RootPane.warningDialogBorder" };
065:
066: /*
067: * Custom layout. It is very similar to JRootPane.RootLayout,
068: * but also takes custom component (MetalTitlePane) into account.
069: */
070: private class MetalRootLayout implements LayoutManager2 {
071: private MetalRootLayout() {
072: }
073:
074: public Dimension preferredLayoutSize(final Container parent) {
075: return Utilities.getRootPaneLayoutSize(root
076: .getContentPane().getPreferredSize(), root
077: .getJMenuBar() != null ? root.getJMenuBar()
078: .getPreferredSize() : null, root
079: .isAncestorOf(titlePane) ? titlePane
080: .getPreferredSize() : null, root.getInsets());
081: }
082:
083: public Dimension minimumLayoutSize(final Container parent) {
084: return Utilities.getRootPaneLayoutSize(root
085: .getContentPane().getMinimumSize(), root
086: .getJMenuBar() != null ? root.getJMenuBar()
087: .getMinimumSize() : null, root
088: .isAncestorOf(titlePane) ? titlePane
089: .getMinimumSize() : null, root.getInsets());
090: }
091:
092: public Dimension maximumLayoutSize(final Container parent) {
093: return Utilities.getRootPaneLayoutSize(root
094: .getContentPane().getMaximumSize(), root
095: .getJMenuBar() != null ? root.getJMenuBar()
096: .getMaximumSize() : null, root
097: .isAncestorOf(titlePane) ? titlePane
098: .getMaximumSize() : null, root.getInsets());
099: }
100:
101: public void layoutContainer(final Container parent) {
102: // The glassPane fills the entire viewable area of the JRootPane (bounds - insets).
103: // The layeredPane fills the entire viewable area of the JRootPane. (bounds - insets)
104: // The titlePane is positioned at the upper edge of the layeredPane, if present.
105: // The menuBar is positioned under titlePane, if present.
106: // The contentPane fills the entire viewable area,
107: // minus the menuBar and the titlePane, if they are present.
108: Rectangle r = SwingUtilities.calculateInnerArea(root, null);
109:
110: root.getGlassPane().setBounds(r);
111: root.getLayeredPane().setBounds(r);
112:
113: // titlePane, menuBar, contentPane lay in layeredPane
114: int top = 0;
115: int height = r.height;
116: if (root.isAncestorOf(titlePane)) {
117: int titleHeight = titlePane.getPreferredSize().height;
118: // titlePane lays in layeredPane
119: titlePane.setBounds(0, top, r.width, titleHeight);
120: top = titleHeight;
121: height -= titleHeight;
122: }
123:
124: if (root.getJMenuBar() != null) {
125: int menuHeight = root.getJMenuBar().getPreferredSize().height;
126: // menuBar lays in layeredPane
127: root.getJMenuBar().setBounds(0, top, r.width,
128: menuHeight);
129: top += menuHeight;
130: height -= menuHeight;
131: }
132:
133: // contentPane lays in layeredPane
134: root.getContentPane().setBounds(0, top, r.width, height);
135: }
136:
137: public void addLayoutComponent(final String name,
138: final Component comp) {
139: // this method is not used
140: }
141:
142: public void removeLayoutComponent(final Component comp) {
143: // this method is not used
144: }
145:
146: public void addLayoutComponent(final Component comp,
147: final Object constraints) {
148: // this method is not used
149: }
150:
151: public float getLayoutAlignmentX(final Container target) {
152: return 0;
153: }
154:
155: public float getLayoutAlignmentY(final Container target) {
156: return 0;
157: }
158:
159: public void invalidateLayout(final Container target) {
160: // this method is not used
161: }
162: }
163:
164: /*
165: * This class is responsible for: the resizement/movement of the window;
166: * the change of the mouse cursor when it is positioned on the border.
167: */
168: private class BorderListener extends MouseInputAdapter implements
169: SwingConstants {
170:
171: /*
172: * Shows direction of resizing
173: */
174: private int resizeDirection = ComponentDragImplHelper.RESIZE_NONE;
175:
176: private ComponentDragImplHelper helper;
177:
178: /*
179: * This is the window that contains <code>JRootPane</code>
180: * with the installed UI.
181: */
182: private Window window;
183:
184: private boolean isDynamicLayout;
185:
186: /*
187: *
188: */
189: private BorderListener() {
190: window = SwingUtilities.getWindowAncestor(root);
191: helper = new ComponentDragImplHelper();
192: }
193:
194: /**
195: *
196: */
197: public void mouseClicked(final MouseEvent e) {
198: if (SwingUtilities.isLeftMouseButton(e)
199: && e.getClickCount() > 1 && isTitlePaneClick(e)) {
200:
201: Window window = SwingUtilities.getWindowAncestor(root);
202: if (window instanceof Frame) {
203: Frame frame = (Frame) window;
204: if (Utilities.isMaximumFrame(frame)) {
205: frame.setExtendedState(Frame.NORMAL);
206: } else {
207: frame.setExtendedState(Frame.MAXIMIZED_BOTH);
208: }
209: }
210: }
211: }
212:
213: public void mousePressed(final MouseEvent e) {
214: resizeDirection = ComponentDragImplHelper
215: .getResizeDirection(e, root);
216:
217: if (resizeDirection != ComponentDragImplHelper.RESIZE_NONE) {
218: if (Utilities.isResizableWindow(window)
219: && !Utilities.isMaximumFrame(window)) {
220: // resizing
221: helper.mousePressed(e, window, root);
222: isDynamicLayout = loadDynamicLayoutProperty();
223: }
224: } else if (isTitlePaneClick(e)) {
225: if (!Utilities.isMaximumFrame(window)) {
226: // dragging
227: helper.mousePressed(e, window, root);
228: }
229: }
230: }
231:
232: public void mouseExited(final MouseEvent e) {
233: if (!helper.isDragging()) {
234: window.setCursor(null);
235: }
236: }
237:
238: public void mouseDragged(final MouseEvent e) {
239: if (!helper.isDragging()) {
240: return;
241: }
242:
243: if (resizeDirection == ComponentDragImplHelper.RESIZE_NONE) {
244: // dragging the internal frame
245: Rectangle newBounds = helper.mouseDragged(e);
246: window.setBounds(newBounds);
247: // end of draggint the internal frame
248: } else {
249: // resizing the internal frame
250: Rectangle newBounds = helper.mouseDragged(e);
251: window.setBounds(newBounds);
252: if (isDynamicLayout) {
253: window.validate();
254: }
255: // end of resizing the internal frame
256: }
257: }
258:
259: public void mouseMoved(final MouseEvent e) {
260: updateMouseCursor(e);
261: }
262:
263: public void mouseReleased(final MouseEvent e) {
264: if (!helper.isDragging()) {
265: return;
266: }
267:
268: helper.endDraggingOrResizing(e);
269: if (resizeDirection != ComponentDragImplHelper.RESIZE_NONE
270: && !isDynamicLayout) {
271: window.validate();
272: }
273: resizeDirection = ComponentDragImplHelper.RESIZE_NONE;
274:
275: // restore the cursor after resizing if we need
276: // (if it doesn't point to the border)
277: updateMouseCursor(e);
278: }
279:
280: private boolean isTitlePaneClick(final MouseEvent e) {
281: Point p = SwingUtilities.convertPoint(e.getComponent(), e
282: .getPoint(), titlePane);
283:
284: return titlePane.contains(p);
285: }
286:
287: /*
288: * Update the mouse cursor.
289: */
290: private void updateMouseCursor(final MouseEvent e) {
291: if (!Utilities.isResizableWindow(window)
292: || Utilities.isMaximumFrame(window)) {
293: return;
294: }
295:
296: window.setCursor(ComponentDragImplHelper.getUpdatedCursor(
297: e, root));
298: }
299:
300: private boolean loadDynamicLayoutProperty() {
301: PrivilegedAction action = new PrivilegedAction() {
302: public Object run() {
303: return System.getProperty("swing.dynamicLayout",
304: "true");
305: }
306: };
307:
308: String value = (String) AccessController
309: .doPrivileged(action);
310: return Boolean.valueOf(value).booleanValue();
311: }
312: }
313:
314: /*
315: * The custom layout manager.
316: */
317: private LayoutManager layout;
318:
319: /*
320: * When the custom layout manager is set, the old layout manager
321: * is stored here.
322: */
323: private LayoutManager saveLayout;
324:
325: /*
326: * The custom component (title pane).
327: */
328: private MetalRootPaneTitlePane titlePane;
329:
330: private JRootPane root;
331:
332: private MouseInputAdapter borderListener;
333:
334: public void installUI(final JComponent c) {
335: super .installUI(c);
336:
337: root = (JRootPane) c;
338: if (root.getWindowDecorationStyle() != JRootPane.NONE) {
339: installWindowDecorations();
340: }
341: }
342:
343: public void uninstallUI(final JComponent c) {
344: if (root.getWindowDecorationStyle() != JRootPane.NONE) {
345: uninstallWindowDecorations();
346: }
347:
348: super .uninstallUI(c);
349: }
350:
351: public void propertyChange(final PropertyChangeEvent e) {
352: // we are interested here only in windowDecorationStyle property
353: if ("windowDecorationStyle".equals(e.getPropertyName())) {
354: //JRootPane root = (JRootPane)e.getSource();
355: if (((Integer) e.getOldValue()).intValue() != JRootPane.NONE) {
356: uninstallWindowDecorations();
357: }
358: if (((Integer) e.getNewValue()).intValue() != JRootPane.NONE) {
359: installWindowDecorations();
360: }
361: } else {
362: super .propertyChange(e);
363: }
364: }
365:
366: public static ComponentUI createUI(final JComponent c) {
367: return new MetalRootPaneUI();
368: }
369:
370: /*
371: * Installs titlePane, border and custom layout. This function
372: * is invoked only if root.getWindowDecorationStyle() != NONE.
373: */
374: private void installWindowDecorations() {
375: if (!Utilities.lookAndFeelSupportsWindowDecorations()) {
376: return;
377: }
378: // install titlePane
379: if (titlePane == null) {
380: titlePane = createTitlePane(root);
381: } else {
382: titlePane.updateTitlePaneProperties();
383: }
384: // titlePane cannot be already installed;
385: // for example: if windowDecorationStyle is clanged
386: // from FRAME to ERROR_DIALOG, windowDecorations has to be
387: // uninstalled first;
388: root.getLayeredPane().add(titlePane,
389: JLayeredPane.FRAME_CONTENT_LAYER, -1);
390:
391: // install border
392: String key = BORDER_KEYS[root.getWindowDecorationStyle()];
393: LookAndFeel.installBorder(root, key);
394:
395: // install custom layout
396: if (layout == null) {
397: layout = createLayout();
398: }
399: saveLayout = root.getLayout();
400: root.setLayout(layout);
401:
402: if (borderListener == null) {
403: borderListener = createBorderListener();
404: }
405: root.addMouseListener(borderListener);
406: root.addMouseMotionListener(borderListener);
407: }
408:
409: /*
410: * Uninstalls titlePane, border and custom layout. This function
411: * is invoked if root.getWindowDecorationStyle() is changed to NONE
412: * or when L&F is uninstalled.
413: */
414: private void uninstallWindowDecorations() {
415: if (!Utilities.lookAndFeelSupportsWindowDecorations()) {
416: return;
417: }
418: // uninstall titlePane
419: root.getLayeredPane().remove(titlePane);
420: titlePane.uninstallTitlePane();
421:
422: // uninstall border
423: LookAndFeel.uninstallBorder(root);
424:
425: // uninstall custom layout
426: root.setLayout(saveLayout);
427:
428: root.removeMouseListener(borderListener);
429: root.removeMouseMotionListener(borderListener);
430: }
431:
432: /*
433: * Creates the new instance of title pane.
434: */
435: private MetalRootPaneTitlePane createTitlePane(final JRootPane root) {
436: return new MetalRootPaneTitlePane(root);
437: }
438:
439: /*
440: * Creates the new instance of custom layout manager.
441: */
442: private LayoutManager createLayout() {
443: return new MetalRootLayout();
444: }
445:
446: private MouseInputAdapter createBorderListener() {
447: return new BorderListener();
448: }
449: }
|