blob: 97fd0feaf0c8decc9c96f684751c0e03cbc4c961 [file] [log] [blame]
/*
* Copyright 2000-2014 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.usages.impl;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.DarculaColors;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
import com.intellij.usageView.UsageTreeColors;
import com.intellij.usageView.UsageTreeColorsScheme;
import com.intellij.usageView.UsageViewBundle;
import com.intellij.usages.*;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
/**
* @author max
*/
class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer {
private static final Logger LOG = Logger.getInstance("#com.intellij.usages.impl.UsageViewTreeCellRenderer");
private static final EditorColorsScheme ourColorsScheme = UsageTreeColorsScheme.getInstance().getScheme();
private static final SimpleTextAttributes ourInvalidAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.INVALID_PREFIX));
private static final SimpleTextAttributes ourReadOnlyAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.READONLY_PREFIX));
private static final SimpleTextAttributes ourNumberOfUsagesAttribute = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.NUMBER_OF_USAGES));
private static final SimpleTextAttributes ourInvalidAttributesDarcula = new SimpleTextAttributes(null, DarculaColors.RED, null, ourInvalidAttributes.getStyle());
public static final Insets STANDARD_IPAD_NOWIFI = new Insets(1, 2, 1, 2);
private static final Rectangle EMPTY_RECTANGLE = new Rectangle();
private boolean myRowBoundsCalled = false;
private final UsageViewPresentation myPresentation;
private final UsageView myView;
UsageViewTreeCellRenderer(@NotNull UsageView view) {
myView = view;
myPresentation = view.getPresentation();
}
private Dimension cachedPreferredSize;
@Override
public void customizeCellRenderer(@Nullable JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
boolean showAsReadOnly = false;
if (value instanceof Node && tree != null && value != tree.getModel().getRoot()) {
Node node = (Node)value;
if (!node.isValid()) {
append(UsageViewBundle.message("node.invalid") + " ", UIUtil.isUnderDarcula() ? ourInvalidAttributesDarcula : ourInvalidAttributes);
}
if (myPresentation.isShowReadOnlyStatusAsRed() && node.isReadOnly()) {
showAsReadOnly = true;
}
}
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
Object userObject = treeNode.getUserObject();
Rectangle visibleRect = tree == null ? EMPTY_RECTANGLE : ((JViewport)tree.getParent()).getViewRect();
if (!visibleRect.isEmpty()) {
//Protection against SOE on some OSes and JDKs IDEA-120631
RowLocation visible = myRowBoundsCalled ? RowLocation.INSIDE_VISIBLE_RECT : isRowVisible(row, visibleRect);
myRowBoundsCalled = false;
if (visible != RowLocation.INSIDE_VISIBLE_RECT) {
// for the node outside visible rect just set its preferred size to the whole visible rect
// and do not compute (expensive) presentation
setIpad(new Insets(1,visibleRect.width, 1, 0));
return;
}
if (!getIpad().equals(STANDARD_IPAD_NOWIFI)) {
// for the visible node, return its ipad to the standard value
setIpad(STANDARD_IPAD_NOWIFI);
}
}
if (userObject instanceof UsageTarget) {
UsageTarget usageTarget = (UsageTarget)userObject;
if (!usageTarget.isValid()) {
append(UsageViewBundle.message("node.invalid"), ourInvalidAttributes);
return;
}
final ItemPresentation presentation = usageTarget.getPresentation();
LOG.assertTrue(presentation != null);
if (showAsReadOnly) {
append(UsageViewBundle.message("node.readonly") + " ", ourReadOnlyAttributes);
}
final String text = presentation.getPresentableText();
append(text == null ? "" : text, SimpleTextAttributes.REGULAR_ATTRIBUTES);
setIcon(presentation.getIcon(expanded));
}
else if (treeNode instanceof GroupNode) {
GroupNode node = (GroupNode)treeNode;
if (node.isRoot()) {
append(StringUtil.capitalize(myPresentation.getUsagesWord()), patchAttrs(node, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES));
}
else {
append(node.getGroup().getText(myView),
patchAttrs(node, showAsReadOnly ? ourReadOnlyAttributes : SimpleTextAttributes.REGULAR_ATTRIBUTES));
setIcon(node.getGroup().getIcon(expanded));
}
int count = node.getRecursiveUsageCount();
append(" (" + StringUtil.pluralize(count + " " + myPresentation.getUsagesWord(), count) + ")",
patchAttrs(node, ourNumberOfUsagesAttribute));
}
else if (treeNode instanceof UsageNode) {
UsageNode node = (UsageNode)treeNode;
setIcon(node.getUsage().getPresentation().getIcon());
if (showAsReadOnly) {
append(UsageViewBundle.message("node.readonly") + " ", patchAttrs(node, ourReadOnlyAttributes));
}
if (node.isValid()) {
TextChunk[] text = node.getUsage().getPresentation().getText();
for (TextChunk textChunk : text) {
SimpleTextAttributes simples = textChunk.getSimpleAttributesIgnoreBackground();
append(textChunk.getText(), patchAttrs(node, simples));
}
}
}
else if (userObject instanceof String) {
append((String)userObject, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
else {
append(userObject == null ? "" : userObject.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
else {
append(value.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
if (tree != null) {
SpeedSearchUtil.applySpeedSearchHighlighting(tree, this, true, mySelected);
}
}
// computes the node text regardless of the node visibility
@NotNull
public String getPlainTextForNode(Object value) {
boolean showAsReadOnly = false;
StringBuilder result = new StringBuilder();
if (value instanceof Node) {
Node node = (Node)value;
if (!node.isValid()) {
result.append(UsageViewBundle.message("node.invalid")).append(" ");
}
if (myPresentation.isShowReadOnlyStatusAsRed() && node.isReadOnly()) {
showAsReadOnly = true;
}
}
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
Object userObject = treeNode.getUserObject();
if (userObject instanceof UsageTarget) {
UsageTarget usageTarget = (UsageTarget)userObject;
if (usageTarget.isValid()) {
final ItemPresentation presentation = usageTarget.getPresentation();
LOG.assertTrue(presentation != null);
if (showAsReadOnly) {
result.append(UsageViewBundle.message("node.readonly")).append(" ");
}
final String text = presentation.getPresentableText();
result.append(text == null ? "" : text);
}
else {
result.append(UsageViewBundle.message("node.invalid"));
}
}
else if (treeNode instanceof GroupNode) {
GroupNode node = (GroupNode)treeNode;
if (node.isRoot()) {
result.append(StringUtil.capitalize(myPresentation.getUsagesWord()));
}
else {
result.append(node.getGroup().getText(myView));
}
int count = node.getRecursiveUsageCount();
result.append(" (" + StringUtil.pluralize(count + " " + myPresentation.getUsagesWord(), count) + ")");
}
else if (treeNode instanceof UsageNode) {
UsageNode node = (UsageNode)treeNode;
if (showAsReadOnly) {
result.append(UsageViewBundle.message("node.readonly")).append(" ");
}
if (node.isValid()) {
TextChunk[] text = node.getUsage().getPresentation().getText();
for (TextChunk textChunk : text) {
result.append(textChunk.getText());
}
}
}
else if (userObject instanceof String) {
result.append((String)userObject);
}
else {
result.append(userObject == null ? "" : userObject.toString());
}
}
else {
result.append(value);
}
return result.toString();
}
enum RowLocation {
BEFORE_VISIBLE_RECT, INSIDE_VISIBLE_RECT, AFTER_VISIBLE_RECT
}
@NotNull
public RowLocation isRowVisible(int row, @NotNull Rectangle visibleRect) {
Dimension pref;
if (cachedPreferredSize == null) {
cachedPreferredSize = pref = getPreferredSize();
}
else {
pref = cachedPreferredSize;
}
pref.width = Math.max(visibleRect.width, pref.width);
myRowBoundsCalled = true;
JTree tree = getTree();
final Rectangle bounds = tree == null ? null : tree.getRowBounds(row);
myRowBoundsCalled = false;
int y = bounds == null ? 0 : bounds.y;
TextRange vis = TextRange.from(Math.max(0, visibleRect.y - pref.height), visibleRect.height + pref.height * 2);
boolean inside = vis.contains(y);
if (inside) {
return RowLocation.INSIDE_VISIBLE_RECT;
}
return y < vis.getStartOffset() ? RowLocation.BEFORE_VISIBLE_RECT : RowLocation.AFTER_VISIBLE_RECT;
}
private static SimpleTextAttributes patchAttrs(@NotNull Node node, @NotNull SimpleTextAttributes original) {
if (node.isExcluded()) {
original = new SimpleTextAttributes(original.getStyle() | SimpleTextAttributes.STYLE_STRIKEOUT, original.getFgColor(), original.getWaveColor());
}
if (node instanceof GroupNode) {
UsageGroup group = ((GroupNode)node).getGroup();
FileStatus fileStatus = group != null ? group.getFileStatus() : null;
if (fileStatus != null && fileStatus != FileStatus.NOT_CHANGED) {
original = new SimpleTextAttributes(original.getStyle(), fileStatus.getColor(), original.getWaveColor());
}
DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
if (parent != null && parent.isRoot()) {
original = new SimpleTextAttributes(original.getStyle() | SimpleTextAttributes.STYLE_BOLD, original.getFgColor(), original.getWaveColor());
}
}
return original;
}
static String getTooltipFromPresentation(final Object value) {
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
if (treeNode instanceof UsageNode) {
UsageNode node = (UsageNode)treeNode;
return node.getUsage().getPresentation().getTooltipText();
}
}
return null;
}
}