/*
* Copyright 2003-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 jetbrains.mps.nodeEditor.cellMenu;
import com.intellij.util.ui.UIUtil;
import jetbrains.mps.ide.icons.IconManager;
import jetbrains.mps.ide.icons.IdeIcons;
import jetbrains.mps.nodeEditor.EditorSettings;
import jetbrains.mps.nodeEditor.SubstituteActionUtil;
import jetbrains.mps.nodeEditor.cells.FontRegistry;
import jetbrains.mps.openapi.editor.cells.SubstituteAction;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.smodel.SNodeUtil;
import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration;
import jetbrains.mps.smodel.presentation.NodePresentationUtil;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class NodeItemCellRenderer extends JPanel implements ListCellRenderer {
private static final int MY_MIN_CELL_WIDTH = 300;
private static final Logger LOG = LogManager.getLogger(NodeItemCellRenderer.class);
public static final String EXCEPTION_WAS_THROWN_TEXT = "!Exception was thrown!";
private JLabel myLeft = new JLabel("", JLabel.LEFT);
private JLabel myRight = new JLabel("", JLabel.RIGHT);
private static final int HORIZONTAL_GAP = 10;
private boolean myLightweightMode = false;
private final Color HIGHLIGHT_COLOR = UIUtil.isUnderDarcula() ? new Color(217, 149, 219) : new Color(189, 55, 186);
private final Color SELECTION_HIGHLIGHT_COLOR = UIUtil.isUnderDarcula() ? HIGHLIGHT_COLOR : new Color(250, 239, 215);
private final String STRING_HIGHLIGHT_COLOR = colorToHtml(HIGHLIGHT_COLOR);
private final String STRING_SELECTION_HIGHLIGHT_COLOR = colorToHtml(SELECTION_HIGHLIGHT_COLOR);
private int myOldStyle = Font.PLAIN;
private Map<SNode, Icon> myNodeIconMap = new HashMap<SNode, Icon>();
private Map<SNode, Icon> myConceptIconMap = new HashMap<SNode, Icon>();
private static final int MY_MIN_CELL_HEIGHT_TO_ADD = 2;
private final NodeSubstituteChooser mySubstituteChooser;
private int myMaxWidth = 0;
private int myMaxHeight = 0;
NodeItemCellRenderer(@NotNull NodeSubstituteChooser substituteChooser) {
mySubstituteChooser = substituteChooser;
setLayout(new BorderLayout(HORIZONTAL_GAP / 2, 0));
myLeft.setFont(EditorSettings.getInstance().getDefaultEditorFont());
myRight.setFont(EditorSettings.getInstance().getDefaultEditorFont());
add(myLeft, BorderLayout.WEST);
add(myRight, BorderLayout.EAST);
}
@Override
public Component getListCellRendererComponent(final JList list, final Object value, int index, final boolean isSelected, boolean cellHasFocus) {
ModelAccess.instance().runReadAction(new Runnable() {
@Override
public void run() {
setupThis(list, value, isSelected);
}
});
return this;
}
@NotNull
Dimension getMaxDimension(List<SubstituteAction> actions) {
int counter = 0;
myMaxHeight = 0;
myMaxWidth = 0;
for (SubstituteAction action : actions) {
counter++;
Dimension dimension = getDimension(action);
myMaxWidth = Math.max(myMaxWidth, dimension.width);
myMaxHeight = Math.max(myMaxHeight, dimension.height);
if (counter == NodeSubstituteChooser.MAX_LOOKUP_LIST_HEIGHT) {
break;
}
}
return new Dimension(myMaxWidth, myMaxHeight);
}
// TODO: use setupThis() & getPreferredSize() instead?
private Dimension getDimension(SubstituteAction action) {
String pattern = mySubstituteChooser.getPatternEditor().getPattern();
String matchingText;
try {
matchingText = action.getVisibleMatchingText(pattern);
} catch (Throwable t) {
matchingText = EXCEPTION_WAS_THROWN_TEXT;
}
String descriptionText;
try {
descriptionText = action.getDescriptionText(pattern);
} catch (Throwable t) {
descriptionText = EXCEPTION_WAS_THROWN_TEXT;
}
int itemWidth = 0;
int itemHeight = 0;
try {
Icon icon = getIcon(action, pattern);
itemWidth += icon.getIconWidth();
itemHeight += icon.getIconHeight();
} catch (Throwable t) {
LOG.error(null, t);
}
Font font = getFont(action);
FontMetrics fontMetrics = FontRegistry.getInstance().getFontMetrics(font);
itemWidth += myLeft.getIconTextGap();
if (matchingText != null) {
itemWidth += fontMetrics.stringWidth(matchingText);
}
itemWidth += HORIZONTAL_GAP;
if (descriptionText != null) {
itemWidth += fontMetrics.stringWidth(descriptionText);
}
itemHeight = Math.max(itemHeight, fontMetrics.getHeight()) + MY_MIN_CELL_HEIGHT_TO_ADD;
itemWidth = Math.max(itemWidth, MY_MIN_CELL_WIDTH);
return new Dimension(itemWidth, itemHeight);
}
private void setupThis(JList list, Object value, boolean isSelected) {
SubstituteAction action = (SubstituteAction) value;
String pattern = mySubstituteChooser.getPatternEditor().getPattern();
if (!myLightweightMode) {
try {
Icon icon = getIcon(action, pattern);
myLeft.setIcon(icon);
} catch (Throwable t) {
LOG.error(null, t);
}
}
try {
int style = getStyle(action);
if (myOldStyle != style) {
Font font = EditorSettings.getInstance().getDefaultEditorFont().deriveFont(style);
myLeft.setFont(font);
myRight.setFont(font);
myOldStyle = style;
}
} catch (Throwable t) {
LOG.error(null, t);
}
try {
if (!isSelected) {
myLeft.setText(SubstituteActionUtil.createText(action, pattern, STRING_HIGHLIGHT_COLOR));
} else {
myLeft.setText(SubstituteActionUtil.createText(action, pattern, STRING_SELECTION_HIGHLIGHT_COLOR));
}
} catch (Throwable t) {
myLeft.setText(EXCEPTION_WAS_THROWN_TEXT);
LOG.error(null, t);
}
try {
myRight.setText(action.getDescriptionText(pattern));
} catch (Throwable t) {
myRight.setText(EXCEPTION_WAS_THROWN_TEXT);
LOG.error(null, t);
}
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
myLeft.setForeground(list.getSelectionForeground());
myRight.setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
myLeft.setForeground(list.getForeground());
myRight.setForeground(list.getForeground());
}
Dimension preferredSize = getPreferredSize();
if (myMaxHeight < preferredSize.height || myMaxWidth < preferredSize.width) {
myMaxWidth = Math.max(myMaxWidth, preferredSize.width);
myMaxHeight = Math.max(myMaxHeight, preferredSize.height);
mySubstituteChooser.getPopupWindow().updateListDimension(new Dimension(myMaxWidth, myMaxHeight));
}
}
private int getStyle(SubstituteAction action) {
int style;
final Object parameterObject = action.getParameterObject();
if (parameterObject instanceof SNode) {
style = NodePresentationUtil.getFontStyle(action.getSourceNode(), (SNode) parameterObject);
} else {
style = Font.PLAIN;
}
return style;
}
private Font getFont(SubstituteAction action) {
Font font = EditorSettings.getInstance().getDefaultEditorFont();
try {
int style = getStyle(action);
font = font.deriveFont(style);
} catch (Throwable t) {
LOG.error(null, t);
}
return font;
}
private Icon getIcon(SubstituteAction action, String pattern) {
Icon icon = null;
if (action instanceof CompletionActionItemAsSubstituteAction){
icon = IconManager.getIconForResource(((CompletionActionItemAsSubstituteAction) action).getIcon(pattern));
}
if (icon != null) {
return icon;
}
SNode iconNode = action.getIconNode(pattern);
if (iconNode != null) {
boolean isConcept = SNodeUtil.isInstanceOfConceptDeclaration(iconNode) && !(action.isReferentPresentation());
if (isConcept) {
icon = myConceptIconMap.get(iconNode);
} else {
icon = myNodeIconMap.get(iconNode);
}
if (icon == null) {
if (isConcept) {
icon = IconManager.getIcon(MetaAdapterByDeclaration.getConcept(iconNode));
if (icon==null){
icon = IdeIcons.DEFAULT_NODE_ICON;
}
myConceptIconMap.put(iconNode, icon);
} else {
icon = IconManager.getIconFor(iconNode);
myNodeIconMap.put(iconNode, icon);
}
}
}
if (icon == null) {
icon = IdeIcons.DEFAULT_ICON;
}
return icon;
}
private String colorToHtml(Color color) {
String rgb = Integer.toHexString(color.getRGB());
return rgb.substring(2, rgb.length());
}
void setLightweightMode(boolean isLightweightMode) {
myLightweightMode = isLightweightMode;
}
}