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