/* * Copyright 2000-2015 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.openapi.wm.impl; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diff.DiffColors; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.impl.EditorComponentImpl; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter; import com.intellij.openapi.fileEditor.impl.EditorsSplitters; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.AbstractPainter; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.EditorTextField; import com.intellij.ui.Graphics2DDelegate; import com.intellij.ui.components.JBLoadingPanel; import com.intellij.ui.components.JBPanelWithEmptyText; import com.intellij.ui.tabs.JBTabs; import com.intellij.util.ImageLoader; import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.JBIterable; import com.intellij.util.ui.JBSwingUtilities; import com.intellij.util.ui.JBUI; 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.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ImageObserver; import java.net.URL; import java.util.Set; /** * @author gregsh */ public class IdeBackgroundUtil { public static final String EDITOR_PROP = "idea.background.editor"; public static final String FRAME_PROP = "idea.background.frame"; public static final String TARGET_PROP = "idea.background.target"; static { JBSwingUtilities.addGlobalCGTransform(new MyTransform()); } @NotNull public static Graphics2D withEditorBackground(@NotNull Graphics g, @NotNull JComponent component) { if (suppressBackground(component)) return (Graphics2D)g; return withNamedPainters(g, EDITOR_PROP, component); } @NotNull public static Graphics2D withFrameBackground(@NotNull Graphics g, @NotNull JComponent component) { if (suppressBackground(component)) return (Graphics2D)g; return withNamedPainters(g, FRAME_PROP, component); } private static boolean suppressBackground(JComponent component) { String type = getComponentType(component); if (type == null) return false; String spec = System.getProperty(TARGET_PROP, "*"); boolean allInclusive = spec.startsWith("*"); return allInclusive && spec.contains("-" + type) || !allInclusive && !spec.contains(type); } private static final Set<String> ourKnownNames = ContainerUtil.newHashSet("navbar", "terminal"); private static String getComponentType(JComponent component) { return component instanceof JTree ? "tree" : component instanceof JList ? "list" : component instanceof JTable ? "table" : component instanceof JViewport ? "viewport" : component instanceof ActionToolbar ? "toolbar" : component instanceof EditorsSplitters ? "frame" : component instanceof EditorComponentImpl ? "editor" : component instanceof EditorGutterComponentEx ? "editor" : component instanceof JBLoadingPanel ? "loading" : component instanceof JBTabs ? "tabs" : component instanceof ToolWindowHeader ? "title" : component instanceof JBPanelWithEmptyText ? "panel" : component instanceof JPanel && ourKnownNames.contains(component.getName()) ? component.getName() : null; } @NotNull public static Graphics2D getOriginalGraphics(@NotNull Graphics g) { return g instanceof MyGraphics? ((MyGraphics)g).getDelegate() : (Graphics2D)g; } @NotNull public static Graphics2D withNamedPainters(@NotNull Graphics g, @NotNull String paintersName, @NotNull final JComponent component) { JRootPane rootPane = component.getRootPane(); Component glassPane = rootPane == null ? null : rootPane.getGlassPane(); PaintersHelper helper = glassPane instanceof IdeGlassPaneImpl? ((IdeGlassPaneImpl)glassPane).getNamedPainters(paintersName) : null; if (helper == null || !helper.needsRepaint()) return (Graphics2D)g; return MyGraphics.wrap(g, helper, component); } public static void initEditorPainters(@NotNull IdeGlassPaneImpl glassPane) { PaintersHelper.initWallpaperPainter(EDITOR_PROP, glassPane.getNamedPainters(EDITOR_PROP)); } public static void initFramePainters(@NotNull IdeGlassPaneImpl glassPane) { PaintersHelper painters = glassPane.getNamedPainters(FRAME_PROP); PaintersHelper.initWallpaperPainter(FRAME_PROP, painters); ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx(); String path = /*UIUtil.isUnderDarcula()? appInfo.getEditorBackgroundImageUrl() : */null; URL url = path == null ? null : appInfo.getClass().getResource(path); Image centerImage = url == null ? null : ImageLoader.loadFromUrl(url); if (centerImage != null) { painters.addPainter(PaintersHelper.newImagePainter(centerImage, PaintersHelper.Fill.PLAIN, PaintersHelper.Place.TOP_CENTER, 1.0f, JBUI.insets(10, 0, 0, 0)), null); } painters.addPainter(new AbstractPainter() { EditorEmptyTextPainter p = EditorEmptyTextPainter.ourInstance; @Override public boolean needsRepaint() { return true; } @Override public void executePaint(Component component, Graphics2D g) { p.paintEmptyText((JComponent)component, g); } }, null); } @Nullable public static Color getIdeBackgroundColor() { Color result = UIUtil.getSlightlyDarkerColor(UIUtil.getPanelBackground()); return UIUtil.isUnderDarcula() ? new Color(40, 40, 41) : UIUtil.getSlightlyDarkerColor(UIUtil.getSlightlyDarkerColor(result)); } public static void createTemporaryBackgroundTransform(JPanel root, String tmp, Disposable disposable) { PaintersHelper paintersHelper = new PaintersHelper(root); PaintersHelper.initWallpaperPainter(tmp, paintersHelper); Disposer.register(disposable, JBSwingUtilities.addGlobalCGTransform((t, v) -> { if (!UIUtil.isAncestor(root, t)) return v; return MyGraphics.wrap(v, paintersHelper, t); })); } @NotNull public static String getBackgroundSpec(@Nullable Project project, @NotNull String propertyName) { String spec = project == null || project.isDisposed() ? null : PropertiesComponent.getInstance(project).getValue(propertyName); if (spec == null) spec = PropertiesComponent.getInstance().getValue(propertyName); return StringUtil.notNullize(spec, System.getProperty(propertyName, "")); } public static boolean isBackgroundImageSet(@Nullable Project project) { return StringUtil.isNotEmpty(getBackgroundSpec(project, EDITOR_PROP)) || StringUtil.isNotEmpty(getBackgroundSpec(project, FRAME_PROP)); } public static void repaintAllWindows() { for (Window window : Window.getWindows()) { window.repaint(); } } static final RenderingHints.Key ADJUST_ALPHA = new RenderingHints.Key(1) { @Override public boolean isCompatibleValue(Object val) { return val instanceof Boolean; } }; private static class MyGraphics extends Graphics2DDelegate { final PaintersHelper helper; final int[] offsets; Set<Color> preserved; static Graphics2D wrap(Graphics g, PaintersHelper helper, JComponent component) { MyGraphics gg = g instanceof MyGraphics ? (MyGraphics)g : null; return new MyGraphics(gg != null ? gg.myDelegate : g, helper, helper.computeOffsets(g, component), gg != null ? gg.preserved : null); } MyGraphics(Graphics g, PaintersHelper helper, int[] offsets, Set<Color> preserved) { super((Graphics2D)g); this.helper = helper; this.offsets = offsets; this.preserved = preserved; } @NotNull @Override public Graphics create() { return new MyGraphics(getDelegate().create(), helper, offsets, preserved); } @Override public void clearRect(int x, int y, int width, int height) { super.clearRect(x, y, width, height); runAllPainters(x, y, width, height, getColor()); } @Override public void fillRect(int x, int y, int width, int height) { super.fillRect(x, y, width, height); runAllPainters(x, y, width, height, getColor()); } @Override public void fill(Shape s) { super.fill(s); Rectangle r = s.getBounds(); runAllPainters(r.x, r.y, r.width, r.height, getColor()); } @Override public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { super.drawImage(img, op, x, y); runAllPainters(x, y, img.getWidth(), img.getHeight(), img); } @Override public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { boolean b = super.drawImage(img, x, y, width, height, observer); runAllPainters(x, y, width, height, img); return b; } @Override public boolean drawImage(Image img, int x, int y, ImageObserver observer) { boolean b = super.drawImage(img, x, y, observer); runAllPainters(x, y, img.getWidth(null), img.getHeight(null), img); return b; } void runAllPainters(int x, int y, int width, int height, Object reason) { if (width <= 1 || height <= 1) return; // skip painters for transparent 'reasons' if (reason instanceof Color && ((Color)reason).getAlpha() < 255) return; if (reason instanceof Image) { if (!(reason instanceof BufferedImage)) return; if (((BufferedImage)reason).getColorModel().hasAlpha()) return; } boolean preserve = preserved != null && reason instanceof Color && preserved.contains(reason); if (preserve) { myDelegate.setRenderingHint(ADJUST_ALPHA, Boolean.TRUE); } Shape s = getClip(); Rectangle newClip = s == null ? new Rectangle(x, y, width, height) : SwingUtilities.computeIntersection(x, y, width, height, s.getBounds()); setClip(newClip); helper.runAllPainters(myDelegate, offsets); setClip(s); if (preserve) { myDelegate.setRenderingHint(ADJUST_ALPHA, Boolean.FALSE); } } } private static final JBIterable<Object> ourPreservedKeys = JBIterable.of( EditorColors.SELECTION_BACKGROUND_COLOR, DiffColors.DIFF_INSERTED, DiffColors.DIFF_DELETED, DiffColors.DIFF_MODIFIED, DiffColors.DIFF_CONFLICT); private static class MyTransform implements PairFunction<JComponent, Graphics2D, Graphics2D> { @Override public Graphics2D fun(JComponent c, Graphics2D g) { String type = getComponentType(c); if (type == null) return g; if ("frame".equals(type)) return withFrameBackground(g, c); if ("editor".equals(type)) { //noinspection CastConflictsWithInstanceof Editor editor = c instanceof EditorComponentImpl ? ((EditorComponentImpl)c).getEditor() : c instanceof EditorGutterComponentEx ? CommonDataKeys.EDITOR.getData((DataProvider)c) : null; if (editor != null) { if (!(g instanceof MyGraphics) && Boolean.TRUE.equals(EditorTextField.SUPPLEMENTARY_KEY.get(editor))) return g; Graphics2D gg = withEditorBackground(g, c); if (gg instanceof MyGraphics) { EditorColorsScheme scheme = editor.getColorsScheme(); ((MyGraphics)gg).preserved = ourPreservedKeys.map( o -> { if (o instanceof ColorKey) return scheme.getColor((ColorKey)o); TextAttributes attrs = scheme.getAttributes((TextAttributesKey)o); return attrs != null ? attrs.getBackgroundColor() : null; } ).toSet(); } return gg; } } Graphics2D gg = withEditorBackground(g, c); if (gg instanceof MyGraphics) { Component view = c instanceof JViewport ? ((JViewport)c).getView() : c; Color selectionColor = view instanceof JTree ? UIUtil.getTreeSelectionBackground() : view instanceof JList ? UIUtil.getListSelectionBackground() : view instanceof JTable ? UIUtil.getTableSelectionBackground() : null; ((MyGraphics)gg).preserved = ContainerUtil.createMaybeSingletonSet(selectionColor); } return gg; } } }