/*
* Copyright 2013 original Randori IntelliJ Plugin authors.
*
* 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 randori.plugin.ui;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.problems.annotations.DefaultSeverity;
import randori.plugin.components.RandoriProjectComponent;
import randori.plugin.service.ProblemsService;
import randori.plugin.service.ProblemsService.OnProblemServiceListener;
import randori.plugin.util.ProjectUtils;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.ui.content.ContentManager;
import com.intellij.ui.table.JBTable;
import randori.plugin.ui.icons.RandoriIcons;
// TODO added an error log to the ProblemsService, the collection should not
// contain null File instances on CompilerProblems, left null checks in
/**
* @author Michael Schmalle
*/
public class ProblemsToolWindow
{
private static final String[] COLUMN_TITLES = new String[] { "Description",
"Resource", "Path", "Location", "Type" };
private static ProblemsToolWindow instance;
private final List<Integer> columnSizes;
@SuppressWarnings("unused")
private final ProblemsService service;
private final ContentManager contentManager;
@SuppressWarnings("unused")
private ToolWindow window;
private JBTable table;
private JPanel jPanel;
public ProblemsToolWindow(ToolWindow window, ProblemsService service,
List<Integer> columnSizes)
{
instance = this;
this.window = window;
this.service = service;
this.columnSizes = columnSizes;
service.addListener(new OnProblemServiceListener() {
@Override
public void onReset()
{
refreshTree(new HashSet<ICompilerProblem>());
}
@Override
public void onChange(Set<ICompilerProblem> problems)
{
refreshTree(problems);
}
});
contentManager = window.getContentManager();
create();
refreshTree(service.getProblems());
}
public static ProblemsToolWindow getInstance()
{
return instance;
}
private ProblemsTableModel getModel()
{
return (ProblemsTableModel) table.getModel();
}
private void create()
{
contentManager.removeAllContents(true);
String tableName = "Randori Project";
jPanel = new JPanel();
jPanel.setLayout(new BorderLayout());
// create a table with columns and rows
table = new JBTable();
table.setCellSelectionEnabled(false);
table.setAutoCreateRowSorter(true);
// the scrollpane holds the table which is layed out FULL and will
// not fit inside the parent Panel without scroll
JBScrollPane jbScrollPane = new JBScrollPane(table);
jbScrollPane.getVerticalScrollBar().setUnitIncrement(16);
jPanel.add(jbScrollPane, BorderLayout.CENTER);
Content content = ContentFactory.SERVICE.getInstance().createContent(
jPanel, tableName, false);
contentManager.addContent(content);
ListSelectionModel listMod = table.getSelectionModel();
listMod.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
listMod.addListSelectionListener(new TableMouseListener());
table.setEnableAntialiasing(true);
table.setCellSelectionEnabled(false);
table.setRowSelectionAllowed(true);
table.getTableHeader().setReorderingAllowed(false);
table.setModel(new ProblemsTableModel());
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount() == 2)
{
handleDoubleClick(e);
}
}
});
table.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e)
{
if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED
&& (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0)
{
saveColumnWidths(table, columnSizes);
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e)
{
int r = table.rowAtPoint(e.getPoint());
if (r >= 0 && r < table.getRowCount())
{
table.setRowSelectionInterval(r, r);
}
else
{
table.clearSelection();
}
int rowIndex = table.getSelectedRow();
if (rowIndex < 0)
return;
if (e.isPopupTrigger() && e.getComponent() instanceof JBTable)
{
JPopupMenu popupMenu = createPopupMenu(rowIndex);
if (popupMenu != null)
{
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
});
}
private JPopupMenu createPopupMenu(final int rowIndex)
{
String menuTitle = generateMenuTitle(rowIndex);
if (menuTitle != null)
{
JPopupMenu popupMenu = new JPopupMenu();
ActionListener menuListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event)
{
jumpToProblemInFile(rowIndex);
}
};
JMenuItem item;
popupMenu.add(item = new JMenuItem(menuTitle,
RandoriIcons.JumpToArrow));
item.setHorizontalTextPosition(SwingConstants.RIGHT);
item.addActionListener(menuListener);
return popupMenu;
}
return null;
}
private String generateMenuTitle(int rowIndex)
{
rowIndex = table.convertRowIndexToModel(rowIndex);
ProblemsTableModel model = (ProblemsTableModel) table.getModel();
ICompilerProblem problem = model.getProblemAt(rowIndex);
if (problem != null)
{
return "Jump to "
+ model.getSeverity(problem).toLowerCase() + " on line "
+ problem.getLine() + " in " + model.getName(problem);
}
return null;
}
private void saveColumnWidths(JBTable table, List<Integer> columnSizes)
{
for (int i = 0; i < columnSizes.size(); i++)
{
TableColumn column = table.getColumnModel().getColumn(i);
columnSizes.set(i, column.getWidth());
}
}
private void setColumnWidths(JBTable table, List<Integer> columnSizes)
{
for (int i = 0; i < columnSizes.size(); i++)
{
TableColumn column = table.getColumnModel().getColumn(i);
column.setPreferredWidth(columnSizes.get(i));
}
table.doLayout();
}
void refreshTree(Set<ICompilerProblem> problems)
{
saveColumnWidths(table, columnSizes);
if (columnSizes.size() != 0 && columnSizes.get(0) > -1)
{
setColumnWidths(table, columnSizes);
}
getModel().setProblems(new ArrayList<ICompilerProblem>(problems));
// allows the table to be updated without resetting the columns
getModel().fireTableDataChanged();
}
private void handleDoubleClick(MouseEvent e)
{
int rowIndex = table.rowAtPoint(e.getPoint());
if (rowIndex > -1)
{
jumpToProblemInFile(rowIndex);
}
}
private void jumpToProblemInFile(int rowIndex)
{
rowIndex = table.convertRowIndexToModel(rowIndex);
ProblemsTableModel model = (ProblemsTableModel) table.getModel();
ICompilerProblem problem = model.getProblemAt(rowIndex);
if (!isValid(problem))
return;
RandoriProjectComponent component = ProjectUtils.findProjectComponent(
jPanel, RandoriProjectComponent.class);
component.openFileForProblem(problem);
}
private boolean isValid(ICompilerProblem problem)
{
return problem.getLine() != -1;
}
@SuppressWarnings("serial")
private static class IconRenderer extends DefaultTableCellRenderer
{
@Override
public void setValue(Object value)
{
if (value == null)
{
setText("");
}
else
{
setIcon((Icon) value);
}
}
}
@SuppressWarnings("serial")
public static class ProblemsTableModel extends AbstractTableModel
{
private List<ICompilerProblem> problems;
public ProblemsTableModel()
{
}
void setProblems(List<ICompilerProblem> problems)
{
this.problems = problems;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch (columnIndex)
{
case 0:
case 1:
case 2:
case 4:
return String.class;
case 3:
return Integer.class;
}
return Object.class;
}
@Override
public int getRowCount()
{
if (problems == null)
return 0;
return problems.size();
}
@Override
public String getColumnName(int column)
{
return COLUMN_TITLES[column];
}
@Override
public int getColumnCount()
{
return COLUMN_TITLES.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
return getProblemValue(columnIndex, problems.get(rowIndex));
}
private String getProblemValue(int index, ICompilerProblem problem)
{
if (problem != null)
{
switch (index)
{
case 0:
return problem.toString();
case 1:
return getName(problem);
case 2:
return getPath(problem);
case 3:
return Integer.toString(problem.getLine());
case 4:
return getSeverity(problem);
}
}
return "Not found";
}
public String getSeverity(ICompilerProblem problem)
{
if (problem == null)
return "";
DefaultSeverity defaultSeverity = problem.getClass().getAnnotation(
DefaultSeverity.class);
return defaultSeverity.value().toString();
}
private String getPath(ICompilerProblem problem)
{
String result = "";
if (problem != null)
{
File file = new File(problem.getSourcePath());
if (file.exists())
result = file.getParent();
}
return result;
}
public String getName(ICompilerProblem problem)
{
String result = "";
if (problem != null)
{
File file = new File(problem.getSourcePath());
if (file.exists())
result = file.getName();
}
return result;
}
public ICompilerProblem getProblemAt(int row)
{
// TODO the problems should NEVER be null, fix this
return (problems != null) ? problems.get(row) : null;
}
}
private class TableMouseListener implements ListSelectionListener
{
@Override
public void valueChanged(ListSelectionEvent e)
{
}
}
}