1 /*
   2  * Copyright (c) 2011, 2014, 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.image.BufferedImage;
  30 import java.security.PrivilegedAction;
  31 
  32 import javax.swing.*;
  33 import javax.swing.plaf.*;
  34 
  35 import sun.lwawt.macosx.LWCToolkit;
  36 import apple.laf.JRSUIConstants.AlignmentHorizontal;
  37 import apple.laf.JRSUIConstants.AlignmentVertical;
  38 import apple.laf.JRSUIConstants.Direction;
  39 import apple.laf.JRSUIConstants.State;
  40 import apple.laf.JRSUIConstants.Widget;
  41 import apple.laf.*;
  42 
  43 import com.apple.eio.FileManager;
  44 import com.apple.laf.AquaIcon.InvertableIcon;
  45 import com.apple.laf.AquaIcon.JRSUIControlSpec;
  46 import com.apple.laf.AquaIcon.SystemIcon;
  47 import com.apple.laf.AquaUtils.RecyclableObject;
  48 import com.apple.laf.AquaUtils.RecyclableSingleton;
  49 import java.util.Arrays;
  50 import java.util.List;
  51 import sun.awt.image.MultiResolutionImage;
  52 
  53 public class AquaImageFactory {
  54     public static IconUIResource getConfirmImageIcon() {
  55         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
  56 
  57         return new IconUIResource(new AquaIcon.CachingScalingIcon(kAlertIconSize, kAlertIconSize) {
  58             Image createImage() {
  59                 return getThisApplicationsIcon(kAlertIconSize, kAlertIconSize);
  60             }
  61         });
  62     }
  63 
  64     public static IconUIResource getCautionImageIcon() {
  65         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
  66         return getAppIconCompositedOn(AquaIcon.SystemIcon.getCautionIcon());
  67     }
  68 
  69     public static IconUIResource getStopImageIcon() {
  70         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
  71         return getAppIconCompositedOn(AquaIcon.SystemIcon.getStopIcon());
  72     }
  73 
  74     public static IconUIResource getLockImageIcon() {
  75         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
  76         if (JRSUIUtils.Images.shouldUseLegacySecurityUIPath()) {
  77             final Image lockIcon = AquaUtils.getCImageCreator().createImageFromFile("/System/Library/CoreServices/SecurityAgent.app/Contents/Resources/Security.icns", kAlertIconSize, kAlertIconSize);
  78             return getAppIconCompositedOn(lockIcon);
  79         }
  80 
  81         final Image lockIcon = Toolkit.getDefaultToolkit().getImage("NSImage://NSSecurity");
  82         return getAppIconCompositedOn(lockIcon);
  83     }
  84 
  85     static Image getThisApplicationsIcon(final int width, final int height) {
  86         final String path = getPathToThisApplication();
  87 
  88         if (path == null) {
  89             return getGenericJavaIcon();
  90         }
  91 
  92         if (path.endsWith("/Home/bin")) {
  93             return getGenericJavaIcon();
  94         }
  95 
  96         if (path.startsWith("/usr/bin")) {
  97             return getGenericJavaIcon();
  98         }
  99 
 100         return AquaUtils.getCImageCreator().createImageOfFile(path, height, width);
 101     }
 102 
 103     static Image getGenericJavaIcon() {
 104         return java.security.AccessController.doPrivileged(new PrivilegedAction<Image>() {
 105             public Image run() {
 106                 return com.apple.eawt.Application.getApplication().getDockIconImage();
 107             }
 108         });
 109     }
 110 
 111     static String getPathToThisApplication() {
 112         return java.security.AccessController.doPrivileged(new PrivilegedAction<String>() {
 113             public String run() {
 114                 return FileManager.getPathToApplicationBundle();
 115             }
 116         });
 117     }
 118 
 119     static IconUIResource getAppIconCompositedOn(final SystemIcon systemIcon) {
 120         systemIcon.setSize(kAlertIconSize, kAlertIconSize);
 121         return getAppIconCompositedOn(systemIcon.createImage());
 122     }
 123 
 124     private static final int kAlertIconSize = 64;
 125     static IconUIResource getAppIconCompositedOn(final Image background) {
 126 
 127         final BufferedImage iconImage = getAppIconImageCompositedOn(background, 1);
 128 
 129         if (background instanceof MultiResolutionIconImage) {
 130             BufferedImage background2x
 131                     = ((MultiResolutionIconImage) background).resolutionVariant;
 132             BufferedImage icon2xImage = getAppIconImageCompositedOn(background2x, 2);
 133 
 134             return new IconUIResource(new ImageIcon(
 135                     new MultiResolutionIconImage(iconImage, icon2xImage)));
 136         }
 137         return new IconUIResource(new ImageIcon(iconImage));
 138     }
 139 
 140     static BufferedImage getAppIconImageCompositedOn(final Image background, int scaleFactor) {
 141 
 142         final int scaledAlertIconSize = kAlertIconSize * scaleFactor;
 143         final int kAlertSubIconSize = (int) (scaledAlertIconSize * 0.5);
 144         final int kAlertSubIconInset = scaledAlertIconSize - kAlertSubIconSize;
 145         final Icon smallAppIconScaled = new AquaIcon.CachingScalingIcon(
 146                 kAlertSubIconSize, kAlertSubIconSize) {
 147                     Image createImage() {
 148                         return getThisApplicationsIcon(kAlertSubIconSize, kAlertSubIconSize);
 149                     }
 150                 };
 151 
 152         final BufferedImage image = new BufferedImage(scaledAlertIconSize,
 153                 scaledAlertIconSize, BufferedImage.TYPE_INT_ARGB);
 154         final Graphics g = image.getGraphics();
 155         g.drawImage(background, 0, 0,
 156                 scaledAlertIconSize, scaledAlertIconSize, null);
 157         if (g instanceof Graphics2D) {
 158             // improves icon rendering quality in Quartz
 159             ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING,
 160                     RenderingHints.VALUE_RENDER_QUALITY);
 161         }
 162 
 163         smallAppIconScaled.paintIcon(null, g,
 164                 kAlertSubIconInset, kAlertSubIconInset);
 165         g.dispose();
 166 
 167         return image;
 168     }
 169 
 170     public static IconUIResource getTreeFolderIcon() {
 171         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 172         return AquaIcon.SystemIcon.getFolderIconUIResource();
 173     }
 174 
 175     public static IconUIResource getTreeOpenFolderIcon() {
 176         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 177         return AquaIcon.SystemIcon.getOpenFolderIconUIResource();
 178     }
 179 
 180     public static IconUIResource getTreeDocumentIcon() {
 181         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 182         return AquaIcon.SystemIcon.getDocumentIconUIResource();
 183     }
 184 
 185     public static UIResource getTreeExpandedIcon() {
 186         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 187         return AquaIcon.getIconFor(new JRSUIControlSpec() {
 188             public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {
 189                 painter.state.set(Widget.DISCLOSURE_TRIANGLE);
 190                 painter.state.set(State.ACTIVE);
 191                 painter.state.set(Direction.DOWN);
 192                 painter.state.set(AlignmentHorizontal.CENTER);
 193                 painter.state.set(AlignmentVertical.CENTER);
 194             }
 195         }, 20, 20);
 196     }
 197 
 198     public static UIResource getTreeCollapsedIcon() {
 199         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 200         return AquaIcon.getIconFor(new JRSUIControlSpec() {
 201             public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {
 202                 painter.state.set(Widget.DISCLOSURE_TRIANGLE);
 203                 painter.state.set(State.ACTIVE);
 204                 painter.state.set(Direction.RIGHT);
 205                 painter.state.set(AlignmentHorizontal.CENTER);
 206                 painter.state.set(AlignmentVertical.CENTER);
 207             }
 208         }, 20, 20);
 209     }
 210 
 211     public static UIResource getTreeRightToLeftCollapsedIcon() {
 212         // public, because UIDefaults.ProxyLazyValue uses reflection to get this value
 213         return AquaIcon.getIconFor(new JRSUIControlSpec() {
 214             public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {
 215                 painter.state.set(Widget.DISCLOSURE_TRIANGLE);
 216                 painter.state.set(State.ACTIVE);
 217                 painter.state.set(Direction.LEFT);
 218                 painter.state.set(AlignmentHorizontal.CENTER);
 219                 painter.state.set(AlignmentVertical.CENTER);
 220             }
 221         }, 20, 20);
 222     }
 223 
 224     static class NamedImageSingleton extends RecyclableSingleton<Image> {
 225         final String namedImage;
 226 
 227         NamedImageSingleton(final String namedImage) {
 228             this.namedImage = namedImage;
 229         }
 230 
 231         @Override
 232         protected Image getInstance() {
 233             return Toolkit.getDefaultToolkit().getImage("NSImage://" + namedImage);
 234         }
 235     }
 236 
 237     static class IconUIResourceSingleton extends RecyclableSingleton<IconUIResource> {
 238         final NamedImageSingleton holder;
 239 
 240         public IconUIResourceSingleton(final NamedImageSingleton holder) {
 241             this.holder = holder;
 242         }
 243 
 244         @Override
 245         protected IconUIResource getInstance() {
 246             return new IconUIResource(new ImageIcon(holder.get()));
 247         }
 248     }
 249 
 250     @SuppressWarnings("serial") // Superclass is not serializable across versions
 251     static class InvertableImageIcon extends ImageIcon implements InvertableIcon, UIResource {
 252         Icon invertedImage;
 253         public InvertableImageIcon(final Image image) {
 254             super(image);
 255         }
 256 
 257         @Override
 258         public Icon getInvertedIcon() {
 259             if (invertedImage != null) return invertedImage;
 260             return invertedImage = new IconUIResource(new ImageIcon(AquaUtils.generateLightenedImage(getImage(), 100)));
 261         }
 262     }
 263 
 264     protected static final NamedImageSingleton northArrow = new NamedImageSingleton("NSMenuScrollUp");
 265     protected static final IconUIResourceSingleton northArrowIcon = new IconUIResourceSingleton(northArrow);
 266     protected static final NamedImageSingleton southArrow = new NamedImageSingleton("NSMenuScrollDown");
 267     protected static final IconUIResourceSingleton southArrowIcon = new IconUIResourceSingleton(southArrow);
 268     protected static final NamedImageSingleton westArrow = new NamedImageSingleton("NSMenuSubmenuLeft");
 269     protected static final IconUIResourceSingleton westArrowIcon = new IconUIResourceSingleton(westArrow);
 270     protected static final NamedImageSingleton eastArrow = new NamedImageSingleton("NSMenuSubmenu");
 271     protected static final IconUIResourceSingleton eastArrowIcon = new IconUIResourceSingleton(eastArrow);
 272 
 273     static Image getArrowImageForDirection(final int direction) {
 274         switch(direction) {
 275             case SwingConstants.NORTH: return northArrow.get();
 276             case SwingConstants.SOUTH: return southArrow.get();
 277             case SwingConstants.EAST: return eastArrow.get();
 278             case SwingConstants.WEST: return westArrow.get();
 279         }
 280         return null;
 281     }
 282 
 283     static Icon getArrowIconForDirection(int direction) {
 284         switch(direction) {
 285             case SwingConstants.NORTH: return northArrowIcon.get();
 286             case SwingConstants.SOUTH: return southArrowIcon.get();
 287             case SwingConstants.EAST: return eastArrowIcon.get();
 288             case SwingConstants.WEST: return westArrowIcon.get();
 289         }
 290         return null;
 291     }
 292 
 293     public static Icon getMenuArrowIcon() {
 294         return new InvertableImageIcon(AquaUtils.generateLightenedImage(eastArrow.get(), 25));
 295     }
 296 
 297     public static Icon getMenuItemCheckIcon() {
 298         return new InvertableImageIcon(AquaUtils.generateLightenedImage(Toolkit.getDefaultToolkit().getImage("NSImage://NSMenuItemSelection"), 25));
 299     }
 300 
 301     public static Icon getMenuItemDashIcon() {
 302         return new InvertableImageIcon(AquaUtils.generateLightenedImage(Toolkit.getDefaultToolkit().getImage("NSImage://NSMenuMixedState"), 25));
 303     }
 304 
 305     public static class NineSliceMetrics {
 306         public final int wCut, eCut, nCut, sCut;
 307         public final int minW, minH;
 308         public final boolean showMiddle, stretchH, stretchV;
 309 
 310         public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut) {
 311             this(minWidth, minHeight, westCut, eastCut, northCut, southCut, true);
 312         }
 313 
 314         public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean showMiddle) {
 315             this(minWidth, minHeight, westCut, eastCut, northCut, southCut, showMiddle, true, true);
 316         }
 317 
 318         public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean showMiddle, final boolean stretchHorizontally, final boolean stretchVertically) {
 319             this.wCut = westCut; this.eCut = eastCut; this.nCut = northCut; this.sCut = southCut;
 320             this.minW = minWidth; this.minH = minHeight;
 321             this.showMiddle = showMiddle; this.stretchH = stretchHorizontally; this.stretchV = stretchVertically;
 322         }
 323     }
 324 
 325     /*
 326      * A "paintable" which holds nine images, which represent a sliced up initial
 327      * image that can be streched from its middles.
 328      */
 329     public static class SlicedImageControl {
 330         final BufferedImage NW, N, NE;
 331         final BufferedImage W, C, E;
 332         final BufferedImage SW, S, SE;
 333 
 334         final NineSliceMetrics metrics;
 335 
 336         final int totalWidth, totalHeight;
 337         final int centerColWidth, centerRowHeight;
 338 
 339         public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut) {
 340             this(img, westCut, eastCut, northCut, southCut, true);
 341         }
 342 
 343         public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle) {
 344             this(img, westCut, eastCut, northCut, southCut, useMiddle, true, true);
 345         }
 346 
 347         public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle, final boolean stretchHorizontally, final boolean stretchVertically) {
 348             this(img, new NineSliceMetrics(img.getWidth(null), img.getHeight(null), westCut, eastCut, northCut, southCut, useMiddle, stretchHorizontally, stretchVertically));
 349         }
 350 
 351         public SlicedImageControl(final Image img, final NineSliceMetrics metrics) {
 352             this.metrics = metrics;
 353 
 354             if (img.getWidth(null) != metrics.minW || img.getHeight(null) != metrics.minH) {
 355                 throw new IllegalArgumentException("SlicedImageControl: template image and NineSliceMetrics don't agree on minimum dimensions");
 356             }
 357 
 358             totalWidth = metrics.minW;
 359             totalHeight = metrics.minH;
 360             centerColWidth = totalWidth - metrics.wCut - metrics.eCut;
 361             centerRowHeight = totalHeight - metrics.nCut - metrics.sCut;
 362 
 363             NW = createSlice(img, 0, 0, metrics.wCut, metrics.nCut);
 364             N = createSlice(img, metrics.wCut, 0, centerColWidth, metrics.nCut);
 365             NE = createSlice(img, totalWidth - metrics.eCut, 0, metrics.eCut, metrics.nCut);
 366             W = createSlice(img, 0, metrics.nCut, metrics.wCut, centerRowHeight);
 367             C = metrics.showMiddle ? createSlice(img, metrics.wCut, metrics.nCut, centerColWidth, centerRowHeight) : null;
 368             E = createSlice(img, totalWidth - metrics.eCut, metrics.nCut, metrics.eCut, centerRowHeight);
 369             SW = createSlice(img, 0, totalHeight - metrics.sCut, metrics.wCut, metrics.sCut);
 370             S = createSlice(img, metrics.wCut, totalHeight - metrics.sCut, centerColWidth, metrics.sCut);
 371             SE = createSlice(img, totalWidth - metrics.eCut, totalHeight - metrics.sCut, metrics.eCut, metrics.sCut);
 372         }
 373 
 374         static BufferedImage createSlice(final Image img, final int x, final int y, final int w, final int h) {
 375             if (w == 0 || h == 0) return null;
 376 
 377             final BufferedImage slice = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
 378             final Graphics2D g2d = slice.createGraphics();
 379             g2d.drawImage(img, 0, 0, w, h, x, y, x + w, y + h, null);
 380             g2d.dispose();
 381 
 382             return slice;
 383         }
 384 
 385         public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
 386             g.translate(x, y);
 387 
 388             if (w < totalWidth || h < totalHeight) {
 389                 paintCompressed(g, w, h);
 390             } else {
 391                 paintStretchedMiddles(g, w, h);
 392             }
 393 
 394             g.translate(-x, -y);
 395         }
 396 
 397         void paintStretchedMiddles(final Graphics g, final int w, final int h) {
 398             int baseX = metrics.stretchH ? 0 : ((w / 2) - (totalWidth / 2));
 399             int baseY = metrics.stretchV ? 0 : ((h / 2) - (totalHeight / 2));
 400             int adjustedWidth = metrics.stretchH ? w : totalWidth;
 401             int adjustedHeight = metrics.stretchV ? h : totalHeight;
 402 
 403             if (NW != null) g.drawImage(NW, baseX, baseY, null);
 404             if (N != null) g.drawImage(N, baseX + metrics.wCut, baseY, adjustedWidth - metrics.eCut - metrics.wCut, metrics.nCut, null);
 405             if (NE != null) g.drawImage(NE, baseX + adjustedWidth - metrics.eCut, baseY, null);
 406             if (W != null) g.drawImage(W, baseX, baseY + metrics.nCut, metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut, null);
 407             if (C != null) g.drawImage(C, baseX + metrics.wCut, baseY + metrics.nCut, adjustedWidth - metrics.eCut - metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut, null);
 408             if (E != null) g.drawImage(E, baseX + adjustedWidth - metrics.eCut, baseY + metrics.nCut, metrics.eCut, adjustedHeight - metrics.nCut - metrics.sCut, null);
 409             if (SW != null) g.drawImage(SW, baseX, baseY + adjustedHeight - metrics.sCut, null);
 410             if (S != null) g.drawImage(S, baseX + metrics.wCut, baseY + adjustedHeight - metrics.sCut, adjustedWidth - metrics.eCut - metrics.wCut, metrics.sCut, null);
 411             if (SE != null) g.drawImage(SE, baseX + adjustedWidth - metrics.eCut, baseY + adjustedHeight - metrics.sCut, null);
 412 
 413             /*
 414             if (NW != null) {g.setColor(Color.GREEN); g.fillRect(baseX, baseY, NW.getWidth(), NW.getHeight());}
 415             if (N != null) {g.setColor(Color.RED); g.fillRect(baseX + metrics.wCut, baseY, adjustedWidth - metrics.eCut - metrics.wCut, metrics.nCut);}
 416             if (NE != null) {g.setColor(Color.BLUE); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY, NE.getWidth(), NE.getHeight());}
 417             if (W != null) {g.setColor(Color.PINK); g.fillRect(baseX, baseY + metrics.nCut, metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut);}
 418             if (C != null) {g.setColor(Color.ORANGE); g.fillRect(baseX + metrics.wCut, baseY + metrics.nCut, adjustedWidth - metrics.eCut - metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut);}
 419             if (E != null) {g.setColor(Color.CYAN); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY + metrics.nCut, metrics.eCut, adjustedHeight - metrics.nCut - metrics.sCut);}
 420             if (SW != null) {g.setColor(Color.MAGENTA); g.fillRect(baseX, baseY + adjustedHeight - metrics.sCut, SW.getWidth(), SW.getHeight());}
 421             if (S != null) {g.setColor(Color.DARK_GRAY); g.fillRect(baseX + metrics.wCut, baseY + adjustedHeight - metrics.sCut, adjustedWidth - metrics.eCut - metrics.wCut, metrics.sCut);}
 422             if (SE != null) {g.setColor(Color.YELLOW); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY + adjustedHeight - metrics.sCut, SE.getWidth(), SE.getHeight());}
 423             */
 424         }
 425 
 426         void paintCompressed(final Graphics g, final int w, final int h) {
 427             final double heightRatio = h > totalHeight ? 1.0 : (double)h / (double)totalHeight;
 428             final double widthRatio = w > totalWidth ? 1.0 : (double)w / (double)totalWidth;
 429 
 430             final int northHeight = (int)(metrics.nCut * heightRatio);
 431             final int southHeight = (int)(metrics.sCut * heightRatio);
 432             final int centerHeight = h - northHeight - southHeight;
 433 
 434             final int westWidth = (int)(metrics.wCut * widthRatio);
 435             final int eastWidth = (int)(metrics.eCut * widthRatio);
 436             final int centerWidth = w - westWidth - eastWidth;
 437 
 438             if (NW != null) g.drawImage(NW, 0, 0, westWidth, northHeight, null);
 439             if (N != null) g.drawImage(N, westWidth, 0, centerWidth, northHeight, null);
 440             if (NE != null) g.drawImage(NE, w - eastWidth, 0, eastWidth, northHeight, null);
 441             if (W != null) g.drawImage(W, 0, northHeight, westWidth, centerHeight, null);
 442             if (C != null) g.drawImage(C, westWidth, northHeight, centerWidth, centerHeight, null);
 443             if (E != null) g.drawImage(E, w - eastWidth, northHeight, eastWidth, centerHeight, null);
 444             if (SW != null) g.drawImage(SW, 0, h - southHeight, westWidth, southHeight, null);
 445             if (S != null) g.drawImage(S, westWidth, h - southHeight, centerWidth, southHeight, null);
 446             if (SE != null) g.drawImage(SE, w - eastWidth, h - southHeight, eastWidth, southHeight, null);
 447         }
 448     }
 449 
 450     public abstract static class RecyclableSlicedImageControl extends RecyclableObject<SlicedImageControl> {
 451         final NineSliceMetrics metrics;
 452 
 453         public RecyclableSlicedImageControl(final NineSliceMetrics metrics) {
 454             this.metrics = metrics;
 455         }
 456 
 457         @Override
 458         protected SlicedImageControl create() {
 459             return new SlicedImageControl(createTemplateImage(metrics.minW, metrics.minH), metrics);
 460         }
 461 
 462         protected abstract Image createTemplateImage(final int width, final int height);
 463     }
 464 
 465     // when we use SystemColors, we need to proxy the color with something that implements UIResource,
 466     // so that it will be uninstalled when the look and feel is changed.
 467     @SuppressWarnings("serial") // JDK implementation class
 468     private static class SystemColorProxy extends Color implements UIResource {
 469         final Color color;
 470         public SystemColorProxy(final Color color) {
 471             super(color.getRGB());
 472             this.color = color;
 473         }
 474 
 475         public int getRGB() {
 476             return color.getRGB();
 477         }
 478     }
 479 
 480     public static Color getWindowBackgroundColorUIResource() {
 481         //return AquaNativeResources.getWindowBackgroundColorUIResource();
 482         return new SystemColorProxy(SystemColor.window);
 483     }
 484 
 485     public static Color getTextSelectionBackgroundColorUIResource() {
 486         return new SystemColorProxy(SystemColor.textHighlight);
 487     }
 488 
 489     public static Color getTextSelectionForegroundColorUIResource() {
 490         return new SystemColorProxy(SystemColor.textHighlightText);
 491     }
 492 
 493     public static Color getSelectionBackgroundColorUIResource() {
 494         return new SystemColorProxy(SystemColor.controlHighlight);
 495     }
 496 
 497     public static Color getSelectionForegroundColorUIResource() {
 498         return new SystemColorProxy(SystemColor.controlLtHighlight);
 499     }
 500 
 501     public static Color getFocusRingColorUIResource() {
 502         return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.KEYBOARD_FOCUS_COLOR));
 503     }
 504 
 505     public static Color getSelectionInactiveBackgroundColorUIResource() {
 506         return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_BACKGROUND_COLOR));
 507     }
 508 
 509     public static Color getSelectionInactiveForegroundColorUIResource() {
 510         return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_FOREGROUND_COLOR));
 511     }
 512 
 513     static class MultiResolutionIconImage extends BufferedImage
 514             implements MultiResolutionImage {
 515 
 516         BufferedImage resolutionVariant;
 517 
 518         public MultiResolutionIconImage(BufferedImage image, BufferedImage resolutionVariant) {
 519             super(image.getWidth(), image.getHeight(), image.getType());
 520             this.resolutionVariant = resolutionVariant;
 521             Graphics g = getGraphics();
 522             g.drawImage(image, 0, 0, null);
 523             g.dispose();
 524         }
 525 
 526         @Override
 527         public Image getResolutionVariant(int width, int height) {
 528             return ((width <= getWidth() && height <= getHeight()))
 529                     ? this : resolutionVariant;
 530         }
 531 
 532         @Override
 533         public List<Image> getResolutionVariants() {
 534             return Arrays.asList(this, resolutionVariant);
 535         }
 536     }
 537 }