| /* |
| * Copyright (C) 2006 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.traceview; |
| |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITreeViewerListener; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeExpansionEvent; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeColumn; |
| import org.eclipse.swt.widgets.TreeItem; |
| |
| import java.util.ArrayList; |
| import java.util.Observable; |
| import java.util.Observer; |
| |
| public class ProfileView extends Composite implements Observer { |
| |
| private TreeViewer mTreeViewer; |
| private Text mSearchBox; |
| private SelectionController mSelectionController; |
| private ProfileProvider mProfileProvider; |
| private Color mColorNoMatch; |
| private Color mColorMatch; |
| private MethodData mCurrentHighlightedMethod; |
| private MethodHandler mMethodHandler; |
| |
| public interface MethodHandler { |
| void handleMethod(MethodData method); |
| } |
| |
| public ProfileView(Composite parent, TraceReader reader, |
| SelectionController selectController) { |
| super(parent, SWT.NONE); |
| setLayout(new GridLayout(1, false)); |
| this.mSelectionController = selectController; |
| mSelectionController.addObserver(this); |
| |
| // Add a tree viewer at the top |
| mTreeViewer = new TreeViewer(this, SWT.MULTI | SWT.NONE); |
| mTreeViewer.setUseHashlookup(true); |
| mProfileProvider = reader.getProfileProvider(); |
| mProfileProvider.setTreeViewer(mTreeViewer); |
| SelectionAdapter listener = mProfileProvider.getColumnListener(); |
| final Tree tree = mTreeViewer.getTree(); |
| tree.setHeaderVisible(true); |
| tree.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| // Get the column names from the ProfileProvider |
| String[] columnNames = mProfileProvider.getColumnNames(); |
| int[] columnWidths = mProfileProvider.getColumnWidths(); |
| int[] columnAlignments = mProfileProvider.getColumnAlignments(); |
| for (int ii = 0; ii < columnWidths.length; ++ii) { |
| TreeColumn column = new TreeColumn(tree, SWT.LEFT); |
| column.setText(columnNames[ii]); |
| column.setWidth(columnWidths[ii]); |
| column.setMoveable(true); |
| column.addSelectionListener(listener); |
| column.setAlignment(columnAlignments[ii]); |
| } |
| |
| // Add a listener to the tree so that we can make the row |
| // height smaller. |
| tree.addListener(SWT.MeasureItem, new Listener() { |
| @Override |
| public void handleEvent(Event event) { |
| int fontHeight = event.gc.getFontMetrics().getHeight(); |
| event.height = fontHeight; |
| } |
| }); |
| |
| mTreeViewer.setContentProvider(mProfileProvider); |
| mTreeViewer.setLabelProvider(mProfileProvider.getLabelProvider()); |
| mTreeViewer.setInput(mProfileProvider.getRoot()); |
| |
| // Create another composite to hold the label and text box |
| Composite composite = new Composite(this, SWT.NONE); |
| composite.setLayout(new GridLayout(2, false)); |
| composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| |
| // Add a label for the search box |
| Label label = new Label(composite, SWT.NONE); |
| label.setText("Find:"); |
| |
| // Add a text box for searching for method names |
| mSearchBox = new Text(composite, SWT.BORDER); |
| mSearchBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| |
| Display display = getDisplay(); |
| mColorNoMatch = new Color(display, 255, 200, 200); |
| mColorMatch = mSearchBox.getBackground(); |
| |
| mSearchBox.addModifyListener(new ModifyListener() { |
| @Override |
| public void modifyText(ModifyEvent ev) { |
| String query = mSearchBox.getText(); |
| if (query.length() == 0) |
| return; |
| findName(query); |
| } |
| }); |
| |
| // Add a key listener to the text box so that we can clear |
| // the text box if the user presses <ESC>. |
| mSearchBox.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent event) { |
| if (event.keyCode == SWT.ESC) { |
| mSearchBox.setText(""); |
| } else if (event.keyCode == SWT.CR) { |
| String query = mSearchBox.getText(); |
| if (query.length() == 0) |
| return; |
| findNextName(query); |
| } |
| } |
| }); |
| |
| // Also add a key listener to the tree viewer so that the |
| // user can just start typing anywhere in the tree view. |
| tree.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent event) { |
| if (event.keyCode == SWT.ESC) { |
| mSearchBox.setText(""); |
| } else if (event.keyCode == SWT.BS) { |
| // Erase the last character from the search box |
| String text = mSearchBox.getText(); |
| int len = text.length(); |
| String chopped; |
| if (len <= 1) |
| chopped = ""; |
| else |
| chopped = text.substring(0, len - 1); |
| mSearchBox.setText(chopped); |
| } else if (event.keyCode == SWT.CR) { |
| String query = mSearchBox.getText(); |
| if (query.length() == 0) |
| return; |
| findNextName(query); |
| } else { |
| // Append the given character to the search box |
| String str = String.valueOf(event.character); |
| mSearchBox.append(str); |
| } |
| event.doit = false; |
| } |
| }); |
| |
| // Add a selection listener to the tree so that the user can click |
| // on a method that is a child or parent and jump to that method. |
| mTreeViewer |
| .addSelectionChangedListener(new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent ev) { |
| ISelection sel = ev.getSelection(); |
| if (sel.isEmpty()) |
| return; |
| if (sel instanceof IStructuredSelection) { |
| IStructuredSelection selection = (IStructuredSelection) sel; |
| Object element = selection.getFirstElement(); |
| if (element == null) |
| return; |
| if (element instanceof MethodData) { |
| MethodData md = (MethodData) element; |
| highlightMethod(md, true); |
| } |
| if (element instanceof ProfileData) { |
| MethodData md = ((ProfileData) element) |
| .getMethodData(); |
| highlightMethod(md, true); |
| } |
| } |
| } |
| }); |
| |
| // Add a tree listener so that we can expand the parents and children |
| // of a method when a method is expanded. |
| mTreeViewer.addTreeListener(new ITreeViewerListener() { |
| @Override |
| public void treeExpanded(TreeExpansionEvent event) { |
| Object element = event.getElement(); |
| if (element instanceof MethodData) { |
| MethodData md = (MethodData) element; |
| expandNode(md); |
| } |
| } |
| @Override |
| public void treeCollapsed(TreeExpansionEvent event) { |
| } |
| }); |
| |
| tree.addListener(SWT.MouseDown, new Listener() { |
| @Override |
| public void handleEvent(Event event) { |
| Point point = new Point(event.x, event.y); |
| TreeItem treeItem = tree.getItem(point); |
| MethodData md = mProfileProvider.findMatchingTreeItem(treeItem); |
| if (md == null) |
| return; |
| ArrayList<Selection> selections = new ArrayList<Selection>(); |
| selections.add(Selection.highlight("MethodData", md)); |
| mSelectionController.change(selections, "ProfileView"); |
| |
| if (mMethodHandler != null && (event.stateMask & SWT.MOD1) != 0) { |
| mMethodHandler.handleMethod(md); |
| } |
| } |
| }); |
| } |
| |
| public void setMethodHandler(MethodHandler handler) { |
| mMethodHandler = handler; |
| } |
| |
| private void findName(String query) { |
| MethodData md = mProfileProvider.findMatchingName(query); |
| selectMethod(md); |
| } |
| |
| private void findNextName(String query) { |
| MethodData md = mProfileProvider.findNextMatchingName(query); |
| selectMethod(md); |
| } |
| |
| private void selectMethod(MethodData md) { |
| if (md == null) { |
| mSearchBox.setBackground(mColorNoMatch); |
| return; |
| } |
| mSearchBox.setBackground(mColorMatch); |
| highlightMethod(md, false); |
| } |
| |
| @Override |
| public void update(Observable objservable, Object arg) { |
| // Ignore updates from myself |
| if (arg == "ProfileView") |
| return; |
| // System.out.printf("profileview update from %s\n", arg); |
| ArrayList<Selection> selections; |
| selections = mSelectionController.getSelections(); |
| for (Selection selection : selections) { |
| Selection.Action action = selection.getAction(); |
| if (action != Selection.Action.Highlight) |
| continue; |
| String name = selection.getName(); |
| if (name == "MethodData") { |
| MethodData md = (MethodData) selection.getValue(); |
| highlightMethod(md, true); |
| return; |
| } |
| if (name == "Call") { |
| Call call = (Call) selection.getValue(); |
| MethodData md = call.getMethodData(); |
| highlightMethod(md, true); |
| return; |
| } |
| } |
| } |
| |
| private void highlightMethod(MethodData md, boolean clearSearch) { |
| if (md == null) |
| return; |
| // Avoid an infinite recursion |
| if (md == mCurrentHighlightedMethod) |
| return; |
| if (clearSearch) { |
| mSearchBox.setText(""); |
| mSearchBox.setBackground(mColorMatch); |
| } |
| mCurrentHighlightedMethod = md; |
| mTreeViewer.collapseAll(); |
| // Expand this node and its children |
| expandNode(md); |
| StructuredSelection sel = new StructuredSelection(md); |
| mTreeViewer.setSelection(sel, true); |
| Tree tree = mTreeViewer.getTree(); |
| TreeItem[] items = tree.getSelection(); |
| if (items.length != 0) { |
| tree.setTopItem(items[0]); |
| // workaround a Mac bug by adding showItem(). |
| tree.showItem(items[0]); |
| } |
| } |
| |
| private void expandNode(MethodData md) { |
| ProfileNode[] nodes = md.getProfileNodes(); |
| mTreeViewer.setExpandedState(md, true); |
| // Also expand the "Parents" and "Children" nodes. |
| if (nodes != null) { |
| for (ProfileNode node : nodes) { |
| if (node.isRecursive() == false) |
| mTreeViewer.setExpandedState(node, true); |
| } |
| } |
| } |
| } |