blob: 07b5fca6b7e45981fd0104c5fd95a1d9f516e69d [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util;
import com.intellij.ide.FileIconPatcher;
import com.intellij.ide.FileIconProvider;
import com.intellij.ide.presentation.VirtualFilePresentation;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.WritingAccessProvider;
import com.intellij.ui.IconDeferrer;
import com.intellij.ui.LayeredIcon;
import com.intellij.ui.RowIcon;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
/**
* @author max
* @author Konstantin Bulenkov
*/
public class IconUtil {
private static final Key<Boolean> PROJECT_WAS_EVER_INITIALIZED = Key.create("iconDeferrer:projectWasEverInitialized");
private static boolean wasEverInitialized(@NotNull Project project) {
Boolean was = project.getUserData(PROJECT_WAS_EVER_INITIALIZED);
if (was == null) {
if (project.isInitialized()) {
was = Boolean.valueOf(true);
project.putUserData(PROJECT_WAS_EVER_INITIALIZED, was);
}
else {
was = Boolean.valueOf(false);
}
}
return was.booleanValue();
}
@NotNull
public static Icon cropIcon(@NotNull Icon icon, int maxWidth, int maxHeight) {
if (icon.getIconHeight() <= maxHeight && icon.getIconWidth() <= maxWidth) {
return icon;
}
final int w = Math.min(icon.getIconWidth(), maxWidth);
final int h = Math.min(icon.getIconHeight(), maxHeight);
final BufferedImage image = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.createCompatibleImage(icon.getIconWidth(), icon.getIconHeight(), Transparency.TRANSLUCENT);
final Graphics2D g = image.createGraphics();
icon.paintIcon(new JPanel(), g, 0, 0);
g.dispose();
final BufferedImage img = UIUtil.createImage(w, h, Transparency.TRANSLUCENT);
final int offX = icon.getIconWidth() > maxWidth ? (icon.getIconWidth() - maxWidth) / 2 : 0;
final int offY = icon.getIconHeight() > maxHeight ? (icon.getIconHeight() - maxHeight) / 2 : 0;
for (int col = 0; col < w; col++) {
for (int row = 0; row < h; row++) {
img.setRGB(col, row, image.getRGB(col + offX, row + offY));
}
}
return new ImageIcon(img);
}
public static Icon cropIcon(@NotNull Icon icon, Rectangle area) {
if (!new Rectangle(icon.getIconWidth(), icon.getIconHeight()).contains(area)) {
return icon;
}
return new CropIcon(icon, area);
}
@NotNull
public static Icon flip(@NotNull Icon icon, boolean horizontal) {
int w = icon.getIconWidth();
int h = icon.getIconHeight();
BufferedImage first = UIUtil.createImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = first.createGraphics();
icon.paintIcon(new JPanel(), g, 0, 0);
g.dispose();
BufferedImage second = UIUtil.createImage(w, h, BufferedImage.TYPE_INT_ARGB);
g = second.createGraphics();
if (horizontal) {
g.drawImage(first, 0, 0, w, h, w, 0, 0, h, null);
}
else {
g.drawImage(first, 0, 0, w, h, 0, h, w, 0, null);
}
g.dispose();
return new ImageIcon(second);
}
private static final NullableFunction<FileIconKey, Icon> ICON_NULLABLE_FUNCTION = new NullableFunction<FileIconKey, Icon>() {
@Override
public Icon fun(final FileIconKey key) {
final VirtualFile file = key.getFile();
final int flags = key.getFlags();
final Project project = key.getProject();
if (!file.isValid() || project != null && (project.isDisposed() || !wasEverInitialized(project))) return null;
final Icon providersIcon = getProvidersIcon(file, flags, project);
Icon icon = providersIcon == null ? VirtualFilePresentation.getIcon(file) : providersIcon;
final boolean dumb = project != null && DumbService.getInstance(project).isDumb();
for (FileIconPatcher patcher : getPatchers()) {
if (dumb && !DumbService.isDumbAware(patcher)) {
continue;
}
icon = patcher.patchIcon(icon, file, flags, project);
}
if ((flags & Iconable.ICON_FLAG_READ_STATUS) != 0 &&
(!file.isWritable() || !WritingAccessProvider.isPotentiallyWritable(file, project))) {
icon = new LayeredIcon(icon, PlatformIcons.LOCKED_ICON);
}
if (file.is(VFileProperty.SYMLINK)) {
icon = new LayeredIcon(icon, PlatformIcons.SYMLINK_ICON);
}
Iconable.LastComputedIcon.put(file, icon, flags);
return icon;
}
};
public static Icon getIcon(@NotNull final VirtualFile file, @Iconable.IconFlags final int flags, @Nullable final Project project) {
Icon lastIcon = Iconable.LastComputedIcon.get(file, flags);
final Icon base = lastIcon != null ? lastIcon : VirtualFilePresentation.getIcon(file);
return IconDeferrer.getInstance().defer(base, new FileIconKey(file, project, flags), ICON_NULLABLE_FUNCTION);
}
@Nullable
private static Icon getProvidersIcon(@NotNull VirtualFile file, @Iconable.IconFlags int flags, Project project) {
for (FileIconProvider provider : getProviders()) {
final Icon icon = provider.getIcon(file, flags, project);
if (icon != null) return icon;
}
return null;
}
@NotNull
public static Icon getEmptyIcon(boolean showVisibility) {
RowIcon baseIcon = new RowIcon(2);
baseIcon.setIcon(createEmptyIconLike(PlatformIcons.CLASS_ICON_PATH), 0);
if (showVisibility) {
baseIcon.setIcon(createEmptyIconLike(PlatformIcons.PUBLIC_ICON_PATH), 1);
}
return baseIcon;
}
@NotNull
private static Icon createEmptyIconLike(@NotNull String baseIconPath) {
Icon baseIcon = IconLoader.findIcon(baseIconPath);
if (baseIcon == null) {
return EmptyIcon.ICON_16;
}
return new EmptyIcon(baseIcon.getIconWidth(), baseIcon.getIconHeight());
}
private static class FileIconProviderHolder {
private static final FileIconProvider[] myProviders = Extensions.getExtensions(FileIconProvider.EP_NAME);
}
private static FileIconProvider[] getProviders() {
return FileIconProviderHolder.myProviders;
}
private static class FileIconPatcherHolder {
private static final FileIconPatcher[] ourPatchers = Extensions.getExtensions(FileIconPatcher.EP_NAME);
}
private static FileIconPatcher[] getPatchers() {
return FileIconPatcherHolder.ourPatchers;
}
public static Image toImage(@NotNull Icon icon) {
if (icon instanceof ImageIcon) {
return ((ImageIcon)icon).getImage();
}
else {
final int w = icon.getIconWidth();
final int h = icon.getIconHeight();
final BufferedImage image = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(w, h, Transparency.TRANSLUCENT);
final Graphics2D g = image.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
return image;
}
}
public static Icon getAddIcon() {
return getToolbarDecoratorIcon("add.png");
}
public static Icon getRemoveIcon() {
return getToolbarDecoratorIcon("remove.png");
}
public static Icon getMoveUpIcon() {
return getToolbarDecoratorIcon("moveUp.png");
}
public static Icon getMoveDownIcon() {
return getToolbarDecoratorIcon("moveDown.png");
}
public static Icon getEditIcon() {
return getToolbarDecoratorIcon("edit.png");
}
public static Icon getAddClassIcon() {
return getToolbarDecoratorIcon("addClass.png");
}
public static Icon getAddPatternIcon() {
return getToolbarDecoratorIcon("addPattern.png");
}
public static Icon getAddJiraPatternIcon() {
return getToolbarDecoratorIcon("addJira.png");
}
public static Icon getAddYouTrackPatternIcon() {
return getToolbarDecoratorIcon("addYouTrack.png");
}
public static Icon getAddBlankLineIcon() {
return getToolbarDecoratorIcon("addBlankLine.png");
}
public static Icon getAddPackageIcon() {
return getToolbarDecoratorIcon("addPackage.png");
}
public static Icon getAddLinkIcon() {
return getToolbarDecoratorIcon("addLink.png");
}
public static Icon getAddFolderIcon() {
return getToolbarDecoratorIcon("addFolder.png");
}
public static Icon getAnalyzeIcon() {
return getToolbarDecoratorIcon("analyze.png");
}
public static void paintInCenterOf(@NotNull Component c, Graphics g, Icon icon) {
final int x = (c.getWidth() - icon.getIconWidth()) / 2;
final int y = (c.getHeight() - icon.getIconHeight()) / 2;
icon.paintIcon(c, g, x, y);
}
public static Icon getToolbarDecoratorIcon(String name) {
return IconLoader.getIcon(getToolbarDecoratorIconsFolder() + name);
}
private static String getToolbarDecoratorIconsFolder() {
return "/toolbarDecorator/" + (SystemInfo.isMac ? "mac/" : "");
}
/**
* Result icons look like original but have equal (maximum) size
*/
@NotNull
public static Icon[] getEqualSizedIcons(@NotNull Icon... icons) {
Icon[] result = new Icon[icons.length];
int width = 0;
int height = 0;
for (Icon icon : icons) {
width = Math.max(width, icon.getIconWidth());
height = Math.max(height, icon.getIconHeight());
}
for (int i = 0; i < icons.length; i++) {
result[i] = new IconSizeWrapper(icons[i], width, height);
}
return result;
}
public static Icon toSize(@NotNull Icon icon, int width, int height) {
return new IconSizeWrapper(icon, width, height);
}
private static class IconSizeWrapper implements Icon {
private final Icon myIcon;
private final int myWidth;
private final int myHeight;
private IconSizeWrapper(@NotNull Icon icon, int width, int height) {
myIcon = icon;
myWidth = width;
myHeight = height;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
x += (myWidth - myIcon.getIconWidth()) / 2;
y += (myHeight - myIcon.getIconHeight()) / 2;
myIcon.paintIcon(c, g, x, y);
}
@Override
public int getIconWidth() {
return myWidth;
}
@Override
public int getIconHeight() {
return myHeight;
}
}
private static class CropIcon implements Icon {
private final Icon mySrc;
private final Rectangle myCrop;
private CropIcon(@NotNull Icon src, Rectangle crop) {
mySrc = src;
myCrop = crop;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
mySrc.paintIcon(c, g, x - myCrop.x, y - myCrop.y);
}
@Override
public int getIconWidth() {
return myCrop.width;
}
@Override
public int getIconHeight() {
return myCrop.height;
}
}
public static Icon scale(@NotNull final Icon source, double _scale) {
final int hiDPIscale;
if (source instanceof ImageIcon) {
Image image = ((ImageIcon)source).getImage();
hiDPIscale = RetinaImage.isAppleHiDPIScaledImage(image) || image instanceof JBHiDPIScaledImage ? 2 : 1;
} else {
hiDPIscale = 1;
}
final double scale = Math.min(32, Math.max(.1, _scale));
return new Icon() {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2d = (Graphics2D)g.create();
try {
AffineTransform transform = AffineTransform.getScaleInstance(scale, scale);
transform.preConcatenate(g2d.getTransform());
g2d.setTransform(transform);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
source.paintIcon(c, g2d, x, y);
} finally {
g2d.dispose();
}
}
@Override
public int getIconWidth() {
return (int)(source.getIconWidth() * scale) / hiDPIscale;
}
@Override
public int getIconHeight() {
return (int)(source.getIconHeight() * scale) / hiDPIscale;
}
};
}
}