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 26 package com.apple.laf; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.awt.geom.Rectangle2D; 31 import java.awt.image.BufferedImage; 32 import java.beans.PropertyVetoException; 33 34 import javax.swing.*; 35 import javax.swing.plaf.*; 36 37 import sun.swing.SwingUtilities2; 38 39 /** 40 * From MacDockIconUI 41 * 42 * A JRSUI L&F implementation of JInternalFrame.JDesktopIcon 43 */ 44 public 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 public void mouseEntered(final MouseEvent e) { 141 if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) return; 142 String title = fFrame.getTitle(); 143 if (title == null || title.equals("")) title = "Untitled"; 144 fDockLabel = new DockLabel(title); 145 fDockLabel.show(fDesktopIcon); 146 } 147 148 @Override 149 public void mouseExited(final MouseEvent e) { 150 if (fDockLabel != null && (e.getModifiers() & InputEvent.BUTTON1_MASK) == 0) fDockLabel.hide(); 151 } 152 153 @Override 154 public void mouseClicked(final MouseEvent e) { } 155 156 @Override 157 public void mouseDragged(final MouseEvent e) { } 158 159 @Override 160 public void mouseMoved(final MouseEvent e) { } 161 162 @SuppressWarnings("serial") // Superclass is not serializable across versions 163 private final class ScaledImageLabel extends JLabel { 164 ScaledImageLabel() { 165 super(null, null, CENTER); 166 } 167 168 void updateIcon() { 169 int width = fFrame.getWidth(); 170 int height = fFrame.getHeight(); 171 172 // Protect us from unsized frames, like in JCK test DefaultDesktopManager2008 173 if (width <= 0 || height <= 0) { 174 width = 128; 175 height = 128; 176 } 177 178 final Image fImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); 179 final Graphics g = fImage.getGraphics(); 180 fFrame.paint(g); 181 g.dispose(); 182 183 final float scale = (float)fDesktopIcon.getWidth() / (float)Math.max(width, height) * 0.89f; 184 // Sending in -1 for width xor height causes it to maintain aspect ratio 185 setIcon(new ImageIcon(fImage.getScaledInstance((int)(width * scale), -1, Image.SCALE_SMOOTH))); 186 } 187 188 @Override 189 public void paint(final Graphics g) { 190 if (getIcon() == null) updateIcon(); 191 192 g.translate(0, 2); 193 194 if (!fTrackingIcon) { 195 super.paint(g); 196 return; 197 } 198 199 final ImageIcon prev = (ImageIcon)getIcon(); 200 final ImageIcon pressedIcon = new ImageIcon(AquaUtils.generateSelectedDarkImage(prev.getImage())); 201 setIcon(pressedIcon); 202 super.paint(g); 203 setIcon(prev); 204 } 205 206 boolean mouseInIcon(final MouseEvent e) { 207 return getBounds().contains(e.getX(), e.getY()); 208 } 209 210 @Override 211 public Dimension getPreferredSize() { 212 return new Dimension(64, 64); //$ Dock preferred size 213 } 214 } 215 216 @SuppressWarnings("serial") // Superclass is not serializable across versions 217 private static final class DockLabel extends JLabel { 218 static final int NUB_HEIGHT = 7; 219 static final int ROUND_ADDITIONAL_HEIGHT = 8; 220 static final int ROUND_ADDITIONAL_WIDTH = 12; 221 222 DockLabel(final String text) { 223 super(text); 224 setBorder(null); 225 setOpaque(false); 226 setFont(AquaFonts.getDockIconFont()); 227 228 final FontMetrics metrics = getFontMetrics(getFont()); 229 setSize(SwingUtilities.computeStringWidth(metrics, getText()) + ROUND_ADDITIONAL_WIDTH * 2, metrics.getAscent() + NUB_HEIGHT + ROUND_ADDITIONAL_HEIGHT); 230 } 231 232 @Override 233 public void paint(final Graphics g) { 234 final int width = getWidth(); 235 final int height = getHeight(); 236 237 final Font font = getFont(); 238 final FontMetrics metrics = getFontMetrics(font); 239 g.setFont(font); 240 241 final String text = getText().trim(); 242 final int ascent = metrics.getAscent(); 243 244 final Rectangle2D stringBounds = metrics.getStringBounds(text, g); 245 final int halfway = width / 2; 246 247 final int x = (halfway - (int)stringBounds.getWidth() / 2); 248 249 final Graphics2D g2d = g instanceof Graphics2D ? (Graphics2D)g : null; 250 if (g2d != null) { 251 g.setColor(UIManager.getColor("DesktopIcon.labelBackground")); 252 final Object origAA = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 253 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 254 255 final int roundHeight = height - ROUND_ADDITIONAL_HEIGHT + 1; 256 g.fillRoundRect(0, 0, width, roundHeight, roundHeight, roundHeight); 257 258 final int[] xpts = { halfway, halfway + NUB_HEIGHT, halfway - NUB_HEIGHT }; 259 final int[] ypts = { height, height - NUB_HEIGHT, height - NUB_HEIGHT }; 260 g.fillPolygon(xpts, ypts, 3); 261 262 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, origAA); 263 } 264 265 g.setColor(Color.black); 266 SwingUtilities2.drawString(this, g, text, x, 2 + ascent); 267 g.setColor(Color.white); 268 SwingUtilities2.drawString(this, g, text, x, 1 + ascent); 269 } 270 271 public void show(final Component invoker) { 272 final int desiredLocationX = (invoker.getWidth() - getWidth()) / 2; 273 final int desiredLocationY = -(getHeight() + 6); 274 275 Container parent = invoker.getParent(); 276 277 for (Container p = parent; p != null; p = p.getParent()) { 278 if (p instanceof JRootPane) { 279 if (p.getParent() instanceof JInternalFrame) continue; 280 parent = ((JRootPane)p).getLayeredPane(); 281 for (p = parent.getParent(); p != null && (!(p instanceof java.awt.Window)); p = p.getParent()); 282 break; 283 } 284 } 285 286 final Point p = SwingUtilities.convertPoint(invoker, desiredLocationX, desiredLocationY, parent); 287 setLocation(p.x, p.y); 288 if (parent instanceof JLayeredPane) { 289 ((JLayeredPane)parent).add(this, JLayeredPane.POPUP_LAYER, 0); 290 } 291 } 292 293 @Override 294 @Deprecated 295 public void hide() { 296 final Container parent = getParent(); 297 final Rectangle r = this.getBounds(); 298 if (parent == null) return; 299 parent.remove(this); 300 parent.repaint(r.x, r.y, r.width, r.height); 301 } 302 } 303 }