mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-148164 Preliminary support of error stripes in the project view tree
This commit is contained in:
+50
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user