| package com.android.tools.idea.editors.vmtrace.treemodel; |
| |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.ColoredTableCellRenderer; |
| import com.intellij.ui.SimpleTextAttributes; |
| import com.intellij.ui.TableSpeedSearch; |
| import com.intellij.ui.treeStructure.treetable.TreeTable; |
| |
| import javax.swing.*; |
| import javax.swing.table.*; |
| import java.awt.*; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.text.NumberFormat; |
| import java.util.Locale; |
| |
| public class VmStatsTreeUtils { |
| public static void adjustTableColumnWidths(TreeTable table) { |
| TableColumnModel columnModel = table.getColumnModel(); |
| FontMetrics fm = table.getFontMetrics(table.getFont()); |
| |
| for (int i = 0; i < table.getColumnCount(); i++) { |
| TableColumn column = columnModel.getColumn(i); |
| column.setPreferredWidth(fm.stringWidth(getSampleTextForColumn(i))); |
| } |
| } |
| |
| private static String getSampleTextForColumn(int index) { |
| return StatsTableColumn.fromColumnIndex(index).getSampleText(); |
| } |
| |
| public static void setCellRenderers(TreeTable table) { |
| TableCellRenderer headerRenderer = new TableHeaderCellRenderer(table); |
| for (StatsTableColumn c: StatsTableColumn.values()) { |
| getTableColumn(table, c).setHeaderRenderer(headerRenderer); |
| } |
| |
| TableCellRenderer renderer = new ProfileTimeRenderer(); |
| getTableColumn(table, StatsTableColumn.INVOCATION_COUNT).setCellRenderer(renderer); |
| getTableColumn(table, StatsTableColumn.INCLUSIVE_TIME).setCellRenderer(renderer); |
| getTableColumn(table, StatsTableColumn.EXCLUSIVE_TIME).setCellRenderer(renderer); |
| } |
| |
| private static TableColumn getTableColumn(TreeTable table, StatsTableColumn column) { |
| return table.getColumnModel().getColumn(column.getColumnIndex()); |
| } |
| |
| public static void setSpeedSearch(TreeTable treeTable) { |
| new TableSpeedSearch(treeTable) { |
| @Override |
| protected boolean isMatchingElement(Object element, String pattern) { |
| String text = super.getElementText(element); |
| // match search as long as some portion of the text matches the pattern |
| return text != null && text.contains(pattern); |
| } |
| }; |
| } |
| |
| // Enable sorting in the vm stats tree table. |
| // Sorting is ill-defined in the context of a TreeTable since you can't sort across different levels in the |
| // hierarchy. As a result, we can't hook into JTable's sorting infrastructure and we roll our own. This |
| // implementation listens to mouse click events on the tree table's header, interprets them as sort requests |
| // and passes them on to the model. |
| public static void enableSorting(final TreeTable treeTable, final VmStatsTreeTableModel vmStatsTreeTableModel) { |
| JTableHeader header = treeTable.getTableHeader(); |
| header.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| TableColumnModel columnModel = treeTable.getColumnModel(); |
| int index = columnModel.getColumnIndexAtX(e.getX()); |
| vmStatsTreeTableModel.sortByColumn(StatsTableColumn.fromColumnIndex(index)); |
| } |
| }); |
| } |
| |
| /** A {@link TableCellRenderer} used to render certain columns of the VM Trace statistics tree table. */ |
| private static class ProfileTimeRenderer extends ColoredTableCellRenderer { |
| private static final char FIGURE_SPACE_CHAR = '\u2007'; // \u2007 = figure space, space the width of a digit |
| |
| private final NumberFormat myNumberFormat; |
| private final NumberFormat myPercentFormat; |
| |
| private boolean myFontCanDisplayFigureSpace; |
| private double mySpacesPerFigureSpaceChar = -1; |
| |
| public ProfileTimeRenderer() { |
| myPercentFormat = NumberFormat.getPercentInstance(Locale.getDefault()); |
| myPercentFormat.setMaximumFractionDigits(1); |
| myPercentFormat.setMinimumFractionDigits(1); |
| |
| myNumberFormat = NumberFormat.getNumberInstance(); |
| } |
| |
| @Override |
| protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { |
| setTextAlign(SwingConstants.RIGHT); |
| |
| if (value instanceof Long) { |
| // Format long values as a number |
| append(myNumberFormat.format(value)); |
| } else if (value instanceof Pair) { |
| /** Interpret a pair of <Long,Double> as the time value and a percentage as generated by |
| * {@link AbstractProfileDataNode#getValueAndPercentagePair}, and render them appropriately. */ |
| //noinspection unchecked |
| Pair<Long,Double> p = (Pair<Long,Double>) value; |
| append(myNumberFormat.format(p.getFirst())); |
| append(" "); |
| append(formatPercentage(p.getSecond()), SimpleTextAttributes.GRAY_ATTRIBUTES); |
| } |
| } |
| |
| private String formatPercentage(Double d) { |
| String format = myPercentFormat.format(d); |
| return format.length() < 6 ? prefixWithSpaces(format, 6 - format.length()) : format; // 6 = "100.0%".length |
| } |
| |
| private String prefixWithSpaces(String s, int nSpaces) { |
| if (mySpacesPerFigureSpaceChar < 0) { // not initialized yet |
| mySpacesPerFigureSpaceChar = 1; |
| |
| myFontCanDisplayFigureSpace = getFont().canDisplay(FIGURE_SPACE_CHAR); |
| if (!myFontCanDisplayFigureSpace) { |
| FontMetrics fm = getFontMetrics(getFont()); |
| mySpacesPerFigureSpaceChar = fm.charWidth(FIGURE_SPACE_CHAR) / fm.charWidth(' '); |
| } |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| if (myFontCanDisplayFigureSpace) { |
| StringUtil.repeatSymbol(sb, FIGURE_SPACE_CHAR, nSpaces); |
| } |
| else { |
| // When the current font cannot display the figure space character, we attempt to |
| // use an appropriate number of regular spaces |
| StringUtil.repeatSymbol(sb, ' ', (int)(nSpaces * mySpacesPerFigureSpaceChar) + 1); |
| } |
| |
| sb.append(s); |
| return sb.toString(); |
| } |
| } |
| |
| /** A renderer that center aligns column titles. */ |
| private static class TableHeaderCellRenderer implements TableCellRenderer { |
| private final TableCellRenderer myDefaultRenderer; |
| |
| public TableHeaderCellRenderer(TreeTable table) { |
| myDefaultRenderer = table.getTableHeader().getDefaultRenderer(); |
| if (myDefaultRenderer instanceof DefaultTableCellRenderer) { |
| ((DefaultTableCellRenderer)myDefaultRenderer).setHorizontalAlignment(SwingConstants.CENTER); |
| } |
| } |
| |
| @Override |
| public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| return myDefaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); |
| } |
| } |
| } |