7113349: Initial changeset for Macosx port to jdk
Reviewed-by: jjh, alanb, dholmes, anthony, ohrstrom, ksrini, jrose, weijun, smarks
Contributed-by: Alan Bateman <alan.bateman@oracle.com>, Alexander Potochkin <alexander.potochkin@oracle.com>, Alexander Zuev <alexander.zuev@oracle.com>, Andrew Brygin <andrew.brygin@oracle.com>, Artem Ananiev <artem.ananiev@oracle.com>, Alex Strange <astrange@apple.com>, Bino George <bino@apple.com>, Christine Lu <christine.lu@oracle.com>, David Katleman <david.katleman@oracle.com>, David Durrence <david_durrence@apple.com>, Dmitry Cherepanov <dmitry.cherepanov@oracle.com>, Greg Lewis <glewis@eyesbeyond.com>, Kevin Miller <kevin_m_miller@apple.com>, Kurt Miller <kurt@intricatesoftware.com>, Landon Fuller <landonf@plausiblelabs.com>, Leonid Romanov <leonid.romanov@oracle.com>, Loefty Walkowiak <loefty@apple.com>, Mark Reinhold <mark.reinhold@oracle.com>, Naoto Sato <naoto.sato@oracle.com>, Philip Race <philip.race@oracle.com>, Roger Hoover <rhoover@apple.com>, Scott Kovatch <scott.kovatch@oracle.com>, Sergey ByloKhov <sergey.bylokhov@oracle.com>, Mike Swingler <swingler@apple.com>, Tomas Hurka <tomas.hurka@oracle.com>
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.apple.laf;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyVetoException;
import javax.swing.*;
import javax.swing.plaf.*;
import sun.swing.SwingUtilities2;
/**
* From MacDockIconUI
*
* A JRSUI L&F implementation of JInternalFrame.JDesktopIcon
* @author
* @version
*/
public class AquaInternalFrameDockIconUI extends DesktopIconUI implements MouseListener, MouseMotionListener, ComponentListener {
private static final String CACHED_FRAME_ICON_KEY = "apple.laf.internal.frameIcon";
protected JInternalFrame.JDesktopIcon fDesktopIcon;
protected JInternalFrame fFrame;
protected ScaledImageLabel fIconPane;
protected DockLabel fDockLabel;
protected boolean fTrackingIcon = false;
public static ComponentUI createUI(final JComponent c) {
return new AquaInternalFrameDockIconUI();
}
public void installUI(final JComponent c) {
fDesktopIcon = (JInternalFrame.JDesktopIcon)c;
installComponents();
installListeners();
}
public void uninstallUI(final JComponent c) {
uninstallComponents();
uninstallListeners();
fDesktopIcon = null;
fFrame = null;
}
protected void installComponents() {
fFrame = fDesktopIcon.getInternalFrame();
fIconPane = new ScaledImageLabel();
fDesktopIcon.setLayout(new BorderLayout());
fDesktopIcon.add(fIconPane, BorderLayout.CENTER);
}
protected void uninstallComponents() {
fDesktopIcon.setLayout(null);
fDesktopIcon.remove(fIconPane);
}
protected void installListeners() {
fDesktopIcon.addMouseListener(this);
fDesktopIcon.addMouseMotionListener(this);
fFrame.addComponentListener(this);
}
protected void uninstallListeners() {
fFrame.removeComponentListener(this);
fDesktopIcon.removeMouseMotionListener(this);
fDesktopIcon.removeMouseListener(this);
}
public Dimension getMinimumSize(final JComponent c) {
return new Dimension(32, 32);
}
public Dimension getMaximumSize(final JComponent c) {
return new Dimension(128, 128);
}
public Dimension getPreferredSize(final JComponent c) {
return new Dimension(64, 64); //$ Dock preferred size
}
public Insets getInsets(final JComponent c) {
return new Insets(0, 0, 0, 0);
}
void updateIcon() {
fIconPane.updateIcon();
}
public void mousePressed(final MouseEvent e) {
fTrackingIcon = fIconPane.mouseInIcon(e);
if (fTrackingIcon) fIconPane.repaint();
}
public void mouseReleased(final MouseEvent e) {// only when it's actually in the image
if (fFrame.isIconifiable() && fFrame.isIcon()) {
if (fTrackingIcon) {
fTrackingIcon = false;
if (fIconPane.mouseInIcon(e)) {
if (fDockLabel != null) fDockLabel.hide();
try {
fFrame.setIcon(false);
} catch(final PropertyVetoException e2) {}
} else {
fIconPane.repaint();
}
}
}
// if the mouse was completely outside fIconPane, hide the label
if (fDockLabel != null && !fIconPane.getBounds().contains(e.getX(), e.getY())) fDockLabel.hide();
}
public void mouseEntered(final MouseEvent e) {
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) return;
String title = fFrame.getTitle();
if (title == null || title.equals("")) title = "Untitled";
fDockLabel = new DockLabel(title);
fDockLabel.show(fDesktopIcon);
}
public void mouseExited(final MouseEvent e) {
if (fDockLabel != null && (e.getModifiers() & InputEvent.BUTTON1_MASK) == 0) fDockLabel.hide();
}
public void mouseClicked(final MouseEvent e) { }
public void mouseDragged(final MouseEvent e) { }
public void mouseMoved(final MouseEvent e) { }
public void componentHidden(final ComponentEvent e) { }
public void componentMoved(final ComponentEvent e) { }
public void componentResized(final ComponentEvent e) {
fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, null);
}
public void componentShown(final ComponentEvent e) {
fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, null);
}
class ScaledImageLabel extends JLabel {
ScaledImageLabel() {
super(null, null, CENTER);
}
void updateIcon() {
final Object priorIcon = fFrame.getClientProperty(CACHED_FRAME_ICON_KEY);
if (priorIcon instanceof ImageIcon) {
setIcon((ImageIcon)priorIcon);
return;
}
int width = fFrame.getWidth();
int height = fFrame.getHeight();
// Protect us from unsized frames, like in JCK test DefaultDesktopManager2008
if (width <= 0 || height <= 0) {
width = 128;
height = 128;
}
final Image fImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
final Graphics g = fImage.getGraphics();
fFrame.paint(g);
g.dispose();
final float scale = (float)fDesktopIcon.getWidth() / (float)Math.max(width, height) * 0.89f;
// Sending in -1 for width xor height causes it to maintain aspect ratio
final ImageIcon icon = new ImageIcon(fImage.getScaledInstance((int)(width * scale), -1, Image.SCALE_SMOOTH));
fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, icon);
setIcon(icon);
}
public void paint(final Graphics g) {
if (getIcon() == null) updateIcon();
g.translate(0, 2);
if (!fTrackingIcon) {
super.paint(g);
return;
}
final ImageIcon prev = (ImageIcon)getIcon();
final ImageIcon pressedIcon = new ImageIcon(AquaUtils.generateSelectedDarkImage(prev.getImage()));
setIcon(pressedIcon);
super.paint(g);
setIcon(prev);
}
boolean mouseInIcon(final MouseEvent e) {
return getBounds().contains(e.getX(), e.getY());
}
public Dimension getPreferredSize() {
return new Dimension(64, 64); //$ Dock preferred size
}
}
class DockLabel extends JLabel {
final static int NUB_HEIGHT = 7;
final static int ROUND_ADDITIONAL_HEIGHT = 8;
final static int ROUND_ADDITIONAL_WIDTH = 12;
DockLabel(final String text) {
super(text);
setBorder(null);
setOpaque(false);
setFont(AquaFonts.getDockIconFont());
final FontMetrics metrics = getFontMetrics(getFont());
setSize(SwingUtilities.computeStringWidth(metrics, getText()) + ROUND_ADDITIONAL_WIDTH * 2, metrics.getAscent() + NUB_HEIGHT + ROUND_ADDITIONAL_HEIGHT);
}
public void paint(final Graphics g) {
final int width = getWidth();
final int height = getHeight();
final Font font = getFont();
final FontMetrics metrics = getFontMetrics(font);
g.setFont(font);
final String text = getText().trim();
final int ascent = metrics.getAscent();
final Rectangle2D stringBounds = metrics.getStringBounds(text, g);
final int halfway = width / 2;
final int x = (halfway - (int)stringBounds.getWidth() / 2);
final Graphics2D g2d = g instanceof Graphics2D ? (Graphics2D)g : null;
if (g2d != null) {
g.setColor(UIManager.getColor("DesktopIcon.labelBackground"));
final Object origAA = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
final int roundHeight = height - ROUND_ADDITIONAL_HEIGHT + 1;
g.fillRoundRect(0, 0, width, roundHeight, roundHeight, roundHeight);
final int[] xpts = { halfway, halfway + NUB_HEIGHT, halfway - NUB_HEIGHT };
final int[] ypts = { height, height - NUB_HEIGHT, height - NUB_HEIGHT };
g.fillPolygon(xpts, ypts, 3);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, origAA);
}
g.setColor(Color.black);
SwingUtilities2.drawString(this, g, text, x, 2 + ascent);
g.setColor(Color.white);
SwingUtilities2.drawString(this, g, text, x, 1 + ascent);
}
public void show(final Component invoker) {
final int desiredLocationX = (invoker.getWidth() - getWidth()) / 2;
final int desiredLocationY = -(getHeight() + 6);
Container parent = invoker.getParent();
for (Container p = parent; p != null; p = p.getParent()) {
if (p instanceof JRootPane) {
if (p.getParent() instanceof JInternalFrame) continue;
parent = ((JRootPane)p).getLayeredPane();
for (p = parent.getParent(); p != null && (!(p instanceof java.awt.Window)); p = p.getParent());
break;
}
}
final Point p = SwingUtilities.convertPoint(invoker, desiredLocationX, desiredLocationY, parent);
setLocation(p.x, p.y);
if (parent instanceof JLayeredPane) {
((JLayeredPane)parent).add(this, JLayeredPane.POPUP_LAYER, 0);
}
}
public void hide() {
final Container parent = getParent();
final Rectangle r = this.getBounds();
if (parent == null) return;
parent.remove(this);
parent.repaint(r.x, r.y, r.width, r.height);
}
}
}