/*******************************************************************************
- * Copyright (c) 2011, 2013 Ericsson
+ * Copyright (c) 2011, 2014 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
*
* Contributors:
* Patrick Tasse - Initial API and implementation
+ * Bernd Hufmann - Add support for event collapsing
******************************************************************************/
package org.eclipse.linuxtools.tmf.ui.viewers.events;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.linuxtools.internal.tmf.core.filter.TmfCollapseFilter;
import org.eclipse.linuxtools.internal.tmf.ui.Activator;
import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEventType;
import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
* This can help avoid re-reading the trace when the user scrolls a window,
* for example.
*
- * @version 1.0
* @author Patrick Tasse
*/
public class TmfEventsCache {
/**
- * The generic TMF Events table cached event
+ * The generic TMF Events table cached event.
*
- * @version 1.0
* @author Patrick Tasse
*/
- public static class CachedEvent {
+ public static class CachedEvent implements ITmfEvent {
+ /**
+ * Event reference.
+ *
+ * When {@link TmfCollapseFilter} is active then it's event reference
+ * of the first event of repeated events.
+ */
ITmfEvent event;
+ /**
+ * Events rank.
+ *
+ * When {@link TmfCollapseFilter} is active then it's event rank of the
+ * first event of repeated events.
+ */
long rank;
+ /**
+ * Number times event is repeated. Updated by using {@link TmfCollapseFilter}
+ */
+ long repeatCount;
/**
* Constructor for new cached events.
this.event = iTmfEvent;
this.rank = rank;
}
+ /**
+ * @since 3.1
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ return event.getAdapter(adapter);
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public ITmfTrace getTrace() {
+ return event.getTrace();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public long getRank() {
+ return event.getRank();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public ITmfTimestamp getTimestamp() {
+ return event.getTimestamp();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public String getSource() {
+ return event.getSource();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public ITmfEventType getType() {
+ return event.getType();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public ITmfEventField getContent() {
+ return event.getContent();
+ }
+ /**
+ * @since 3.1
+ */
+ @Override
+ public String getReference() {
+ return event.getReference();
+ }
}
private final CachedEvent[] fCache;
}
}
+ /**
+ * Update event repeat count at index
+ *
+ * @param index
+ * The index this event occupies in the cache
+ *
+ * @since 3.1
+ */
+ public synchronized void updateCollapsedEvent(int index) {
+ int i = index - fCacheStartIndex;
+ if (i < fCache.length) {
+ fCache[i].repeatCount++;
+ }
+ }
+
/**
* Get the cache index of an event from his rank in the trace. This will
* take in consideration any filter that might be applied.
if (fFilter != null) {
fTable.cacheUpdated(false);
}
+ } else if (((fFilter != null) && !fFilter.matches(event)) && (skipCount <= 0)) { // TODO fix duplicated call to matches()
+ if ((count > 0) && (fFilter instanceof TmfCollapseFilter)) {
+ fCache[count - 1].repeatCount++;
+ }
}
if (count >= fCache.length) {
cancel();
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
+import org.eclipse.linuxtools.internal.tmf.core.filter.TmfCollapseFilter;
import org.eclipse.linuxtools.internal.tmf.ui.Activator;
import org.eclipse.linuxtools.internal.tmf.ui.Messages;
import org.eclipse.linuxtools.internal.tmf.ui.commands.ExportToTextCommandHandler;
import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.collapse.ITmfCollapsibleEvent;
import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfCallsite;
import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfModelLookup;
import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfSourceLookup;
import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
*/
protected static final @NonNull String EMPTY_STRING = ""; //$NON-NLS-1$
+ private static final boolean IS_LINUX = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
+
private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
"icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
private static final int MAX_CACHE_SIZE = 1000;
+ private static final int MARGIN_COLUMN_INDEX = 0;
+ private static final int FILTER_SUMMARY_INDEX = 1;
+ private static final int EVENT_COLUMNS_START_INDEX = MARGIN_COLUMN_INDEX + 1;
+
/**
* Default set of columns to use for trace types that do not specify
* anything
private SashForm fSashForm;
private TmfRawEventViewer fRawViewer;
private ITmfTrace fTrace;
- private boolean fPackDone = false;
+ volatile private boolean fPackDone = false;
private HeaderState fHeaderState = HeaderState.SEARCH;
private long fSelectedRank = 0;
private ITmfTimestamp fSelectedBeginTimestamp = null;
fColumns.addAll(columns);
}
+ TmfMarginColumn collapseCol = new TmfMarginColumn();
+ fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
+
// Create the UI columns in the table
for (TmfEventTableColumn col : fColumns) {
TableColumn column = fTable.newTableColumn(SWT.LEFT);
column.setToolTipText(col.getHeaderTooltip());
column.setData(Key.FIELD_ID, col.getFilterFieldId());
column.pack();
+ if (col instanceof TmfMarginColumn) {
+ column.setResizable(false);
+ }
}
// Set the frozen row for header row
final CachedEvent cachedEvent = fCache.getEvent(index);
if (cachedEvent != null) {
- setItemData(item, cachedEvent.event, cachedEvent.rank);
+ setItemData(item, cachedEvent, cachedEvent.rank);
return;
}
layout.marginWidth = 2;
tooltipShell.setLayout(layout);
final Label label = new Label(tooltipShell, SWT.WRAP);
- String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$
+ String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
label.setText(text);
* the method toDisplay() expects coordinates relative to an origin that includes the table header.
*/
int y = event.y;
- if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
+ if (IS_LINUX) {
y += fTable.getHeaderHeight();
}
Point pt = fTable.toDisplay(event.x, y);
IMarker marker = null;
try {
String fileName = cs.getFileName();
- final String trimmedPath = fileName.replaceAll("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
final ArrayList<IFile> files = new ArrayList<>();
ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
@Override
try {
HashMap<String, Object> parameters = new HashMap<>();
Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
- ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command,parameters);
+ ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command, parameters);
+
IEvaluationContext context = handlerService.getCurrentState();
- context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, fColumns);
+ // Omit the margin column
+ List<TmfEventTableColumn> exportColumns = fColumns.subList(EVENT_COLUMNS_START_INDEX, fColumns.size());
+ context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, exportColumns);
+
handlerService.executeCommandInContext(cmd, null, context);
} catch (ExecutionException e) {
displayException(e);
}
};
+ final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
+ @Override
+ public void run() {
+ applyFilter(new TmfCollapseFilter());
+ }
+ };
+
class ToggleBookmarkAction extends Action {
Long fRank;
}
}
+ // only show collapse filter if at least one trace can be collapsed
+ boolean isCollapsible = false;
+ if (fTrace != null) {
+ ITmfTrace traces[] = TmfTraceManager.getTraceSet(fTrace);
+ for (ITmfTrace trace : traces) {
+ Class <? extends ITmfEvent> eventClass = trace.getEventType();
+ isCollapsible = ITmfCollapsibleEvent.class.isAssignableFrom(eventClass);
+ if (isCollapsible) {
+ break;
+ }
+ }
+ }
+
+ if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
+ tablePopupMenu.add(collapseAction);
+ tablePopupMenu.add(new Separator());
+ }
+
tablePopupMenu.add(clearFiltersAction);
final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
if (savedFilters.length > 0) {
* Which rank this event has in the trace/experiment
*/
protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
- item.setText(getItemStrings(fColumns, event));
+ String[] itemStrings = getItemStrings(fColumns, event);
+ item.setText(itemStrings);
item.setData(event);
item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
item.setData(Key.RANK, rank);
} else {
item.setImage((Image) null);
}
+
+ if ((itemStrings[MARGIN_COLUMN_INDEX] != null) && !itemStrings[MARGIN_COLUMN_INDEX].isEmpty()) {
+ packMarginColumn();
+ }
}
/**
txtKey = Key.FILTER_TXT;
}
item.setForeground(fGrayColor);
- for (int i = 0; i < fTable.getColumns().length; i++) {
+ // Ignore collapse and image column
+ for (int i = EVENT_COLUMNS_START_INDEX; i < fTable.getColumns().length; i++) {
final TableColumn column = fTable.getColumns()[i];
final String filter = (String) column.getData(txtKey);
if (filter == null) {
*/
protected void setFilterStatusRowItemData(final TableItem item) {
for (int i = 0; i < fTable.getColumns().length; i++) {
- if (i == 0) {
+ if (i == MARGIN_COLUMN_INDEX) {
if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
item.setImage(FILTER_IMAGE);
} else {
item.setImage(STOP_IMAGE);
}
- item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
+ }
+
+ if (i == FILTER_SUMMARY_INDEX) {
+ item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
} else {
- item.setText(i, ""); //$NON-NLS-1$
+ item.setText(i, EMPTY_STRING);
}
}
item.setData(null);
}
fireFilterApplied(null);
updateStatusLine(null);
+
+ // Set original width
+ fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+ packMarginColumn();
}
/**
if (request.isCancelled()) {
return;
}
+ boolean refresh = false;
if (filter.matches(event)) {
final long rank = fFilterCheckCount;
final int index = (int) fFilterMatchCount;
fFilterMatchCount++;
fCache.storeEvent(event, rank, index);
- refreshTable();
- } else if ((fFilterCheckCount % 100) == 0) {
+ refresh = true;
+ } else {
+ if (filter instanceof TmfCollapseFilter) {
+ fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
+ }
+ }
+
+ if (refresh || (fFilterCheckCount % 100) == 0) {
refreshTable();
}
fFilterCheckCount++;
return;
}
fTable.setRedraw(false);
+ try {
+ TableColumn tableColumns[] = fTable.getColumns();
+ for (int i = 0; i < tableColumns.length; i++) {
+ final TableColumn column = tableColumns[i];
+ packSingleColumn(i, column);
+ }
+ } finally {
+ // Make sure that redraw is always enabled.
+ fTable.setRedraw(true);
+ }
+ fPackDone = true;
+ }
- boolean isLinux = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
- TableColumn tableColumns[] = fTable.getColumns();
- for (int i = 0; i < tableColumns.length; i++) {
- final TableColumn column = tableColumns[i];
- final int headerWidth = column.getWidth();
- column.pack();
- // Workaround for Linux which doesn't consider the image width of
- // search/filter row in TableColumn.pack() after having executed
- // TableItem.setImage((Image)null) for other rows than search/filter row.
- if (isLinux && (i == 0)) {
- column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
- }
+ private void packMarginColumn() {
+ TableColumn[] columns = fTable.getColumns();
+ if (columns.length > 0) {
+ packSingleColumn(0, columns[0]);
+ }
+ }
- if (column.getWidth() < headerWidth) {
- column.setWidth(headerWidth);
- }
+ private void packSingleColumn(int i, final TableColumn column) {
+ final int headerWidth = column.getWidth();
+ column.pack();
+ // Workaround for Linux which doesn't consider the image width of
+ // search/filter row in TableColumn.pack() after having executed
+ // TableItem.setImage((Image)null) for other rows than search/filter row.
+ boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
+ if (IS_LINUX && (i == 0) && isCollapseFilter) {
+ column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
}
- fTable.setRedraw(true);
- fPackDone = true;
+ if (column.getWidth() < headerWidth) {
+ column.setWidth(headerWidth);
+ }
}
/**
synchronized (columns) {
List<String> itemStrings = new ArrayList<>(columns.size());
for (TmfEventTableColumn column : columns) {
- itemStrings.add(column.getItemString(event));
+ ITmfEvent passedEvent = event;
+ if (!(column instanceof TmfMarginColumn) && (event instanceof CachedEvent)) {
+ // Make sure that the event object from the trace is passed
+ // to all columns but the TmfMarginColumn
+ passedEvent = ((CachedEvent) event).event;
+ }
+ if (passedEvent == null) {
+ itemStrings.add(EMPTY_STRING);
+ } else {
+ itemStrings.add(column.getItemString(passedEvent));
+ }
+
}
return itemStrings.toArray(new String[0]);
}
*/
public void setStatusLineManager(IStatusLineManager statusLineManager) {
if (fStatusLineManager != null && statusLineManager == null) {
- fStatusLineManager.setMessage(""); //$NON-NLS-1$
+ fStatusLineManager.setMessage(EMPTY_STRING);
}
fStatusLineManager = statusLineManager;
}
fTable.refresh();
fTable.redraw();
}
+
+ /**
+ * Margin column for images and special text (e.g. collapse count)
+ */
+ private static final class TmfMarginColumn extends TmfEventTableColumn {
+
+ private static final @NonNull String HEADER = EMPTY_STRING;
+
+ /**
+ * Constructor
+ */
+ public TmfMarginColumn() {
+ super(HEADER);
+ }
+
+ @Override
+ public String getItemString(ITmfEvent event) {
+ if (!(event instanceof CachedEvent) || ((CachedEvent) event).repeatCount == 0) {
+ return EMPTY_STRING;
+ }
+ return "+" + ((CachedEvent) event).repeatCount; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getFilterFieldId() {
+ return null;
+ }
+ }
+
}