IDEA-148164 Preliminary support of error stripes in the project view tree

This commit is contained in:
Sergey Malenkov
2015-12-04 16:19:21 +03:00
parent baa3dfe8e6
commit 30ff166ebe
9 changed files with 949 additions and 0 deletions
@@ -22,19 +22,30 @@ package com.intellij.ide.projectView.impl;
import com.intellij.ide.DataManager;
import com.intellij.ide.PsiCopyPasteManager;
import com.intellij.ide.projectView.BaseProjectTreeBuilder;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.ui.customization.CustomizationUtil;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeUpdater;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
import com.intellij.ide.util.treeView.TreeBuilderUtil;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.stripe.ErrorStripe;
import com.intellij.ui.stripe.ExtraErrorStripePainter;
import com.intellij.ui.stripe.TreeUpdater;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.OpenSourceUtil;
import com.intellij.util.ui.UIUtil;
@@ -71,6 +82,45 @@ public abstract class AbstractProjectViewPSIPane extends AbstractProjectViewPane
myTree = createTree(treeModel);
enableDnD();
myComponent = ScrollPaneFactory.createScrollPane(myTree);
if (Registry.is("error.stripe.enabled")) {
ExtraErrorStripePainter painter = new ExtraErrorStripePainter(true);
Disposer.register(this, new TreeUpdater<ExtraErrorStripePainter>(painter, myComponent, myTree) {
@Override
protected void update(ExtraErrorStripePainter painter, int index, Object object) {
if (object instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)object;
object = node.getUserObject();
}
painter.setExtraStripe(index, getExtraStripe(object));
if (object instanceof PsiDirectoryNode && !myTree.isCollapsed(index)) {
object = null;
}
super.update(painter, index, object);
}
@Override
protected ErrorStripe getErrorStripe(Object object) {
if (object instanceof PresentableNodeDescriptor) {
PresentableNodeDescriptor node = (PresentableNodeDescriptor)object;
PresentationData presentation = node.getPresentation();
TextAttributesKey key = presentation.getTextAttributesKey();
if (key != null) {
TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(key);
if (attributes != null) return ErrorStripe.create(attributes.getEffectColor(), 1);
}
}
return null;
}
private ErrorStripe getExtraStripe(Object object) {
if (object instanceof NodeDescriptor) {
NodeDescriptor node = (NodeDescriptor)object;
return ErrorStripe.create(node.getColor(), 0);
}
return null;
}
});
}
myTreeStructure = createStructure();
setTreeBuilder(createBuilder(treeModel));
@@ -637,6 +637,10 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
if (alwaysShowTrack() || myMouseOverScrollbarExpandLevel > 0) {
doPaintTrack(g, c, trackBounds);
}
RegionPainter painter = UIUtil.getClientProperty(c, RegionPainter.BUTTONLESS_SCROLL_BAR_UI_EXTRA_TRACK);
if (painter != null) {
painter.paint(g, trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
}
}
protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) {
@@ -0,0 +1,61 @@
/*
* 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.util.ui;
import com.intellij.openapi.util.Key;
import com.intellij.util.ui.UIUtil;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
/**
* @author Sergey.Malenkov
*/
public interface RegionPainter {
void paint(Graphics g, int x, int y, int width, int height);
Key<RegionPainter> BUTTONLESS_SCROLL_BAR_UI_EXTRA_TRACK = Key.create("BUTTONLESS_SCROLL_BAR_UI_EXTRA_TRACK");
class Image implements RegionPainter {
private BufferedImage myImage;
protected void updateImage(BufferedImage image) {
}
protected BufferedImage createImage(int width, int height) {
return UIUtil.createImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
protected void invalidate() {
myImage = null;
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
if (width > 0 && width > 0) {
if (myImage == null || width != myImage.getWidth() || height != myImage.getHeight()) {
myImage = createImage(width, height);
}
else if (myImage != null) {
updateImage(myImage);
}
if (myImage != null) {
UIUtil.drawImage(g, myImage, null, x, y);
}
}
}
}
}
@@ -0,0 +1,79 @@
/*
* 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.ui.stripe;
import com.intellij.util.ui.RegionPainter;
import java.awt.Color;
import java.awt.Graphics;
/**
* @author Sergey.Malenkov
*/
public final class ErrorStripe implements RegionPainter, Comparable<ErrorStripe> {
private final Color myColor;
private final int myLayer;
private ErrorStripe(Color color, int layer) {
myColor = color;
myLayer = layer;
}
public static ErrorStripe create(Color color, int layer) {
return color == null ? null : new ErrorStripe(color, layer);
}
public Color getColor() {
return myColor;
}
public int getLayer() {
return myLayer;
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
g.setColor(myColor);
g.fillRect(x, y, width, height);
}
@Override
public int hashCode() {
return myLayer + myColor.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object instanceof ErrorStripe) {
ErrorStripe stripe = (ErrorStripe)object;
return stripe.myLayer == myLayer && stripe.myColor.getRGB() == myColor.getRGB();
}
return false;
}
@Override
public int compareTo(ErrorStripe stripe) {
if (stripe == this) return 0;
if (stripe == null || stripe.myLayer < myLayer) return -1;
if (stripe.myLayer > myLayer) return 1;
int thisRGB = myColor.getRGB();
int thatRGB = stripe.myColor.getRGB();
if (thatRGB == thisRGB) return 0;
return thatRGB < thisRGB ? -1 : 1;
}
}
@@ -0,0 +1,348 @@
/*
* 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.ui.stripe;
import com.intellij.util.ui.RegionPainter;
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.TreeSet;
/**
* @author Sergey.Malenkov
*/
public class ErrorStripePainter extends RegionPainter.Image {
public enum Alignment {TOP, CENTER, BOTTOM}
private final boolean mySingleValue;
private final Alignment myAlignment;
private Value[] myArray;
private int myArraySize;
private int myImageY;
private int myImageHeight;
private int myMax = Integer.MAX_VALUE;
private int myMin = 1;
private int myGap;
public ErrorStripePainter(boolean single) {
this(single, Alignment.CENTER);
}
public ErrorStripePainter(boolean single, Alignment style) {
mySingleValue = single;
myAlignment = style;
}
public int getMaximalThickness() {
return myMax;
}
public void setMaximalThickness(int thickness) {
if (myMax != thickness) {
myMax = thickness;
invalidate();
}
}
public int getMinimalThickness() {
return myMin;
}
public void setMinimalThickness(int thickness) {
if (thickness < 1) thickness = 1;
if (myMin != thickness) {
myMin = thickness;
invalidate();
}
}
public int getErrorStripeGap() {
return myGap;
}
public void setErrorStripeGap(int gap) {
if (gap < 0) gap = 0;
if (myGap != gap) {
myGap = gap;
invalidate();
}
}
public int findIndex(int x, int y) {
if (0 < myImageHeight && myImageY <= y) {
int index = myArraySize * (y - myImageY) / myImageHeight;
if (index < myArraySize) return index;
}
return -1;
}
public int getErrorStripeCount() {
return myArraySize;
}
public void setErrorStripeCount(int count) {
if (count < 0) count = 0;
if (myArray == null) {
myArray = new Value[count];
}
else if (myArray.length < count) {
Value[] old = myArray;
myArray = new Value[count];
System.arraycopy(old, 0, myArray, 0, old.length);
}
if (myArraySize != count) {
myArraySize = count;
invalidate();
}
}
public boolean isModified() {
for (int index = 0; index < myArraySize; index++) {
Value value = myArray[index];
if (value != null && value.myModified) return true;
}
return false;
}
public void clear() {
for (int index = 0; index < myArraySize; index++) {
Value value = myArray[index];
if (value != null) value.set(null);
}
}
public void clear(int index) {
Value value = getValue(index, false);
if (value != null) value.set(null);
}
public ErrorStripe getErrorStripe(int index) {
Value value = getValue(index, false);
return value == null ? null : value.get();
}
public void setErrorStripe(int index, ErrorStripe stripe) {
Value value = getValue(index, stripe != null);
if (value != null) value.set(stripe);
}
public void addErrorStripe(int index, ErrorStripe stripe) {
Value value = getValue(index, stripe != null);
if (value != null) value.add(stripe);
}
private Value getValue(int index, boolean create) {
if (0 > index || index >= myArraySize) return null;
if (create && null == myArray[index]) {
myArray[index] = mySingleValue ? new SingleValue() : new ComplexValue();
}
return myArray[index];
}
private int getOffset(int height, int thickness) {
if (height > thickness) {
if (myAlignment == Alignment.CENTER) return (height - thickness) / 2;
if (myAlignment == Alignment.BOTTOM) return (height - thickness);
}
return 0;
}
private void updateImage(BufferedImage image, boolean force) {
int width = image.getWidth();
int height = image.getHeight();
Graphics2D g = image.createGraphics();
myImageHeight = 0;
int min = myMin + myGap;
int max = height / myArraySize;
if (max < min) {
max = height / min;
int currentIndex = 0;
SingleValue currentValue = new SingleValue();
for (int index = 0; index < myArraySize; index++) {
Value value = myArray[index];
int i = index * max / myArraySize;
if (i > currentIndex) {
currentValue.paint(g, 0, myImageHeight, width, min, force);
myImageHeight += min;
currentIndex = i;
currentValue.myStripe = value == null ? null : value.get();
currentValue.myModified = value != null && value.myModified;
}
else if (value != null) {
value.myModified = false;
ErrorStripe stripe = value.get();
if (stripe != null && stripe.compareTo(currentValue.myStripe) < 0) {
currentValue.myStripe = stripe;
}
if (value.myModified) {
currentValue.myModified = true;
}
}
if (value != null) {
value.myModified = false;
}
}
currentValue.paint(g, 0, myImageHeight, width, min, force);
myImageHeight += min;
}
else {
if (max > myMax) {
max = Math.max(myMax, min);
}
for (int index = 0; index < myArraySize; index++) {
Value value = myArray[index];
if (value != null) {
value.myModified = false;
value.paint(g, 0, myImageHeight, width, max, force);
}
myImageHeight += max;
}
}
g.dispose();
}
@Override
protected void updateImage(BufferedImage image) {
if (isModified()) updateImage(image, false);
}
@Override
protected BufferedImage createImage(int width, int height) {
BufferedImage image = myArraySize == 0 ? null : super.createImage(width, height);
if (image != null) updateImage(image, true);
return image;
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
myImageY = y;
super.paint(g, x, y, width, height);
}
private abstract static class Value implements RegionPainter {
boolean myModified;
abstract boolean set(ErrorStripe stripe);
abstract boolean add(ErrorStripe stripe);
abstract ErrorStripe get();
void paint(Graphics2D g, int x, int y, int width, int height, boolean force) {
if (force || myModified) {
if (!force) {
Composite old = g.getComposite();
g.setComposite(AlphaComposite.Clear);
g.fillRect(x, y, width, height);
g.setComposite(old);
}
paint(g, x, y, width, height);
}
}
}
private final class SingleValue extends Value {
private ErrorStripe myStripe;
@Override
boolean set(ErrorStripe stripe) {
if (stripe == null ? myStripe == null : stripe.equals(myStripe)) return false;
myStripe = stripe;
myModified = true;
return true;
}
@Override
boolean add(ErrorStripe stripe) {
if (stripe == null || stripe.compareTo(myStripe) >= 0) return false;
myStripe = stripe;
myModified = true;
return true;
}
@Override
ErrorStripe get() {
return myStripe;
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
myModified = false;
if (myStripe != null) {
int thickness = myAlignment != null ? myMin + myGap : height;
y += getOffset(height, thickness);
myStripe.paint(g, x, y, width, thickness - myGap);
}
}
}
private final class ComplexValue extends Value {
private TreeSet<ErrorStripe> mySet;
@Override
boolean set(ErrorStripe stripe) {
if (add(stripe)) return true;
if (mySet == null || mySet.isEmpty()) return false;
mySet.clear();
myModified = true;
return true;
}
@Override
boolean add(ErrorStripe stripe) {
if (stripe == null) return false;
if (mySet == null) mySet = new TreeSet<ErrorStripe>();
mySet.add(stripe);
myModified = true;
return true;
}
@Override
ErrorStripe get() {
if (mySet == null) return null;
Iterator<ErrorStripe> iterator = mySet.iterator();
return iterator.hasNext() ? iterator.next() : null;
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
myModified = false;
if (mySet != null) {
Iterator<ErrorStripe> iterator = mySet.iterator();
if (iterator.hasNext()) {
int thickness = myAlignment != null ? myMin + myGap : height;
if (thickness < height) {
int count = Math.min(height / thickness, mySet.size());
y += getOffset(height, thickness * count);
do {
iterator.next().paint(g, x, y, width, thickness - myGap);
y += thickness;
}
while (--count > 0 && iterator.hasNext());
}
else {
iterator.next().paint(g, x, y, width, thickness - myGap);
}
}
}
}
}
}
@@ -0,0 +1,87 @@
/*
* 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.ui.stripe;
import java.awt.Graphics;
/**
* @author Sergey.Malenkov
*/
public class ExtraErrorStripePainter extends ErrorStripePainter {
private final ErrorStripePainter myPainter = new ErrorStripePainter(true, null);
private boolean myGroupSwap;
private Integer myGroupGap;
public ExtraErrorStripePainter(boolean single) {
super(single);
}
public boolean isGroupSwap() {
return myGroupSwap;
}
public void setGroupSwap(boolean swap) {
myGroupSwap = swap;
}
public int getGroupGap() {
return myGroupGap;
}
public void setGroupGap(int gap) {
myGroupGap = gap;
}
public void setMaximalThickness(int thickness) {
super.setMaximalThickness(thickness);
myPainter.setMaximalThickness(getMaximalThickness());
}
public void setMinimalThickness(int thickness) {
super.setMinimalThickness(thickness);
myPainter.setMinimalThickness(getMinimalThickness() + getErrorStripeGap());
}
@Override
public void setErrorStripeGap(int gap) {
super.setErrorStripeGap(gap);
myPainter.setMinimalThickness(getMinimalThickness() + getErrorStripeGap());
}
public void setErrorStripeCount(int count) {
super.setErrorStripeCount(count);
myPainter.setErrorStripeCount(count);
}
public void setExtraStripe(int index, ErrorStripe stripe) {
myPainter.setErrorStripe(index, stripe);
}
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
int min = getMinimalThickness();
int gap = myGroupGap == null ? min : myGroupGap;
int pos = x;
if (myGroupSwap) {
pos += width - min;
}
else {
x += gap + min;
}
myPainter.paint(g, pos, y, min, height);
super.paint(g, x, y, width - gap - min, height);
}
}
@@ -0,0 +1,95 @@
/*
* 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.ui.stripe;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAwareAction;
import java.beans.EventHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
* @author Sergey.Malenkov
*/
public class TreeUpdater<Painter extends ErrorStripePainter> extends Updater<Painter> {
private final JTree myTree;
private final TreeModelListener myTreeModelListener = EventHandler.create(TreeModelListener.class, this, "update");
private final PropertyChangeListener myPropertyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
Object oldValue = event.getOldValue();
if (oldValue instanceof TreeModel) ((TreeModel)oldValue).removeTreeModelListener(myTreeModelListener);
Object newValue = event.getNewValue();
if (newValue instanceof TreeModel) ((TreeModel)newValue).addTreeModelListener(myTreeModelListener);
}
};
public TreeUpdater(Painter painter, JScrollPane pane, JTree tree) {
super(painter, pane);
myTree = tree;
myTree.addPropertyChangeListener(JTree.TREE_MODEL_PROPERTY, myPropertyChangeListener);
TreeModel model = myTree.getModel();
if (model != null) model.addTreeModelListener(myTreeModelListener);
new DumbAwareAction() {
@Override
public void actionPerformed(AnActionEvent event) {
selectNext(myTree.getMaxSelectionRow());
}
}.registerCustomShortcutSet(getNextErrorShortcut(), myTree, this);
new DumbAwareAction() {
@Override
public void actionPerformed(AnActionEvent event) {
selectPrevious(myTree.getMinSelectionRow());
}
}.registerCustomShortcutSet(getPreviousErrorShortcut(), myTree, this);
}
@Override
public void dispose() {
myTree.removePropertyChangeListener(JTree.TREE_MODEL_PROPERTY, myPropertyChangeListener);
TreeModel model = myTree.getModel();
if (model != null) model.removeTreeModelListener(myTreeModelListener);
super.dispose();
}
@Override
protected void onSelect(Painter painter, int index) {
if (0 <= index) {
myTree.setSelectionRow(index);
myTree.scrollRowToVisible(index);
}
}
protected void update(Painter painter) {
update(painter, myTree);
}
protected void update(Painter painter, JTree tree) {
int count = tree.getRowCount();
painter.setErrorStripeCount(count);
for (int index = 0; index < count; index++) {
TreePath path = tree.getPathForRow(index);
update(painter, index, path == null ? null : path.getLastPathComponent());
}
}
}
@@ -0,0 +1,221 @@
/*
* 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.ui.stripe;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.RegionPainter;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* @author Sergey.Malenkov
*/
public abstract class Updater<Painter extends ErrorStripePainter> implements Disposable {
private final Painter myPainter;
private final JScrollBar myScrollBar;
private final MergingUpdateQueue myQueue;
private final MouseAdapter myMouseAdapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent event) {
onMouseMove(myPainter, event.getX(), event.getY());
}
@Override
public void mouseClicked(MouseEvent event) {
onMouseClick(myPainter, event.getX(), event.getY());
}
};
protected Updater(@NotNull Painter painter, JScrollPane pane) {
myPainter = painter;
myScrollBar = pane.getVerticalScrollBar();
myScrollBar.addMouseListener(myMouseAdapter);
myScrollBar.addMouseMotionListener(myMouseAdapter);
myQueue = new MergingUpdateQueue("ErrorStripeUpdater", 100, true, myScrollBar, this);
UIUtil.putClientProperty(myScrollBar, RegionPainter.BUTTONLESS_SCROLL_BAR_UI_EXTRA_TRACK, new RegionPainter() {
@Override
public void paint(Graphics g, int x, int y, int width, int height) {
DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
myPainter.setMinimalThickness(settings == null ? 2 : Math.min(settings.ERROR_STRIPE_MARK_MIN_HEIGHT, JBUI.scale(4)));
myPainter.setErrorStripeGap(Registry.intValue("error.stripe.gap", 0));
myPainter.paint(g, x, y, width, height);
}
});
}
@Override
public void dispose() {
myScrollBar.removeMouseListener(myMouseAdapter);
myScrollBar.removeMouseMotionListener(myMouseAdapter);
UIUtil.putClientProperty(myScrollBar, RegionPainter.BUTTONLESS_SCROLL_BAR_UI_EXTRA_TRACK, null);
}
private int findErrorStripeIndex(Painter painter, int x, int y) {
int index = painter.findIndex(x, y);
if (null != painter.getErrorStripe(index)) return index;
index = painter.findIndex(x, y + 1);
if (null != painter.getErrorStripe(index)) return index;
index = painter.findIndex(x, y - 1);
if (null != painter.getErrorStripe(index)) return index;
index = painter.findIndex(x, y + 2);
if (null != painter.getErrorStripe(index)) return index;
return -1;
}
protected void onMouseMove(Painter painter, int x, int y) {
onMouseMove(painter, findErrorStripeIndex(painter, x, y));
}
protected void onMouseMove(Painter painter, int index) {
myScrollBar.setCursor(index < 0 ? null : Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
protected void onMouseClick(Painter painter, int x, int y) {
onMouseClick(painter, findErrorStripeIndex(painter, x, y));
}
protected void onMouseClick(Painter painter, int index) {
onSelect(painter, index);
}
protected void onSelect(Painter painter, int index) {
}
protected ShortcutSet getNextErrorShortcut() {
return new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts("GotoNextError"));
}
public void selectNext(int index) {
onSelect(myPainter, findNextIndex(index));
}
protected ShortcutSet getPreviousErrorShortcut() {
return new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts("GotoPreviousError"));
}
public void selectPrevious(int index) {
onSelect(myPainter, findPreviousIndex(index));
}
protected abstract void update(Painter painter);
protected void update(Painter painter, int index, Object object) {
painter.setErrorStripe(index, getErrorStripe(object));
}
protected ErrorStripe getErrorStripe(Object object) {
return null;
}
public final void update() {
myQueue.cancelAllUpdates();
myQueue.queue(new Update("update") {
@Override
public void run() {
update(myPainter);
if (myPainter.isModified()) {
myScrollBar.repaint();
}
}
});
}
public int findNextIndex(int current) {
int count = myPainter.getErrorStripeCount();
int foundIndex = -1;
int foundLayer = 0;
if (0 <= current && current < count) {
current++;
for (int index = current; index < count; index++) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
for (int index = 0; index < current; index++) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
}
else {
for (int index = 0; index < count; index++) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
}
return foundIndex;
}
public int findPreviousIndex(int current) {
int count = myPainter.getErrorStripeCount();
int foundIndex = -1;
int foundLayer = 0;
if (0 <= current && current < count) {
current--;
for (int index = count - 1; index >= 0; index++) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
for (int index = current - 1; index >= 0; index++) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
}
else {
for (int index = count - 1; index >= 0; index--) {
int layer = getLayer(index);
if (layer > foundLayer) {
foundIndex = index;
foundLayer = layer;
}
}
}
return foundIndex;
}
private int getLayer(int index) {
ErrorStripe stripe = myPainter.getErrorStripe(index);
return stripe == null ? -1 : stripe.getLayer();
}
}
@@ -606,6 +606,10 @@ editor.config.stop.at.project.root.description=Stops searching for .editorconfig
editor.bold.underline.height=2
editor.bold.underline.height.description=Underline height for EffectType.BOLD_LINE_UNDERSCORE
error.stripe.enabled=true
error.stripe.gap=1
error.stripe.gap.description=Minimal space between error stripe.
JDK8042508.bug.fixed=false
JDK8042508.bug.fixed.description=Disable check for type variable until javac bug is fixed