/*
* Copyright 2000-2012 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.roots.ui.configuration;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.GraphicsConfig;
import com.intellij.openapi.ui.MessageType;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.JBColor;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import com.intellij.util.ui.BaseButtonBehavior;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.TimedDeadzone;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
/**
* User: spLeaner
*/
public class ConfigurationErrorsComponent extends JPanel {
@NonNls private static final String FIX_ACTION_NAME = "FIX";
@NonNls private static final String NAVIGATE_ACTION_NAME = "NAVIGATE";
private final JBList myErrorList;
private final CollectionListModel<ConfigurationError> myModel;
public ConfigurationErrorsComponent(@NotNull final Project project) {
super(new BorderLayout());
myModel = new CollectionListModel<ConfigurationError>();
final JLabel label = new JLabel("<html><body><b>Problems:</b></body></html>");
label.setVisible(false);
add(label, BorderLayout.NORTH);
project.getMessageBus().connect().subscribe(ConfigurationErrors.TOPIC, new ConfigurationErrors() {
@Override
public void addError(@NotNull ConfigurationError error) {
int elementIndex = myModel.getElementIndex(error);
if (elementIndex != -1) {
return;
}
myModel.add(error);
label.setVisible(myModel.getSize() != 0);
}
@Override
public void removeError(@NotNull ConfigurationError error) {
myModel.remove(error);
label.setVisible(myModel.getSize() != 0);
}
});
myErrorList = new JBList(myModel);
myErrorList.setEmptyText("");
myErrorList.setBackground(UIUtil.getPanelBackground());
myErrorList.setCellRenderer(new ErrorListRenderer(myErrorList));
myErrorList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
if (!e.isPopupTrigger()) {
processListMouseEvent(e, true);
}
}
});
myErrorList.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
if (!e.isPopupTrigger()) {
processListMouseEvent(e, false);
}
}
});
add(myErrorList, BorderLayout.CENTER);
}
private abstract static class ToolbarAlikeButton extends JComponent {
private BaseButtonBehavior myBehavior;
private Icon myIcon;
private String myName;
ToolbarAlikeButton(@NotNull final Icon icon, @NotNull final String name) {
this(icon);
myName = name;
}
ToolbarAlikeButton(@NotNull final Icon icon) {
myIcon = icon;
myBehavior = new BaseButtonBehavior(this, TimedDeadzone.NULL) {
@Override
protected void execute(MouseEvent e) {
onClick(e);
}
};
setOpaque(false);
}
public String getButtonName() {
return myName;
}
public void onClick(MouseEvent e) {
}
@Override
public Insets getInsets() {
return JBUI.insets(2);
}
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
@Override
public Dimension getMinimumSize() {
final Insets insets = getInsets();
return new Dimension(myIcon.getIconWidth() + insets.left + insets.right, myIcon.getIconHeight() + insets.top + insets.bottom);
}
@Override
public void paint(final Graphics g) {
final Insets insets = getInsets();
final Dimension d = getSize();
int x = (d.width - myIcon.getIconWidth() - insets.left - insets.right) / 2;
int y = (d.height - myIcon.getIconHeight() - insets.top - insets.bottom) / 2;
if (myBehavior.isHovered()) {
// todo
}
if (myBehavior.isPressedByMouse()) {
x += 1;
y += 1;
}
myIcon.paintIcon(this, g, x + insets.left, y + insets.top);
}
}
private void processListMouseEvent(final MouseEvent e, final boolean click) {
final int index = myErrorList.locationToIndex(e.getPoint());
if (index > -1) {
final Object value = myErrorList.getModel().getElementAt(index);
if (value != null && value instanceof ConfigurationError) {
final ConfigurationError error = (ConfigurationError)value;
final Component renderer = myErrorList.getCellRenderer().getListCellRendererComponent(myErrorList, value, index, false, false);
if (renderer instanceof ErrorListRenderer) {
final Rectangle bounds = myErrorList.getCellBounds(index, index);
renderer.setBounds(bounds);
renderer.doLayout();
final Point point = e.getPoint();
point.translate(-bounds.x, -bounds.y);
final Component deepestComponentAt = SwingUtilities.getDeepestComponentAt(renderer, point.x, point.y);
if (deepestComponentAt instanceof ToolbarAlikeButton) {
final String name = ((ToolbarAlikeButton)deepestComponentAt).getButtonName();
if (click) {
if (FIX_ACTION_NAME.equals(name)) {
onClickFix(error, (JComponent)deepestComponentAt, e);
}
else if (NAVIGATE_ACTION_NAME.equals(name)) {
error.navigate();
}
else {
error.ignore(!error.isIgnored());
myModel.contentsChanged(error);
}
}
else {
myErrorList.setToolTipText(FIX_ACTION_NAME.equals(name)
? "Fix"
: NAVIGATE_ACTION_NAME.equals(name)
? "Navigate to the problem"
: error.isIgnored() ? "Not ignore this error" : "Ignore this error");
return;
}
}
else {
if (e.getClickCount() == 2) {
error.navigate();
}
}
}
}
}
myErrorList.setToolTipText(null);
}
private void onClickFix(@NotNull final ConfigurationError error, JComponent component, MouseEvent e) {
error.fix(component, new RelativePoint(e));
}
private static class ErrorListRenderer extends JComponent implements ListCellRenderer {
private JTextPane myText;
private JTextPane myFakeTextPane;
private JViewport myFakeViewport;
private JList myList;
private JPanel myButtonsPanel;
private JPanel myFixGroup;
private ErrorListRenderer(@NotNull final JList list) {
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
setOpaque(false);
myList = list;
myText = new JTextPane();
myButtonsPanel = new JPanel(new BorderLayout());
myButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 3, 5, 3));
myButtonsPanel.setOpaque(false);
final JPanel buttons = new JPanel();
buttons.setOpaque(false);
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
myButtonsPanel.add(buttons, BorderLayout.NORTH);
add(myButtonsPanel, BorderLayout.EAST);
myFixGroup = new JPanel();
myFixGroup.setOpaque(false);
myFixGroup.setLayout(new BoxLayout(myFixGroup, BoxLayout.Y_AXIS));
myFixGroup.add(new ToolbarAlikeButton(AllIcons.Actions.QuickfixBulb, FIX_ACTION_NAME) {
});
myFixGroup.add(Box.createHorizontalStrut(3));
buttons.add(myFixGroup);
buttons.add(new ToolbarAlikeButton(AllIcons.General.AutoscrollToSource, NAVIGATE_ACTION_NAME) {
});
buttons.add(Box.createHorizontalStrut(3));
buttons.add(new ToolbarAlikeButton(AllIcons.Actions.Cancel, "IGNORE") {
});
myFakeTextPane = new JTextPane();
myText.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
myFakeTextPane.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
myText.setOpaque(false);
if (UIUtil.isUnderNimbusLookAndFeel()) {
myText.setBackground(UIUtil.TRANSPARENT_COLOR);
}
myText.setEditable(false);
myFakeTextPane.setEditable(false);
myText.setEditorKit(UIUtil.getHTMLEditorKit());
myFakeTextPane.setEditorKit(UIUtil.getHTMLEditorKit());
myFakeViewport = new JViewport();
myFakeViewport.setView(myFakeTextPane);
add(myText, BorderLayout.CENTER);
}
@Override
public Dimension getPreferredSize() {
final Container parent = myList.getParent();
if (parent != null) {
myFakeTextPane.setText(myText.getText());
final Dimension size = parent.getSize();
myFakeViewport.setSize(size);
final Dimension preferredSize = myFakeTextPane.getPreferredSize();
final Dimension buttonsPrefSize = myButtonsPanel.getPreferredSize();
final int maxHeight = Math.max(buttonsPrefSize.height, preferredSize.height);
final Insets insets = getInsets();
return new Dimension(Math.min(size.width, preferredSize.width), maxHeight + insets.top + insets.bottom);
}
return super.getPreferredSize();
}
@Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
final ConfigurationError error = (ConfigurationError)value;
myList = list;
myFixGroup.setVisible(error.canBeFixed());
myText.setText(error.getDescription());
setBackground(error.isIgnored() ? MessageType.WARNING.getPopupBackground() : MessageType.ERROR.getPopupBackground());
return this;
}
@Override
protected void paintComponent(Graphics g) {
final Graphics2D g2d = (Graphics2D)g;
final Rectangle bounds = getBounds();
final Insets insets = getInsets();
final GraphicsConfig cfg = new GraphicsConfig(g);
cfg.setAntialiasing(true);
final Shape shape = new RoundRectangle2D.Double(insets.left, insets.top, bounds.width - 1 - insets.left - insets.right,
bounds.height - 1 - insets.top - insets.bottom, 6, 6);
g2d.setColor(JBColor.WHITE);
g2d.fill(shape);
Color bgColor = getBackground();
g2d.setColor(bgColor);
g2d.fill(shape);
g2d.setColor(getBackground().darker());
g2d.draw(shape);
cfg.restore();
super.paintComponent(g);
}
}
}