1 /* 2 * Copyright (c) 2011, 2015, 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 * @author 44 * @version 45 */ 46 public class AquaInternalFrameDockIconUI extends DesktopIconUI implements MouseListener, MouseMotionListener, ComponentListener { 47 private static final String CACHED_FRAME_ICON_KEY = "apple.laf.internal.frameIcon"; 48 49 protected JInternalFrame.JDesktopIcon fDesktopIcon; 50 protected JInternalFrame fFrame; 51 protected ScaledImageLabel fIconPane; 52 protected DockLabel fDockLabel; 53 protected boolean fTrackingIcon = false; 54 private TextUIDrawing textUIDrawing; 55 56 public static ComponentUI createUI(final JComponent c) { 57 return new AquaInternalFrameDockIconUI(); 58 } 59 60 public void installUI(final JComponent c) { 61 fDesktopIcon = (JInternalFrame.JDesktopIcon)c; 62 installComponents(); 63 installListeners(); 64 textUIDrawing = SwingUtilities2.getTextUIDrawing(textUIDrawing); 65 } 66 67 public void uninstallUI(final JComponent c) { 68 uninstallComponents(); 69 uninstallListeners(); 70 fDesktopIcon = null; 71 fFrame = null; 72 if (textUIDrawing != SwingUtilities2.DEFAULT_UI_TEXT_DRAWING 73 && textUIDrawing instanceof UIResource) { 74 textUIDrawing = SwingUtilities2.DEFAULT_UI_TEXT_DRAWING; 75 } 76 } 77 78 protected void installComponents() { 79 fFrame = fDesktopIcon.getInternalFrame(); 80 fIconPane = new ScaledImageLabel(); 81 fDesktopIcon.setLayout(new BorderLayout()); 82 fDesktopIcon.add(fIconPane, BorderLayout.CENTER); 83 } 84 85 protected void uninstallComponents() { 86 fDesktopIcon.setLayout(null); 87 fDesktopIcon.remove(fIconPane); 88 } 89 90 protected void installListeners() { 91 fDesktopIcon.addMouseListener(this); 92 fDesktopIcon.addMouseMotionListener(this); 93 fFrame.addComponentListener(this); 94 } 95 96 protected void uninstallListeners() { 97 fFrame.removeComponentListener(this); 98 fDesktopIcon.removeMouseMotionListener(this); 99 fDesktopIcon.removeMouseListener(this); 100 } 101 102 public Dimension getMinimumSize(final JComponent c) { 103 return new Dimension(32, 32); 104 } 105 106 public Dimension getMaximumSize(final JComponent c) { 107 return new Dimension(128, 128); 108 } 109 110 public Dimension getPreferredSize(final JComponent c) { 111 return new Dimension(64, 64); //$ Dock preferred size 112 } 113 114 public Insets getInsets(final JComponent c) { 115 return new Insets(0, 0, 0, 0); 116 } 117 118 void updateIcon() { 119 fIconPane.updateIcon(); 120 } 121 122 public void mousePressed(final MouseEvent e) { 123 fTrackingIcon = fIconPane.mouseInIcon(e); 124 if (fTrackingIcon) fIconPane.repaint(); 125 } 126 127 public void mouseReleased(final MouseEvent e) {// only when it's actually in the image 128 if (fFrame.isIconifiable() && fFrame.isIcon()) { 129 if (fTrackingIcon) { 130 fTrackingIcon = false; 131 if (fIconPane.mouseInIcon(e)) { 132 if (fDockLabel != null) fDockLabel.hide(); 133 try { 134 fFrame.setIcon(false); 135 } catch(final PropertyVetoException e2) {} 136 } else { 137 fIconPane.repaint(); 138 } 139 } 140 } 141 142 // if the mouse was completely outside fIconPane, hide the label 143 if (fDockLabel != null && !fIconPane.getBounds().contains(e.getX(), e.getY())) fDockLabel.hide(); 144 } 145 146 public void mouseEntered(final MouseEvent e) { 147 if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) return; 148 String title = fFrame.getTitle(); 149 if (title == null || title.equals("")) title = "Untitled"; 150 fDockLabel = new DockLabel(title); 151 fDockLabel.show(fDesktopIcon); 152 } 153 154 public void mouseExited(final MouseEvent e) { 155 if (fDockLabel != null && (e.getModifiers() & InputEvent.BUTTON1_MASK) == 0) fDockLabel.hide(); 156 } 157 158 public void mouseClicked(final MouseEvent e) { } 159 160 public void mouseDragged(final MouseEvent e) { } 161 162 public void mouseMoved(final MouseEvent e) { } 163 164 public void componentHidden(final ComponentEvent e) { } 165 166 public void componentMoved(final ComponentEvent e) { } 167 168 public void componentResized(final ComponentEvent e) { 169 fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, null); 170 } 171 172 public void componentShown(final ComponentEvent e) { 173 fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, null); 174 } 175 176 @SuppressWarnings("serial") // Superclass is not serializable across versions 177 class ScaledImageLabel extends JLabel { 178 ScaledImageLabel() { 179 super(null, null, CENTER); 180 } 181 182 void updateIcon() { 183 final Object priorIcon = fFrame.getClientProperty(CACHED_FRAME_ICON_KEY); 184 if (priorIcon instanceof ImageIcon) { 185 setIcon((ImageIcon)priorIcon); 186 return; 187 } 188 189 int width = fFrame.getWidth(); 190 int height = fFrame.getHeight(); 191 192 // Protect us from unsized frames, like in JCK test DefaultDesktopManager2008 193 if (width <= 0 || height <= 0) { 194 width = 128; 195 height = 128; 196 } 197 198 final Image fImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); 199 final Graphics g = fImage.getGraphics(); 200 fFrame.paint(g); 201 g.dispose(); 202 203 final float scale = (float)fDesktopIcon.getWidth() / (float)Math.max(width, height) * 0.89f; 204 // Sending in -1 for width xor height causes it to maintain aspect ratio 205 final ImageIcon icon = new ImageIcon(fImage.getScaledInstance((int)(width * scale), -1, Image.SCALE_SMOOTH)); 206 fFrame.putClientProperty(CACHED_FRAME_ICON_KEY, icon); 207 setIcon(icon); 208 } 209 210 public void paint(final Graphics g) { 211 if (getIcon() == null) updateIcon(); 212 213 g.translate(0, 2); 214 215 if (!fTrackingIcon) { 216 super.paint(g); 217 return; 218 } 219 220 final ImageIcon prev = (ImageIcon)getIcon(); 221 final ImageIcon pressedIcon = new ImageIcon(AquaUtils.generateSelectedDarkImage(prev.getImage())); 222 setIcon(pressedIcon); 223 super.paint(g); 224 setIcon(prev); 225 } 226 227 boolean mouseInIcon(final MouseEvent e) { 228 return getBounds().contains(e.getX(), e.getY()); 229 } 230 231 public Dimension getPreferredSize() { 232 return new Dimension(64, 64); //$ Dock preferred size 233 } 234 } 235 236 @SuppressWarnings("serial") // Superclass is not serializable across versions 237 class DockLabel extends JLabel { 238 static final int NUB_HEIGHT = 7; 239 static final int ROUND_ADDITIONAL_HEIGHT = 8; 240 static final int ROUND_ADDITIONAL_WIDTH = 12; 241 242 DockLabel(final String text) { 243 super(text); 244 setBorder(null); 245 setOpaque(false); 246 setFont(AquaFonts.getDockIconFont()); 247 248 final FontMetrics metrics = getFontMetrics(getFont()); 249 setSize(SwingUtilities.computeStringWidth(metrics, getText()) + ROUND_ADDITIONAL_WIDTH * 2, metrics.getAscent() + NUB_HEIGHT + ROUND_ADDITIONAL_HEIGHT); 250 } 251 252 public void paint(final Graphics g) { 253 final int width = getWidth(); 254 final int height = getHeight(); 255 256 final Font font = getFont(); 257 final FontMetrics metrics = getFontMetrics(font); 258 g.setFont(font); 259 260 final String text = getText().trim(); 261 final int ascent = metrics.getAscent(); 262 263 final Rectangle2D stringBounds = metrics.getStringBounds(text, g); 264 final int halfway = width / 2; 265 266 final int x = (halfway - (int)stringBounds.getWidth() / 2); 267 268 final Graphics2D g2d = g instanceof Graphics2D ? (Graphics2D)g : null; 269 if (g2d != null) { 270 g.setColor(UIManager.getColor("DesktopIcon.labelBackground")); 271 final Object origAA = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 272 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 273 274 final int roundHeight = height - ROUND_ADDITIONAL_HEIGHT + 1; 275 g.fillRoundRect(0, 0, width, roundHeight, roundHeight, roundHeight); 276 277 final int[] xpts = { halfway, halfway + NUB_HEIGHT, halfway - NUB_HEIGHT }; 278 final int[] ypts = { height, height - NUB_HEIGHT, height - NUB_HEIGHT }; 279 g.fillPolygon(xpts, ypts, 3); 280 281 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, origAA); 282 } 283 284 g.setColor(Color.black); 285 textUIDrawing.drawString(this, g, text, x, 2 + ascent); 286 g.setColor(Color.white); 287 textUIDrawing.drawString(this, g, text, x, 1 + ascent); 288 } 289 290 public void show(final Component invoker) { 291 final int desiredLocationX = (invoker.getWidth() - getWidth()) / 2; 292 final int desiredLocationY = -(getHeight() + 6); 293 294 Container parent = invoker.getParent(); 295 296 for (Container p = parent; p != null; p = p.getParent()) { 297 if (p instanceof JRootPane) { 298 if (p.getParent() instanceof JInternalFrame) continue; 299 parent = ((JRootPane)p).getLayeredPane(); 300 for (p = parent.getParent(); p != null && (!(p instanceof java.awt.Window)); p = p.getParent()); 301 break; 302 } 303 } 304 305 final Point p = SwingUtilities.convertPoint(invoker, desiredLocationX, desiredLocationY, parent); 306 setLocation(p.x, p.y); 307 if (parent instanceof JLayeredPane) { 308 ((JLayeredPane)parent).add(this, JLayeredPane.POPUP_LAYER, 0); 309 } 310 } 311 312 @Deprecated 313 public void hide() { 314 final Container parent = getParent(); 315 final Rectangle r = this.getBounds(); 316 if (parent == null) return; 317 parent.remove(this); 318 parent.repaint(r.x, r.y, r.width, r.height); 319 } 320 } 321 }