| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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.android.tools.idea.editors.vmtrace; |
| |
| import com.android.tools.idea.editors.vmtrace.treemodel.VmStatsTreeTableModel; |
| import com.android.tools.idea.editors.vmtrace.treemodel.VmStatsTreeUtils; |
| import com.android.tools.perflib.vmtrace.ClockType; |
| import com.android.tools.perflib.vmtrace.SearchResult; |
| import com.android.tools.perflib.vmtrace.ThreadInfo; |
| import com.android.tools.perflib.vmtrace.VmTraceData; |
| import com.android.tools.perflib.vmtrace.viz.TraceViewCanvas; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Iterables; |
| import com.intellij.find.editorHeaderActions.Utils; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.DataKey; |
| import com.intellij.openapi.actionSystem.DataProvider; |
| import com.intellij.openapi.editor.impl.EditorHeaderComponent; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.wm.IdeFocusManager; |
| import com.intellij.ui.ColoredListCellRenderer; |
| import com.intellij.ui.DocumentAdapter; |
| import com.intellij.ui.JBSplitter; |
| import com.intellij.ui.SearchTextField; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.ui.components.panels.NonOpaquePanel; |
| import com.intellij.ui.treeStructure.treetable.TreeTable; |
| import icons.AndroidIcons; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.table.TableModel; |
| import javax.swing.table.TableRowSorter; |
| import javax.swing.text.BadLocationException; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.util.List; |
| |
| public class TraceViewPanel { |
| @NonNls public static DataKey<TraceViewPanel> KEY = DataKey.create("android.traceview.panel"); |
| |
| // The names for the cards used in the card layout. |
| // Note that these are duplicated in the layout form. |
| @NonNls private static final String CARD_FIND = "FIND"; |
| @NonNls private static final String CARD_DEFAULT = "DEFAULT"; |
| |
| /** Default name for main thread in Android apps. */ |
| @NonNls private static final String MAIN_THREAD_NAME = "main"; |
| |
| private final Project myProject; |
| |
| private JPanel myContainer; |
| |
| private JPanel myHeaderPanel; |
| private TraceViewCanvas myTraceViewCanvas; |
| private TreeTable myTreeTable; |
| |
| @SuppressWarnings("UnusedDeclaration") // custom creation only |
| private JPanel myDefaultHeaderPanel; |
| private JComboBox myThreadCombo; |
| private JComboBox myRenderClockSelectorCombo; |
| |
| @SuppressWarnings("UnusedDeclaration") // custom creation only |
| private JPanel myFindPanel; |
| private JPanel myFindFieldWrapper; |
| private SearchTextField mySearchField; |
| private JLabel myCloseLabel; |
| private JBLabel myResultsLabel; |
| private JLabel mySearchLabel; |
| private JBSplitter mySplitter; |
| private JLabel myZoomFitLabel; |
| private JCheckBox myUseInclusiveTimeForColoring; |
| |
| private static final String[] ourRenderClockOptions = new String[] { |
| "Wall Clock Time", |
| "Thread Time", |
| }; |
| |
| private static final ClockType[] ourRenderClockTypes = new ClockType[] { |
| ClockType.GLOBAL, |
| ClockType.THREAD, |
| }; |
| private VmTraceData myTraceData; |
| private VmStatsTreeTableModel myVmStatsTreeTableModel; |
| |
| public TraceViewPanel(Project project) { |
| myProject = project; |
| |
| myRenderClockSelectorCombo.setModel(new DefaultComboBoxModel(ourRenderClockOptions)); |
| myRenderClockSelectorCombo.setSelectedIndex(0); |
| |
| ActionListener l = new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (e.getSource() == myThreadCombo) { |
| final ThreadInfo selectedThread = (ThreadInfo)myThreadCombo.getSelectedItem(); |
| myTraceViewCanvas.displayThread(selectedThread); |
| myVmStatsTreeTableModel.setThread(selectedThread); |
| } else if (e.getSource() == myRenderClockSelectorCombo) { |
| myTraceViewCanvas.setRenderClock(getCurrentRenderClock()); |
| myVmStatsTreeTableModel.setClockType(getCurrentRenderClock()); |
| } else if (e.getSource() == myUseInclusiveTimeForColoring) { |
| myTraceViewCanvas.setUseInclusiveTimeForColorAssignment(myUseInclusiveTimeForColoring.isSelected()); |
| } |
| } |
| }; |
| |
| myThreadCombo.addActionListener(l); |
| myRenderClockSelectorCombo.addActionListener(l); |
| myUseInclusiveTimeForColoring.addActionListener(l); |
| myUseInclusiveTimeForColoring.setOpaque(false); |
| } |
| |
| private SearchTextField createSearchField() { |
| SearchTextField stf = new SearchTextField(true); |
| stf.setOpaque(false); |
| stf.setEnabled(true); |
| Utils.setSmallerFont(stf); |
| |
| stf.addDocumentListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| searchTextChanged(getText(e)); |
| } |
| |
| private String getText(DocumentEvent e) { |
| try { |
| return e.getDocument().getText(0, e.getDocument().getLength()); |
| } |
| catch (BadLocationException e1) { |
| return ""; |
| } |
| } |
| }); |
| |
| JTextField editorTextField = stf.getTextEditor(); |
| editorTextField.setMinimumSize(new Dimension(200, -1)); |
| |
| editorTextField.registerKeyboardAction(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| closeSearchComponent(); |
| } |
| }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); |
| |
| return stf; |
| } |
| |
| private void searchTextChanged(@Nullable String pattern) { |
| if (StringUtil.isEmpty(pattern)) { |
| myTraceViewCanvas.setHighlightMethods(null); |
| myResultsLabel.setText(""); |
| return; |
| } |
| |
| ThreadInfo thread = (ThreadInfo)myThreadCombo.getSelectedItem(); |
| SearchResult results = myTraceData.searchFor(pattern, thread); |
| myTraceViewCanvas.setHighlightMethods(results.getMethods()); |
| |
| String result = String.format("%1$d %2$s, %3$d %4$s", |
| results.getMethods().size(), |
| StringUtil.pluralize("method", results.getMethods().size()), |
| results.getInstances().size(), |
| StringUtil.pluralize("instance", results.getInstances().size())); |
| myResultsLabel.setText(result); |
| } |
| |
| public void setTrace(@NotNull VmTraceData trace) { |
| myTraceData = trace; |
| |
| List<ThreadInfo> threads = trace.getThreads(true); |
| if (threads.isEmpty()) { |
| return; |
| } |
| |
| ThreadInfo defaultThread = Iterables.find(threads, new Predicate<ThreadInfo>() { |
| @Override |
| public boolean apply(ThreadInfo input) { |
| return MAIN_THREAD_NAME.equals(input.getName()); |
| } |
| }, threads.get(0)); |
| |
| myTraceViewCanvas.setTrace(trace, defaultThread, getCurrentRenderClock()); |
| myThreadCombo.setModel(new DefaultComboBoxModel(threads.toArray())); |
| myThreadCombo.setSelectedItem(defaultThread); |
| myThreadCombo.setRenderer(new ColoredListCellRenderer() { |
| @Override |
| protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { |
| String name = value instanceof ThreadInfo ? ((ThreadInfo)value).getName() : value.toString(); |
| append(name); |
| } |
| }); |
| |
| myThreadCombo.setEnabled(true); |
| myRenderClockSelectorCombo.setEnabled(true); |
| |
| myVmStatsTreeTableModel.setTraceData(trace, defaultThread); |
| myVmStatsTreeTableModel.setClockType(getCurrentRenderClock()); |
| myTreeTable.setModel(myVmStatsTreeTableModel); |
| |
| VmStatsTreeUtils.adjustTableColumnWidths(myTreeTable); |
| VmStatsTreeUtils.setCellRenderers(myTreeTable); |
| VmStatsTreeUtils.setSpeedSearch(myTreeTable); |
| VmStatsTreeUtils.enableSorting(myTreeTable, myVmStatsTreeTableModel); |
| } |
| |
| private ClockType getCurrentRenderClock() { |
| return ourRenderClockTypes[myRenderClockSelectorCombo.getSelectedIndex()]; |
| } |
| |
| @NotNull |
| public JComponent getComponent() { |
| return myContainer; |
| } |
| |
| private void createUIComponents() { |
| MouseAdapter l = new MouseAdapter() { |
| @Override |
| public void mousePressed(MouseEvent e) { |
| if (e.getSource() == myCloseLabel) { |
| closeSearchComponent(); |
| } else if (e.getSource() == mySearchLabel) { |
| showSearchComponent(); |
| } else if (e.getSource() == myZoomFitLabel) { |
| myTraceViewCanvas.zoomFit(); |
| } |
| } |
| }; |
| |
| myDefaultHeaderPanel = new EditorHeaderComponent(); |
| mySearchLabel = new JLabel(AllIcons.Actions.Search); |
| mySearchLabel.addMouseListener(l); |
| mySearchLabel.setToolTipText("Find (Ctrl + F)"); |
| myZoomFitLabel = new JLabel(AndroidIcons.ZoomFit); |
| myZoomFitLabel.setToolTipText("Zoom Fit"); |
| myZoomFitLabel.addMouseListener(l); |
| |
| myFindPanel = new EditorHeaderComponent(); |
| myFindFieldWrapper = new NonOpaquePanel(new BorderLayout()); |
| mySearchField = createSearchField(); |
| myFindFieldWrapper.add(mySearchField); |
| |
| myCloseLabel = new JLabel(AllIcons.Actions.Cross); |
| myCloseLabel.addMouseListener(l); |
| |
| myVmStatsTreeTableModel = new VmStatsTreeTableModel(); |
| myTreeTable = new TreeTable(myVmStatsTreeTableModel); |
| myTraceViewCanvas = new TraceViewCanvasWrapper(); |
| JBScrollPane scrollPane = new JBScrollPane(myTreeTable); |
| |
| mySplitter = new JBSplitter(true, 0.75f); |
| mySplitter.setShowDividerControls(true); |
| mySplitter.setShowDividerIcon(true); |
| mySplitter.setFirstComponent(myTraceViewCanvas); |
| mySplitter.setSecondComponent(scrollPane); |
| } |
| |
| public void showSearchComponent() { |
| CardLayout layout = (CardLayout)myHeaderPanel.getLayout(); |
| layout.show(myHeaderPanel, CARD_FIND); |
| IdeFocusManager.getInstance(myProject).requestFocus(mySearchField, true); |
| } |
| |
| private void closeSearchComponent() { |
| CardLayout layout = (CardLayout)myHeaderPanel.getLayout(); |
| layout.show(myHeaderPanel, CARD_DEFAULT); |
| IdeFocusManager.getInstance(myProject).requestFocus(myTraceViewCanvas, true); |
| } |
| |
| /** |
| * {@link TraceViewCanvasWrapper} is a wrapper around {@link TraceViewCanvas} that also implements the {@link DataProvider} interface. |
| * This allows {@link VmTraceEditorSearchAction} to identify the editor from the current context of an event. |
| */ |
| private class TraceViewCanvasWrapper extends TraceViewCanvas implements DataProvider { |
| public TraceViewCanvasWrapper() { |
| addMouseListener(new MouseAdapter() { |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| IdeFocusManager.getInstance(myProject).requestFocus(TraceViewCanvasWrapper.this, true); |
| } |
| }); |
| } |
| |
| @Nullable |
| @Override |
| public Object getData(@NonNls String dataId) { |
| return KEY.is(dataId) ? TraceViewPanel.this : null; |
| } |
| } |
| } |