TimeLineViewpublic class TimeLineView extends org.eclipse.swt.widgets.Composite implements Observer
Fields Summary |
---|
private HashMap | mRowByName | private double | mTotalElapsed | private RowData[] | mRows | private Segment[] | mSegments | private ArrayList | mSegmentList | private HashMap | mThreadLabels | private Timescale | mTimescale | private Surface | mSurface | private RowLabels | mLabels | private org.eclipse.swt.custom.SashForm | mSashForm | private int | mScrollOffsetY | public static final int | PixelsPerTick | private TickScaler | mScaleInfo | private static final int | LeftMargin | private static final int | RightMargin | private org.eclipse.swt.graphics.Color | mColorBlack | private org.eclipse.swt.graphics.Color | mColorGray | private org.eclipse.swt.graphics.Color | mColorDarkGray | private org.eclipse.swt.graphics.Color | mColorForeground | private org.eclipse.swt.graphics.Color | mColorRowBack | private org.eclipse.swt.graphics.Color | mColorZoomSelection | private org.eclipse.jface.resource.FontRegistry | mFontRegistry | private static final int | rowHeightvertical height of drawn blocks in each row | private static final int | rowYMarginthe blank space between rows | private static final int | rowYMarginHalf | private static final int | rowYSpacetotal vertical space for row | private static final int | majorTickLength | private static final int | minorTickLength | private static final int | timeLineOffsetY | private static final int | tickToFontSpacing | private static final int | topMarginstart of first row | private int | mMouseRow | private int | mNumRows | private int | mStartRow | private int | mEndRow | private TraceUnits | mUnits | private int | mSmallFontWidth | private int | mSmallFontHeight | private int | mMediumFontWidth | private SelectionController | mSelectionController | private MethodData | mHighlightMethodData | private Call | mHighlightCall | private static final int | MinInclusiveRange | private boolean | mSetFontsSetting the fonts looks good on Linux but bad on Macs |
Constructors Summary |
---|
public TimeLineView(org.eclipse.swt.widgets.Composite parent, TraceReader reader, SelectionController selectionController)
super(parent, SWT.NONE);
mRowByName = new HashMap<String, RowData>();
this.mSelectionController = selectionController;
selectionController.addObserver(this);
mUnits = reader.getTraceUnits();
mThreadLabels = reader.getThreadLabels();
Display display = getDisplay();
mColorGray = display.getSystemColor(SWT.COLOR_GRAY);
mColorDarkGray = display.getSystemColor(SWT.COLOR_DARK_GRAY);
mColorBlack = display.getSystemColor(SWT.COLOR_BLACK);
// mColorBackground = display.getSystemColor(SWT.COLOR_WHITE);
mColorForeground = display.getSystemColor(SWT.COLOR_BLACK);
mColorRowBack = new Color(display, 240, 240, 255);
mColorZoomSelection = new Color(display, 230, 230, 230);
mFontRegistry = new FontRegistry(display);
mFontRegistry.put("small", // $NON-NLS-1$
new FontData[] { new FontData("Arial", 8, SWT.NORMAL) }); // $NON-NLS-1$
mFontRegistry.put("courier8", // $NON-NLS-1$
new FontData[] { new FontData("Courier New", 8, SWT.BOLD) }); // $NON-NLS-1$
mFontRegistry.put("medium", // $NON-NLS-1$
new FontData[] { new FontData("Courier New", 10, SWT.NORMAL) }); // $NON-NLS-1$
Image image = new Image(display, new Rectangle(100, 100, 100, 100));
GC gc = new GC(image);
if (mSetFonts) {
gc.setFont(mFontRegistry.get("small")); // $NON-NLS-1$
}
mSmallFontWidth = gc.getFontMetrics().getAverageCharWidth();
mSmallFontHeight = gc.getFontMetrics().getHeight();
if (mSetFonts) {
gc.setFont(mFontRegistry.get("medium")); // $NON-NLS-1$
}
mMediumFontWidth = gc.getFontMetrics().getAverageCharWidth();
image.dispose();
gc.dispose();
setLayout(new FillLayout());
// Create a sash form for holding two canvas views, one for the
// thread labels and one for the thread timeline.
mSashForm = new SashForm(this, SWT.HORIZONTAL);
mSashForm.setBackground(mColorGray);
mSashForm.SASH_WIDTH = 3;
// Create a composite for the left side of the sash
Composite composite = new Composite(mSashForm, SWT.NONE);
GridLayout layout = new GridLayout(1, true /* make columns equal width */);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 1;
composite.setLayout(layout);
// Create a blank corner space in the upper left corner
BlankCorner corner = new BlankCorner(composite);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.heightHint = topMargin;
corner.setLayoutData(gridData);
// Add the thread labels below the blank corner.
mLabels = new RowLabels(composite);
gridData = new GridData(GridData.FILL_BOTH);
mLabels.setLayoutData(gridData);
// Create another composite for the right side of the sash
composite = new Composite(mSashForm, SWT.NONE);
layout = new GridLayout(1, true /* make columns equal width */);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 1;
composite.setLayout(layout);
mTimescale = new Timescale(composite);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.heightHint = topMargin;
mTimescale.setLayoutData(gridData);
mSurface = new Surface(composite);
gridData = new GridData(GridData.FILL_BOTH);
mSurface.setLayoutData(gridData);
mSashForm.setWeights(new int[] { 1, 5 });
final ScrollBar vBar = mSurface.getVerticalBar();
vBar.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
mScrollOffsetY = vBar.getSelection();
Point dim = mSurface.getSize();
int newScrollOffsetY = computeVisibleRows(dim.y);
if (newScrollOffsetY != mScrollOffsetY) {
mScrollOffsetY = newScrollOffsetY;
vBar.setSelection(newScrollOffsetY);
}
mLabels.redraw();
mSurface.redraw();
}
});
mSurface.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
Point dim = mSurface.getSize();
// If we don't need the scroll bar then don't display it.
if (dim.y >= mNumRows * rowYSpace) {
vBar.setVisible(false);
} else {
vBar.setVisible(true);
}
int newScrollOffsetY = computeVisibleRows(dim.y);
if (newScrollOffsetY != mScrollOffsetY) {
mScrollOffsetY = newScrollOffsetY;
vBar.setSelection(newScrollOffsetY);
}
int spaceNeeded = mNumRows * rowYSpace;
vBar.setMaximum(spaceNeeded);
vBar.setThumb(dim.y);
mLabels.redraw();
mSurface.redraw();
}
});
mSurface.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent me) {
mSurface.mouseUp(me);
}
@Override
public void mouseDown(MouseEvent me) {
mSurface.mouseDown(me);
}
@Override
public void mouseDoubleClick(MouseEvent me) {
mSurface.mouseDoubleClick(me);
}
});
mSurface.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent me) {
mSurface.mouseMove(me);
}
});
mTimescale.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent me) {
mTimescale.mouseUp(me);
}
@Override
public void mouseDown(MouseEvent me) {
mTimescale.mouseDown(me);
}
@Override
public void mouseDoubleClick(MouseEvent me) {
mTimescale.mouseDoubleClick(me);
}
});
mTimescale.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent me) {
mTimescale.mouseMove(me);
}
});
mLabels.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent me) {
mLabels.mouseMove(me);
}
});
setData(reader.getThreadTimeRecords());
|
Methods Summary |
---|
private int | computeVisibleRows(int ydim)
// If we resize, then move the bottom row down. Don't allow the scroll
// to waste space at the bottom.
int offsetY = mScrollOffsetY;
int spaceNeeded = mNumRows * rowYSpace;
if (offsetY + ydim > spaceNeeded) {
offsetY = spaceNeeded - ydim;
if (offsetY < 0) {
offsetY = 0;
}
}
mStartRow = offsetY / rowYSpace;
mEndRow = (offsetY + ydim) / rowYSpace;
if (mEndRow >= mNumRows) {
mEndRow = mNumRows - 1;
}
return offsetY;
| private void | popFrames(com.android.traceview.TimeLineView$RowData rd, com.android.traceview.TimeLineView$Block top, long startTime)
long topEndTime = top.getEndTime();
long lastEndTime = top.getStartTime();
while (topEndTime <= startTime) {
if (topEndTime > lastEndTime) {
Segment segment = new Segment(rd, top, lastEndTime, topEndTime);
mSegmentList.add(segment);
lastEndTime = topEndTime;
}
rd.pop();
top = rd.top();
if (top == null)
return;
topEndTime = top.getEndTime();
}
// If we get here, then topEndTime > startTime
if (lastEndTime < startTime) {
Segment bd = new Segment(rd, top, lastEndTime, startTime);
mSegmentList.add(bd);
}
| public void | setData(java.util.ArrayList records)
if (records == null)
records = new ArrayList<Record>();
if (false) {
System.out.println("TimelineView() list of records:"); // $NON-NLS-1$
for (Record r : records) {
System.out.printf("row '%s' block '%s' [%d, %d]\n", r.row // $NON-NLS-1$
.getName(), r.block.getName(), r.block.getStartTime(),
r.block.getEndTime());
if (r.block.getStartTime() > r.block.getEndTime()) {
System.err.printf("Error: block startTime > endTime\n"); // $NON-NLS-1$
System.exit(1);
}
}
}
// Sort the records into increasing start time, and decreasing end time
Collections.sort(records, new Comparator<Record>() {
public int compare(Record r1, Record r2) {
long start1 = r1.block.getStartTime();
long start2 = r2.block.getStartTime();
if (start1 > start2)
return 1;
if (start1 < start2)
return -1;
// The start times are the same, so compare the end times
long end1 = r1.block.getEndTime();
long end2 = r2.block.getEndTime();
if (end1 > end2)
return -1;
if (end1 < end2)
return 1;
return 0;
}
});
// The records are sorted into increasing start time,
// so the minimum start time is the start time of the first record.
double minVal = 0;
if (records.size() > 0)
minVal = records.get(0).block.getStartTime();
// Sum the time spent in each row and block, and
// keep track of the maximum end time.
double maxVal = 0;
for (Record rec : records) {
Row row = rec.row;
Block block = rec.block;
String rowName = row.getName();
RowData rd = mRowByName.get(rowName);
if (rd == null) {
rd = new RowData(row);
mRowByName.put(rowName, rd);
}
long blockStartTime = block.getStartTime();
long blockEndTime = block.getEndTime();
if (blockEndTime > rd.mEndTime) {
long start = Math.max(blockStartTime, rd.mEndTime);
rd.mElapsed += blockEndTime - start;
mTotalElapsed += blockEndTime - start;
rd.mEndTime = blockEndTime;
}
if (blockEndTime > maxVal)
maxVal = blockEndTime;
// Keep track of nested blocks by using a stack (for each row).
// Create a Segment object for each visible part of a block.
Block top = rd.top();
if (top == null) {
rd.push(block);
continue;
}
long topStartTime = top.getStartTime();
long topEndTime = top.getEndTime();
if (topEndTime >= blockStartTime) {
// Add this segment if it has a non-zero elapsed time.
if (topStartTime < blockStartTime) {
Segment segment = new Segment(rd, top, topStartTime,
blockStartTime);
mSegmentList.add(segment);
}
// If this block starts where the previous (top) block ends,
// then pop off the top block.
if (topEndTime == blockStartTime)
rd.pop();
rd.push(block);
} else {
// We may have to pop several frames here.
popFrames(rd, top, blockStartTime);
rd.push(block);
}
}
// Clean up the stack of each row
for (RowData rd : mRowByName.values()) {
Block top = rd.top();
popFrames(rd, top, Integer.MAX_VALUE);
}
mSurface.setRange(minVal, maxVal);
mSurface.setLimitRange(minVal, maxVal);
// Sort the rows into decreasing elapsed time
Collection<RowData> rv = mRowByName.values();
mRows = rv.toArray(new RowData[rv.size()]);
Arrays.sort(mRows, new Comparator<RowData>() {
public int compare(RowData rd1, RowData rd2) {
return (int) (rd2.mElapsed - rd1.mElapsed);
}
});
// Assign ranks to the sorted rows
for (int ii = 0; ii < mRows.length; ++ii) {
mRows[ii].mRank = ii;
}
// Compute the number of rows with data
mNumRows = 0;
for (int ii = 0; ii < mRows.length; ++ii) {
if (mRows[ii].mElapsed == 0)
break;
mNumRows += 1;
}
// Sort the blocks into increasing rows, and within rows into
// increasing start values.
mSegments = mSegmentList.toArray(new Segment[mSegmentList.size()]);
Arrays.sort(mSegments, new Comparator<Segment>() {
public int compare(Segment bd1, Segment bd2) {
RowData rd1 = bd1.mRowData;
RowData rd2 = bd2.mRowData;
int diff = rd1.mRank - rd2.mRank;
if (diff == 0) {
long timeDiff = bd1.mStartTime - bd2.mStartTime;
if (timeDiff == 0)
timeDiff = bd1.mEndTime - bd2.mEndTime;
return (int) timeDiff;
}
return diff;
}
});
if (false) {
for (Segment segment : mSegments) {
System.out.printf("seg '%s' [%6d, %6d] %s\n",
segment.mRowData.mName, segment.mStartTime,
segment.mEndTime, segment.mBlock.getName());
if (segment.mStartTime > segment.mEndTime) {
System.err.printf("Error: segment startTime > endTime\n");
System.exit(1);
}
}
}
| private void | startHighlighting()
// System.out.printf("startHighlighting()\n");
mSurface.mHighlightStep = 0;
mSurface.mFadeColors = true;
// Force a recomputation of the color strips
mSurface.mCachedEndRow = -1;
getDisplay().timerExec(0, mSurface.mHighlightAnimator);
| public void | update(java.util.Observable objservable, java.lang.Object arg)
// Ignore updates from myself
if (arg == "TimeLineView") // $NON-NLS-1$
return;
// System.out.printf("timeline update from %s\n", arg);
boolean foundHighlight = false;
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();
// System.out.printf(" timeline highlight %s from %s\n", name, arg);
if (name == "MethodData") { // $NON-NLS-1$
foundHighlight = true;
mHighlightMethodData = (MethodData) selection.getValue();
// System.out.printf(" method %s\n",
// highlightMethodData.getName());
mHighlightCall = null;
startHighlighting();
} else if (name == "Call") { // $NON-NLS-1$
foundHighlight = true;
mHighlightCall = (Call) selection.getValue();
// System.out.printf(" call %s\n", highlightCall.getName());
mHighlightMethodData = null;
startHighlighting();
}
}
if (foundHighlight == false)
mSurface.clearHighlights();
|
|