1/* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.apple.laf; 27 28import java.awt.*; 29import java.awt.event.*; 30import java.awt.geom.Rectangle2D; 31import java.awt.image.BufferedImage; 32import java.beans.PropertyVetoException; 33 34import javax.swing.*; 35import javax.swing.plaf.*; 36 37import sun.swing.SwingUtilities2; 38 39/** 40 * From MacDockIconUI 41 * 42 * A JRSUI L&F implementation of JInternalFrame.JDesktopIcon 43 */ 44public final class AquaInternalFrameDockIconUI extends DesktopIconUI 45 implements MouseListener, MouseMotionListener { 46 47 private JInternalFrame.JDesktopIcon fDesktopIcon; 48 private JInternalFrame fFrame; 49 private ScaledImageLabel fIconPane; 50 private DockLabel fDockLabel; 51 private boolean fTrackingIcon; 52 53 public static ComponentUI createUI(final JComponent c) { 54 return new AquaInternalFrameDockIconUI(); 55 } 56 57 @Override 58 public void installUI(final JComponent c) { 59 fDesktopIcon = (JInternalFrame.JDesktopIcon)c; 60 installComponents(); 61 installListeners(); 62 } 63 64 @Override 65 public void uninstallUI(final JComponent c) { 66 uninstallComponents(); 67 uninstallListeners(); 68 fDesktopIcon = null; 69 fFrame = null; 70 } 71 72 private void installComponents() { 73 fFrame = fDesktopIcon.getInternalFrame(); 74 fIconPane = new ScaledImageLabel(); 75 fDesktopIcon.setLayout(new BorderLayout()); 76 fDesktopIcon.add(fIconPane, BorderLayout.CENTER); 77 } 78 79 private void uninstallComponents() { 80 fDesktopIcon.setLayout(null); 81 fDesktopIcon.remove(fIconPane); 82 } 83 84 private void installListeners() { 85 fDesktopIcon.addMouseListener(this); 86 fDesktopIcon.addMouseMotionListener(this); 87 } 88 89 private void uninstallListeners() { 90 fDesktopIcon.removeMouseMotionListener(this); 91 fDesktopIcon.removeMouseListener(this); 92 } 93 94 @Override 95 public Dimension getMinimumSize(final JComponent c) { 96 return new Dimension(32, 32); 97 } 98 99 @Override 100 public Dimension getMaximumSize(final JComponent c) { 101 return new Dimension(128, 128); 102 } 103 104 @Override 105 public Dimension getPreferredSize(final JComponent c) { 106 return new Dimension(64, 64); //$ Dock preferred size 107 } 108 109 void updateIcon() { 110 fIconPane.updateIcon(); 111 } 112 113 @Override 114 public void mousePressed(final MouseEvent e) { 115 fTrackingIcon = fIconPane.mouseInIcon(e); 116 if (fTrackingIcon) fIconPane.repaint(); 117 } 118 119 @Override 120 public void mouseReleased(final MouseEvent e) {// only when it's actually in the image 121 if (fFrame.isIconifiable() && fFrame.isIcon()) { 122 if (fTrackingIcon) { 123 fTrackingIcon = false; 124 if (fIconPane.mouseInIcon(e)) { 125 if (fDockLabel != null) fDockLabel.hide(); 126 try { 127 fFrame.setIcon(false); 128 } catch(final PropertyVetoException e2) {} 129 } else { 130 fIconPane.repaint(); 131 } 132 } 133 } 134 135 // if the mouse was completely outside fIconPane, hide the label 136 if (fDockLabel != null && !fIconPane.getBounds().contains(e.getX(), e.getY())) fDockLabel.hide(); 137 } 138 139 @Override 140 @SuppressWarnings("deprecation") 141 public void mouseEntered(final MouseEvent e) { 142 if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) return; 143 String title = fFrame.getTitle(); 144 if (title == null || title.equals("")) title = "Untitled"; 145 fDockLabel = new DockLabel(title); 146 fDockLabel.show(fDesktopIcon); 147 } 148 149 @Override 150 @SuppressWarnings("deprecation") 151 public void mouseExited(final MouseEvent e) { 152 if (fDockLabel != null && (e.getModifiers() & InputEvent.BUTTON1_MASK) == 0) fDockLabel.hide(); 153 } 154 155 @Override 156 public void mouseClicked(final MouseEvent e) { } 157 158 @Override 159 public void mouseDragged(final MouseEvent e) { } 160 161 @Override 162 public void mouseMoved(final MouseEvent e) { } 163 164 @SuppressWarnings("serial") // Superclass is not serializable across versions 165 private final class ScaledImageLabel extends JLabel { 166 ScaledImageLabel() { 167 super(null, null, CENTER); 168 } 169 170 void updateIcon() { 171 int width = fFrame.getWidth(); 172 int height = fFrame.getHeight(); 173 174 // Protect us from unsized frames, like in JCK test DefaultDesktopManager2008 175 if (width <= 0 || height <= 0) { 176 width = 128; 177 height = 128; 178 } 179 180 final Image fImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); 181 final Graphics g = fImage.getGraphics(); 182 fFrame.paint(g); 183 g.dispose(); 184 185 final float scale = (float)fDesktopIcon.getWidth() / (float)Math.max(width, height) * 0.89f; 186 // Sending in -1 for width xor height causes it to maintain aspect ratio 187 setIcon(new ImageIcon(fImage.getScaledInstance((int)(width * scale), -1, Image.SCALE_SMOOTH))); 188 } 189 190 @Override 191 public void paint(final Graphics g) { 192 if (getIcon() == null) updateIcon(); 193 194 g.translate(0, 2); 195 196 if (!fTrackingIcon) { 197 super.paint(g); 198 return; 199 } 200 201 final ImageIcon prev = (ImageIcon)getIcon(); 202 final ImageIcon pressedIcon = new ImageIcon(AquaUtils.generateSelectedDarkImage(prev.getImage())); 203 setIcon(pressedIcon); 204 super.paint(g); 205 setIcon(prev); 206 } 207 208 boolean mouseInIcon(final MouseEvent e) { 209 return getBounds().contains(e.getX(), e.getY()); 210 } 211 212 @Override 213 public Dimension getPreferredSize() { 214 return new Dimension(64, 64); //$ Dock preferred size 215 } 216 } 217 218 @SuppressWarnings("serial") // Superclass is not serializable across versions 219 private static final class DockLabel extends JLabel { 220 static final int NUB_HEIGHT = 7; 221 static final int ROUND_ADDITIONAL_HEIGHT = 8; 222 static final int ROUND_ADDITIONAL_WIDTH = 12; 223 224 DockLabel(final String text) { 225 super(text); 226 setBorder(null); 227 setOpaque(false); 228 setFont(AquaFonts.getDockIconFont()); 229 230 final FontMetrics metrics = getFontMetrics(getFont()); 231 setSize(SwingUtilities.computeStringWidth(metrics, getText()) + ROUND_ADDITIONAL_WIDTH * 2, metrics.getAscent() + NUB_HEIGHT + ROUND_ADDITIONAL_HEIGHT); 232 } 233 234 @Override 235 public void paint(final Graphics g) { 236 final int width = getWidth(); 237 final int height = getHeight(); 238 239 final Font font = getFont(); 240 final FontMetrics metrics = getFontMetrics(font); 241 g.setFont(font); 242 243 final String text = getText().trim(); 244 final int ascent = metrics.getAscent(); 245 246 final Rectangle2D stringBounds = metrics.getStringBounds(text, g); 247 final int halfway = width / 2; 248 249 final int x = (halfway - (int)stringBounds.getWidth() / 2); 250 251 final Graphics2D g2d = g instanceof Graphics2D ? (Graphics2D)g : null; 252 if (g2d != null) { 253 g.setColor(UIManager.getColor("DesktopIcon.labelBackground")); 254 final Object origAA = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 255 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 256 257 final int roundHeight = height - ROUND_ADDITIONAL_HEIGHT + 1; 258 g.fillRoundRect(0, 0, width, roundHeight, roundHeight, roundHeight); 259 260 final int[] xpts = { halfway, halfway + NUB_HEIGHT, halfway - NUB_HEIGHT }; 261 final int[] ypts = { height, height - NUB_HEIGHT, height - NUB_HEIGHT }; 262 g.fillPolygon(xpts, ypts, 3); 263 264 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, origAA); 265 } 266 267 g.setColor(Color.black); 268 SwingUtilities2.drawString(this, g, text, x, 2 + ascent); 269 g.setColor(Color.white); 270 SwingUtilities2.drawString(this, g, text, x, 1 + ascent); 271 } 272 273 public void show(final Component invoker) { 274 final int desiredLocationX = (invoker.getWidth() - getWidth()) / 2; 275 final int desiredLocationY = -(getHeight() + 6); 276 277 Container parent = invoker.getParent(); 278 279 for (Container p = parent; p != null; p = p.getParent()) { 280 if (p instanceof JRootPane) { 281 if (p.getParent() instanceof JInternalFrame) continue; 282 parent = ((JRootPane)p).getLayeredPane(); 283 for (p = parent.getParent(); p != null && (!(p instanceof java.awt.Window)); p = p.getParent()); 284 break; 285 } 286 } 287 288 final Point p = SwingUtilities.convertPoint(invoker, desiredLocationX, desiredLocationY, parent); 289 setLocation(p.x, p.y); 290 if (parent instanceof JLayeredPane) { 291 ((JLayeredPane)parent).add(this, JLayeredPane.POPUP_LAYER, 0); 292 } 293 } 294 295 @Override 296 @Deprecated 297 public void hide() { 298 final Container parent = getParent(); 299 final Rectangle r = this.getBounds(); 300 if (parent == null) return; 301 parent.remove(this); 302 parent.repaint(r.x, r.y, r.width, r.height); 303 } 304 } 305} 306