/*******************************************************************************
- * Copyright (c) 2010, 2015 Ericsson
+ * Copyright (c) 2010, 2016 Ericsson and others.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
package org.eclipse.tracecompass.tmf.ui.viewers.events;
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfSourceLookup;
import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode;
-import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterObjectNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
-import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
import org.eclipse.tracecompass.tmf.core.util.Pair;
import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
+import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsTableHeader.IEventsTableHeaderListener;
import org.eclipse.tracecompass.tmf.ui.viewers.events.columns.TmfEventTableColumn;
import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSetting;
import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager;
private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
"icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$
private static final Image FILTER_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$
+ private static final Image FILTER_ADD_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/filter_add.gif"); //$NON-NLS-1$
private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$
private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;
- 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;
- private final class ColumnMovedListener extends ControlAdapter {
+ private final class ColumnListener extends ControlAdapter {
/*
- * Make sure that the margin column is always first and keep the
- * column order variable up to date.
+ * Make sure that the margin column is always first and keep the column
+ * order variable up to date.
*/
@Override
public void controlMoved(ControlEvent e) {
int[] order = fTable.getColumnOrder();
- if (order[0] == MARGIN_COLUMN_INDEX) {
- fColumnOrder = order;
- return;
- }
- for (int i = order.length - 1; i > 0; i--) {
- if (order[i] == MARGIN_COLUMN_INDEX) {
- order[i] = order[i - 1];
- order[i - 1] = MARGIN_COLUMN_INDEX;
+ if (order[0] != MARGIN_COLUMN_INDEX) {
+ for (int i = order.length - 1; i > 0; i--) {
+ if (order[i] == MARGIN_COLUMN_INDEX) {
+ order[i] = order[i - 1];
+ order[i - 1] = MARGIN_COLUMN_INDEX;
+ }
}
+ fTable.setColumnOrder(order);
+ }
+ fColumnOrder = order;
+ fTable.layout();
+ }
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ TableColumn column = (TableColumn) e.widget;
+ if (column.getResizable() && !isExpanded(column)) {
+ int i = (int) column.getData(Key.INDEX);
+ fColumnSize[i] = column.getWidth();
+ column.setData(Key.WIDTH, fColumnSize[i]);
}
- fTable.setColumnOrder(order);
- fColumnOrder = fTable.getColumnOrder();
}
}
}
}
- private final class PainItemListener implements Listener {
+ private static final class PainItemListener implements Listener {
@Override
public void handleEvent(Event event) {
TableItem item = (TableItem) event.item;
}
}
- private final class EraseItemListener implements Listener {
+ private static final class EraseItemListener implements Listener {
@Override
public void handleEvent(Event event) {
TableItem item = (TableItem) event.item;
GC gc = event.gc;
Color background = item.getBackground(event.index);
/*
- * Paint the background if it is not the default system color.
- * In Windows, if you let the widget draw the background, it
- * will not show the item's background color if the item is
- * selected or hot. If there are no style ranges and the item
- * background is the default system color, we do not want to
- * paint it or otherwise we would override the platform theme
- * (e.g. alternating colors).
+ * Paint the background if it is not the default system color. In
+ * Windows, if you let the widget draw the background, it will not
+ * show the item's background color if the item is selected or hot.
+ * If there are no style ranges and the item background is the
+ * default system color, we do not want to paint it or otherwise we
+ * would override the platform theme (e.g. alternating colors).
*/
if (styleRanges != null || !background.equals(item.getParent().getBackground())) {
// we will paint the table item's background
/*
* We will paint the table item's foreground. In Windows, if you
- * paint the background but let the widget draw the foreground,
- * it will override your background, unless the item is selected
- * or hot.
+ * paint the background but let the widget draw the foreground, it
+ * will override your background, unless the item is selected or
+ * hot.
*/
event.detail &= ~SWT.FOREGROUND;
if (item == null) {
return;
}
- final Long rank = (Long) item.getData(Key.RANK);
- if (rank == null) {
- return;
- }
- final String tooltipText = (String) item.getData(Key.BOOKMARK);
- final Rectangle bounds = item.getImageBounds(0);
- bounds.width = BOOKMARK_IMAGE.getBounds().width;
- if (!bounds.contains(event.x, event.y)) {
- return;
+ String text;
+ if (fTable.indexOf(item) == 0) {
+ if (fHeaderState == HeaderState.SEARCH && item.getBounds(0).contains(event.x, event.y)) {
+ text = Messages.TmfEventsTable_AddAsFilterText;
+ } else {
+ return;
+ }
+ } else {
+ final Long rank = (Long) item.getData(Key.RANK);
+ if (rank == null) {
+ return;
+ }
+ final String tooltipText = (String) item.getData(Key.BOOKMARK);
+ final Rectangle bounds = item.getImageBounds(0);
+ bounds.width = BOOKMARK_IMAGE.getBounds().width;
+ if (!bounds.contains(event.x, event.y)) {
+ return;
+ }
+ text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
}
if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
tooltipShell.dispose();
layout.marginWidth = 2;
tooltipShell.setLayout(layout);
final Label label = new Label(tooltipShell, SWT.WRAP);
- 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);
/*
* Bug in Linux. The coordinates of the event have an origin
* that excludes the table header but the method toDisplay()
- * expects coordinates relative to an origin that includes
- * the table header.
+ * expects coordinates relative to an origin that includes the
+ * table header.
*/
int y = event.y;
if (IS_LINUX) {
* The events table search/filter/data keys
*
* @author Patrick Tasse
- * @noimplement This interface only contains Event Table specific
- * static definitions.
+ * @noimplement This interface only contains Event Table specific static
+ * definitions.
*/
public interface Key {
* @since 1.1
*/
String WIDTH = "$width"; //$NON-NLS-1$
+
+ /**
+ * The position of the column
+ *
+ * @since 2.1
+ */
+ String INDEX = "$index"; //$NON-NLS-1$
}
/**
* @author Patrick Tasse
*/
public static enum HeaderState {
- /** A search is being run */
- SEARCH,
+ /**
+ * No search filter is applied
+ *
+ * @since 2.0
+ */
+ NO_SEARCH,
- /** A filter is applied */
- FILTER
+ /** A search filter is applied */
+ SEARCH
}
interface Direction {
// Table data
// ------------------------------------------------------------------------
+ /** The header bar */
+ private TmfEventsTableHeader fHeaderBar;
+
/** The virtual event table */
protected TmfVirtualTable fTable;
private Composite fComposite;
private SashForm fSashForm;
+ private Composite fTableComposite;
private TmfRawEventViewer fRawViewer;
private ITmfTrace fTrace;
private volatile boolean fPackDone = false;
- private HeaderState fHeaderState = HeaderState.SEARCH;
+ private volatile boolean fPackMarginDone = false;
+ private HeaderState fHeaderState = HeaderState.NO_SEARCH;
private long fSelectedRank = -1;
private long fSelectedBeginRank = -1;
private ITmfTimestamp fSelectedBeginTimestamp = null;
private final Object fFilterSyncObj = new Object();
private SearchThread fSearchThread;
private final Object fSearchSyncObj = new Object();
+ private boolean fCollapseFilterEnabled = false;
/**
* List of selection change listeners (element type:
private MenuManager fTablePopupMenuManager;
private MenuManager fHeaderPopupMenuManager;
+ private boolean[] fColumnResizable;
+
+ private int[] fColumnSize;
+
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
}
@Deprecated
- private static @NonNull Iterable<ITmfEventAspect> convertFromColumnData(
+ private static @NonNull Iterable<ITmfEventAspect<?>> convertFromColumnData(
org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
- ImmutableList.Builder<ITmfEventAspect> builder = new ImmutableList.Builder<>();
+ ImmutableList.Builder<ITmfEventAspect<?>> builder = new ImmutableList.Builder<>();
for (org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData col : columnData) {
String fieldName = col.header;
if (fieldName != null) {
builder.add(new TmfContentFieldAspect(fieldName, fieldName));
}
}
- return checkNotNull(builder.build());
+ return builder.build();
}
/**
* </p>
*/
public TmfEventsTable(final Composite parent, int cacheSize,
- @NonNull Iterable<ITmfEventAspect> aspects) {
+ @NonNull Iterable<ITmfEventAspect<?>> aspects) {
super("TmfEventsTable"); //$NON-NLS-1$
fComposite = new Composite(parent, SWT.NONE);
- final GridLayout gl = new GridLayout(1, false);
+ GridLayout gl = new GridLayout(1, false);
gl.marginHeight = 0;
gl.marginWidth = 0;
gl.verticalSpacing = 0;
fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ // Create a composite for the table and its header bar
+ fTableComposite = new Composite(fSashForm, SWT.NONE);
+ fTableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ gl = new GridLayout(1, false);
+ gl.marginHeight = 0;
+ gl.marginWidth = 0;
+ gl.verticalSpacing = 0;
+ fTableComposite.setLayout(gl);
+
+ // Create an events table header bar
+ fHeaderBar = new TmfEventsTableHeader(fTableComposite, SWT.NONE, new IEventsTableHeaderListener() {
+ @Override
+ public void filterSelected(ITmfFilter filter) {
+ if (filter instanceof TmfFilterMatchesNode) {
+ TmfFilterMatchesNode matchFilter = (TmfFilterMatchesNode) filter;
+ for (TableColumn col : fTable.getColumns()) {
+ if (col.getData(Key.ASPECT) == matchFilter.getEventAspect()) {
+ col.setData(Key.FILTER_TXT, matchFilter.getRegex());
+ } else {
+ col.setData(Key.FILTER_TXT, null);
+ }
+ }
+ fTable.refresh();
+ fTable.redraw();
+ }
+ }
+
+ @Override
+ public void filterRemoved(ITmfFilter filter) {
+ for (TableColumn col : fTable.getColumns()) {
+ col.setData(Key.FILTER_TXT, null);
+ }
+ removeFilter(filter);
+ }
+ });
+
// Create a virtual table
final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
- fTable = new TmfVirtualTable(fSashForm, style);
+ fTable = new TmfVirtualTable(fTableComposite, style);
// Set the table layout
final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
fTable.setLinesVisible(true);
// Setup the columns
- for (ITmfEventAspect aspect : aspects) {
+ for (ITmfEventAspect<?> aspect : aspects) {
if (aspect != null) {
fColumns.add(new TmfEventTableColumn(aspect));
+
}
}
fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
fHeaderMenu = new Menu(fTable);
+
+ fColumnSize = new int[fColumns.size()];
+ fColumnResizable = new boolean[fColumns.size()];
+ int i = 0;
// Create the UI columns in the table
for (TmfEventTableColumn col : fColumns) {
TableColumn column = fTable.newTableColumn(SWT.LEFT);
column.setText(col.getHeaderName());
column.setToolTipText(col.getHeaderTooltip());
column.setData(Key.ASPECT, col.getEventAspect());
- column.pack();
+ column.setData(Key.INDEX, i);
if (col instanceof TmfMarginColumn) {
column.setResizable(false);
} else {
+ column.pack();
column.setMoveable(true);
- column.setData(Key.WIDTH, -1);
+ column.setData(Key.WIDTH, column.getWidth());
+ fColumnSize[i] = column.getWidth();
}
- column.addControlListener(new ColumnMovedListener());
+ column.addControlListener(new ColumnListener());
+ fColumnResizable[i] = column.getResizable();
+ i++;
}
fColumnOrder = fTable.getColumnOrder();
// Handle the table item requests
fTable.addListener(SWT.SetData, new SetDataListener());
- fTable.addListener(SWT.MenuDetect, new Listener() {
- @Override
- public void handleEvent(Event event) {
- fLastMenuCursorLocation = new Point(event.x, event.y);
- Point pt = fTable.getDisplay().map(null, fTable, fLastMenuCursorLocation);
- Rectangle clientArea = fTable.getClientArea();
- boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + fTable.getHeaderHeight());
- fTable.setMenu(header ? fHeaderMenu : fTablePopup);
- }
+ fTable.addMenuDetectListener(event -> {
+ fLastMenuCursorLocation = new Point(event.x, event.y);
+ Point pt = fTable.getDisplay().map(null, fTable, fLastMenuCursorLocation);
+ Rectangle clientArea = fTable.getClientArea();
+ boolean header = clientArea.y <= pt.y && pt.y < (clientArea.y + fTable.getHeaderHeight());
+ fTable.setMenu(header ? fHeaderMenu : fTablePopup);
});
fTable.addMouseListener(new MouseDoubleClickListener());
fRawViewer.setVisible(false);
createPopupMenu();
+
+ fComposite.addDisposeListener((e) -> {
+ internalDispose();
+ });
}
/**
* @param column
* the column
*/
- private static IAction createHeaderAction(final TableColumn column) {
+ private IAction createHeaderAction(final TableColumn column) {
final IAction columnMenuAction = new Action(column.getText(), IAction.AS_CHECK_BOX) {
@Override
public void run() {
- if (isChecked()) {
- column.setWidth((int) column.getData(Key.WIDTH));
+ boolean isChecked = isChecked();
+ if (isChecked) {
+ int width = (int) column.getData(Key.WIDTH);
column.setResizable(true);
+ if (width == 0) {
+ column.pack();
+ } else {
+ column.setWidth(width);
+ }
} else {
- column.setData(Key.WIDTH, column.getWidth());
- column.setWidth(0);
column.setResizable(false);
+ column.setWidth(0);
}
+ int pos = (int) column.getData(Key.INDEX);
+ fColumnResizable[pos] = isChecked;
}
};
columnMenuAction.setChecked(column.getResizable());
@Override
public void run() {
for (TableColumn column : fTable.getColumns()) {
- final Object widthVal = column.getData(Key.WIDTH);
- if (widthVal instanceof Integer) {
- Integer width = (Integer) widthVal;
- if (!column.getResizable()) {
+ int index = (int) column.getData(Key.INDEX);
+ if (index != MARGIN_COLUMN_INDEX) {
+ final int width = (int) column.getData(Key.WIDTH);
+ column.setResizable(true);
+ if (width == 0) {
+ column.pack();
+ } else {
column.setWidth(width);
- column.setResizable(true);
- /*
- * This is because Linux always resizes the last
- * column to fill in the void, this means that
- * hiding a column resizes others and we can have 10
- * columns that are 1000 pixels wide by hiding the
- * last one progressively.
- */
- if (IS_LINUX) {
- column.pack();
- }
}
+ fColumnResizable[index] = true;
}
}
}
final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
@Override
public void run() {
- fTable.setVisible(true);
+ fTableComposite.setVisible(true);
fSashForm.layout();
}
};
final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
@Override
public void run() {
- fTable.setVisible(false);
+ fTableComposite.setVisible(false);
fSashForm.layout();
}
};
if (cs == null) {
return;
}
+ Long lineNo = cs.getLineNo();
+ if (lineNo == null) {
+ /* Not enough information to provide a full callsite */
+ return;
+ }
String fileName = cs.getFileName();
final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
* the line number, then seek there.
*/
ITextEditor textEditor = (ITextEditor) editor;
- int lineNumber = Long.valueOf(cs.getLineNumber()).intValue();
+ int lineNumber = lineNo.intValue();
IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
IRegion region = document.getLineInformation(lineNumber - 1);
*/
IMarker marker = null;
final ArrayList<IFile> files = new ArrayList<>();
+ IPath p = new Path(trimmedPath);
ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
@Override
public boolean visit(IResource resource) throws CoreException {
- if (resource instanceof IFile && resource.getFullPath().toString().endsWith(trimmedPath)) {
+ if (resource instanceof IFile && resource.getFullPath().toString().endsWith(p.lastSegment())) {
files.add((IFile) resource);
}
return true;
}
if (file != null) {
marker = file.createMarker(IMarker.MARKER);
- marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
+ marker.setAttribute(IMarker.LINE_NUMBER, lineNo.intValue());
IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
marker.delete();
} else if (files.isEmpty()) {
}
};
- final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
+ final IAction addAsFilterAction = new Action(Messages.TmfEventsTable_AddAsFilterText) {
@Override
public void run() {
- fHeaderState = HeaderState.SEARCH;
- fTable.refresh();
- fTable.redraw();
- }
- };
-
- final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
- @Override
- public void run() {
- fHeaderState = HeaderState.FILTER;
- fTable.refresh();
- fTable.redraw();
+ applySearchAsFilter();
}
};
public void menuAboutToShow(final IMenuManager manager) {
if (fTable.getSelectionIndices().length == 1 && fTable.getSelectionIndices()[0] == 0) {
// Right-click on header row
- if (fHeaderState == HeaderState.FILTER) {
- fTablePopupMenuManager.add(showSearchBarAction);
- } else {
- fTablePopupMenuManager.add(showFilterBarAction);
+ if (fHeaderState == HeaderState.SEARCH) {
+ fTablePopupMenuManager.add(addAsFilterAction);
}
return;
}
}
}
- if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
+ if (isCollapsible && !fCollapseFilterEnabled) {
fTablePopupMenuManager.add(collapseAction);
fTablePopupMenuManager.add(new Separator());
}
@Override
public void dispose() {
+ fComposite.dispose();
+ }
+
+ private void internalDispose() {
stopSearchThread();
stopFilterThread();
PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(this);
ColorSettingsManager.removeColorSettingsListener(this);
- fComposite.dispose();
+ fCache.clear();
if ((fTrace != null) && fDisposeOnClose) {
fTrace.dispose();
}
fResourceManager.dispose();
- fRawViewer.dispose();
if (fRawViewerPopupMenuManager != null) {
fRawViewerPopupMenuManager.dispose();
}
}
item.setText(itemStrings);
item.setData(tmfEvent);
- item.setData(Key.TIMESTAMP, new TmfTimestamp(tmfEvent.getTimestamp()));
+ item.setData(Key.TIMESTAMP, tmfEvent.getTimestamp());
item.setData(Key.RANK, rank);
final Collection<Long> markerIds = fBookmarksMap.get(rank);
for (int index = 0; index < fTable.getColumns().length; index++) {
TableColumn column = fTable.getColumns()[index];
String regex = null;
- if (fHeaderState == HeaderState.FILTER) {
+ if (fHeaderState == HeaderState.SEARCH) {
+ if (searchMatch) {
+ regex = (String) column.getData(Key.SEARCH_TXT);
+ }
+ } else {
regex = (String) column.getData(Key.FILTER_TXT);
- } else if (searchMatch) {
- regex = (String) column.getData(Key.SEARCH_TXT);
}
if (regex != null) {
String text = item.getText(index);
* The item to use as table header
*/
protected void setHeaderRowItemData(final TableItem item) {
- String txtKey = null;
- if (fHeaderState == HeaderState.SEARCH) {
+ if (fHeaderState == HeaderState.NO_SEARCH) {
item.setImage(SEARCH_IMAGE);
- txtKey = Key.SEARCH_TXT;
- } else if (fHeaderState == HeaderState.FILTER) {
- item.setImage(FILTER_IMAGE);
- txtKey = Key.FILTER_TXT;
+ } else if (fHeaderState == HeaderState.SEARCH) {
+ item.setImage(FILTER_ADD_IMAGE);
}
item.setForeground(fGrayColor);
// 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);
+ final String filter = (String) column.getData(Key.SEARCH_TXT);
if (filter == null) {
- if (fHeaderState == HeaderState.SEARCH) {
- item.setText(i, SEARCH_HINT);
- } else if (fHeaderState == HeaderState.FILTER) {
- item.setText(i, FILTER_HINT);
- }
+ item.setText(i, SEARCH_HINT);
item.setForeground(i, fGrayColor);
item.setFont(i, fFont);
} else {
item.setFont(i, fBoldFont);
}
}
+ if (!fPackMarginDone) {
+ packMarginColumn();
+ fPackMarginDone = true;
+ }
}
/**
// Margin column selected
if (item.getBounds(0).contains(point)) {
if (fHeaderState == HeaderState.SEARCH) {
- fHeaderState = HeaderState.FILTER;
- } else if (fHeaderState == HeaderState.FILTER) {
- fHeaderState = HeaderState.SEARCH;
+ applySearchAsFilter();
}
- fTable.setSelection(0);
- fTable.refresh();
- fTable.redraw();
return;
}
column = fTable.getColumns()[columnIndex];
- String txtKey = null;
- if (fHeaderState == HeaderState.SEARCH) {
- txtKey = Key.SEARCH_TXT;
- } else if (fHeaderState == HeaderState.FILTER) {
- txtKey = Key.FILTER_TXT;
- }
-
/*
* The control that will be the editor must be a child of
* the Table
*/
final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
- final String headerString = (String) column.getData(txtKey);
+ final String headerString = (String) column.getData(Key.SEARCH_TXT);
if (headerString != null) {
newEditor.setText(headerString);
}
if (e.character == SWT.CR) {
updateHeader(newEditor.getText());
applyHeader();
-
+ if ((e.stateMask & SWT.CTRL) != 0) {
+ applySearchAsFilter();
+ }
/*
* Set focus on the table so that the next
* carriage return goes to the next result
TmfEventsTable.this.getTable().setFocus();
} else if (e.character == SWT.ESC) {
tableEditor.getEditor().dispose();
+ TmfEventsTable.this.getTable().setFocus();
}
}
});
* returns true is value was changed
*/
private boolean updateHeader(final String regex) {
- String objKey = null;
- String txtKey = null;
- if (fHeaderState == HeaderState.SEARCH) {
- objKey = Key.SEARCH_OBJ;
- txtKey = Key.SEARCH_TXT;
- } else if (fHeaderState == HeaderState.FILTER) {
- objKey = Key.FILTER_OBJ;
- txtKey = Key.FILTER_TXT;
- }
if (regex.length() > 0) {
try {
Pattern.compile(regex);
- if (regex.equals(column.getData(txtKey))) {
+ if (regex.equals(column.getData(Key.SEARCH_TXT))) {
tableEditor.getEditor().dispose();
return false;
}
final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
- ITmfEventAspect aspect = (ITmfEventAspect) column.getData(Key.ASPECT);
+ ITmfEventAspect<?> aspect = (ITmfEventAspect<?>) column.getData(Key.ASPECT);
filter.setEventAspect(aspect);
filter.setRegex(regex);
- column.setData(objKey, filter);
- column.setData(txtKey, regex);
+ column.setData(Key.SEARCH_OBJ, filter);
+ column.setData(Key.SEARCH_TXT, regex);
} catch (final PatternSyntaxException ex) {
tableEditor.getEditor().dispose();
MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
return false;
}
} else {
- if (column.getData(txtKey) == null) {
+ if (column.getData(Key.SEARCH_TXT) == null) {
tableEditor.getEditor().dispose();
return false;
}
- column.setData(objKey, null);
- column.setData(txtKey, null);
+ column.setData(Key.SEARCH_OBJ, null);
+ column.setData(Key.SEARCH_TXT, null);
}
return true;
}
private void applyHeader() {
- if (fHeaderState == HeaderState.SEARCH) {
- stopSearchThread();
- final TmfFilterAndNode filter = new TmfFilterAndNode(null);
- for (final TableColumn col : fTable.getColumns()) {
- final Object filterObj = col.getData(Key.SEARCH_OBJ);
- if (filterObj instanceof ITmfFilterTreeNode) {
- filter.addChild((ITmfFilterTreeNode) filterObj);
- }
- }
- if (filter.getChildrenCount() > 0) {
- fTable.setData(Key.SEARCH_OBJ, filter);
- fTable.refresh();
- searchNext();
- fireSearchApplied(filter);
- } else {
- fTable.setData(Key.SEARCH_OBJ, null);
- fTable.refresh();
- fireSearchApplied(null);
- }
- } else if (fHeaderState == HeaderState.FILTER) {
- final TmfFilterAndNode filter = new TmfFilterAndNode(null);
- for (final TableColumn col : fTable.getColumns()) {
- final Object filterObj = col.getData(Key.FILTER_OBJ);
- if (filterObj instanceof ITmfFilterTreeNode) {
- filter.addChild((ITmfFilterTreeNode) filterObj);
- }
- }
- if (filter.getChildrenCount() > 0) {
- applyFilter(filter);
- } else {
- clearFilters();
+ stopSearchThread();
+ final TmfFilterRootNode filter = new TmfFilterRootNode();
+ for (final TableColumn col : fTable.getColumns()) {
+ final Object filterObj = col.getData(Key.SEARCH_OBJ);
+ if (filterObj instanceof ITmfFilterTreeNode) {
+ filter.addChild((ITmfFilterTreeNode) filterObj);
}
}
+ if (filter.getChildrenCount() > 0) {
+ fHeaderState = HeaderState.SEARCH;
+ fTable.setData(Key.SEARCH_OBJ, filter);
+ fTable.refresh();
+ searchNext();
+ fireSearchApplied(filter);
+ } else {
+ fHeaderState = HeaderState.NO_SEARCH;
+ fTable.setData(Key.SEARCH_OBJ, null);
+ fTable.refresh();
+ fireSearchApplied(null);
+ }
tableEditor.getEditor().dispose();
}
fTable.refresh();
} else if (e.character == SWT.DEL) {
if (fHeaderState == HeaderState.SEARCH) {
+ fHeaderState = HeaderState.NO_SEARCH;
stopSearchThread();
for (final TableColumn column : fTable.getColumns()) {
column.setData(Key.SEARCH_OBJ, null);
column.setData(Key.SEARCH_TXT, null);
+ column.setData(Key.FILTER_TXT, null);
}
fTable.setData(Key.SEARCH_OBJ, null);
fTable.refresh();
fireSearchApplied(null);
- } else if (fHeaderState == HeaderState.FILTER) {
- clearFilters();
+ } else {
+ for (final TableColumn column : fTable.getColumns()) {
+ column.setData(Key.FILTER_TXT, null);
+ }
+ fTable.refresh();
}
} else if (e.character == SWT.CR) {
- if ((e.stateMask & SWT.SHIFT) == 0) {
+ if ((e.stateMask & SWT.CTRL) != 0) {
+ if (fHeaderState == HeaderState.SEARCH) {
+ applySearchAsFilter();
+ }
+ } else if ((e.stateMask & SWT.SHIFT) == 0) {
searchNext();
} else {
searchPrevious();
});
}
+ /**
+ * Apply the current search condition as a new filter.
+ *
+ * @since 2.0
+ */
+ protected void applySearchAsFilter() {
+ Object searchObj = fTable.getData(Key.SEARCH_OBJ);
+ if (searchObj instanceof ITmfFilter) {
+ ITmfFilter filter = (ITmfFilter) searchObj;
+ fTable.setData(Key.SEARCH_OBJ, null);
+ fireSearchApplied(null);
+ fHeaderState = HeaderState.NO_SEARCH;
+ for (final TableColumn col : fTable.getColumns()) {
+ col.setData(Key.FILTER_TXT, col.getData(Key.SEARCH_TXT));
+ col.setData(Key.SEARCH_TXT, null);
+ col.setData(Key.SEARCH_OBJ, null);
+ }
+ applyFilter(filter);
+ }
+ }
+
/**
* Send an event indicating a filter has been applied.
*
}
/**
- * Apply a filter.
+ * Apply a filter. It is added to the existing filters.
*
* @param filter
* The filter to apply
stopSearchThread();
fFilterMatchCount = 0;
fFilterCheckCount = 0;
- fCache.applyFilter(filter);
+ ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+ if (rootFilter == null) {
+ rootFilter = new TmfFilterRootNode();
+ }
+ if (filter instanceof TmfFilterRootNode) {
+ TmfFilterRootNode parentFilter = (TmfFilterRootNode) filter;
+ for (ITmfFilterTreeNode child : parentFilter.getChildren()) {
+ rootFilter.addChild(child);
+ }
+ } else if (filter instanceof TmfCollapseFilter) {
+ fCollapseFilterEnabled = true;
+ } else if (filter instanceof ITmfFilterTreeNode) {
+ rootFilter.addChild((ITmfFilterTreeNode) filter);
+ } else {
+ rootFilter.addChild(new TmfFilterObjectNode(filter));
+ }
+ fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
+ fHeaderBar.addFilter(filter);
+ fTable.clearAll();
+ fTable.setData(Key.FILTER_OBJ, rootFilter);
+ /* +1 for header row, +2 for top and bottom filter status rows */
+ fTable.setItemCount(3);
+ startFilterThread();
+ fireFilterApplied(rootFilter);
+ }
+
+ /**
+ * Remove a filter. Any other existing filters remain applied.
+ *
+ * @param filter
+ * The filter to remove
+ * @since 2.0
+ */
+ protected void removeFilter(ITmfFilter filter) {
+ ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+ if (rootFilter == null) {
+ return;
+ }
+ stopFilterThread();
+ stopSearchThread();
+ fFilterMatchCount = 0;
+ fFilterCheckCount = 0;
+ if (filter instanceof TmfCollapseFilter) {
+ fCollapseFilterEnabled = false;
+ } else if (filter instanceof ITmfFilterTreeNode) {
+ rootFilter.removeChild((ITmfFilterTreeNode) filter);
+ } else {
+ for (ITmfFilterTreeNode child : rootFilter.getChildren()) {
+ if (child instanceof TmfFilterObjectNode) {
+ if (((TmfFilterObjectNode) child).getFilter().equals(filter)) {
+ rootFilter.removeChild(child);
+ break;
+ }
+ }
+ }
+ }
+ if (!rootFilter.hasChildren() && !fCollapseFilterEnabled) {
+ clearFilters();
+ return;
+ }
+ fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
+ fHeaderBar.removeFilter(filter);
fTable.clearAll();
- fTable.setData(Key.FILTER_OBJ, filter);
+ fTable.setData(Key.FILTER_OBJ, rootFilter);
/* +1 for header row, +2 for top and bottom filter status rows */
fTable.setItemCount(3);
startFilterThread();
- fireFilterApplied(filter);
+ fireFilterApplied(rootFilter);
+
+ // Set original width
+ fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+ packMarginColumn();
}
/**
stopFilterThread();
stopSearchThread();
fCache.clearFilter();
+ fHeaderBar.clearFilters();
+ fCollapseFilterEnabled = false;
fTable.clearAll();
for (final TableColumn column : fTable.getColumns()) {
column.setData(Key.FILTER_OBJ, null);
*/
protected class FilterThread extends Thread {
private final ITmfFilterTreeNode filter;
+ private TmfCollapseFilter collapseFilter = null;
private TmfEventRequest request;
private boolean refreshBusy = false;
private boolean refreshPending = false;
if (fTrace == null) {
return;
}
+ if (fCollapseFilterEnabled) {
+ collapseFilter = new TmfCollapseFilter();
+ }
final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
if (nbRequested <= 0) {
return;
}
boolean refresh = false;
if (filter.matches(event)) {
- final long rank = fFilterCheckCount;
- final int index = (int) fFilterMatchCount;
- fFilterMatchCount++;
- fCache.storeEvent(event, rank, index);
- refresh = true;
- } else {
- if (filter instanceof TmfCollapseFilter) {
+ if (collapseFilter == null || collapseFilter.matches(event)) {
+ final long rank = fFilterCheckCount;
+ final int index = (int) fFilterMatchCount;
+ fFilterMatchCount++;
+ fCache.storeEvent(event, rank, index);
+ } else if (collapseFilter != null) {
fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
}
+ refresh = true;
}
if (refresh || (fFilterCheckCount % 100) == 0) {
final int selection = index + 1 + (eventFilter != null ? +1 : 0);
display.asyncExec(new Runnable() {
+
@Override
public void run() {
if (monitor.isCanceled()) {
}
});
return Status.OK_STATUS;
+
}
@Override
protected void canceling() {
- request.cancel();
+ if (request != null) {
+ request.cancel();
+ }
synchronized (fSearchSyncObj) {
fSearchThread = null;
}
}
+
}
/**
/**
* Pack the columns.
+ *
+ * @return Whether or not a pack was done in this call. Otherwise, it was
+ * already done by a previous call
+ *
+ * @since 2.0
*/
- protected void packColumns() {
+ protected boolean packColumns() {
if (fPackDone) {
- return;
+ return false;
}
fTable.setRedraw(false);
try {
fTable.setRedraw(true);
}
fPackDone = true;
+ return true;
}
private void packMarginColumn() {
}
private void packSingleColumn(int i, final TableColumn column) {
- final int headerWidth = column.getWidth();
+ if (i != MARGIN_COLUMN_INDEX && !column.getResizable()) {
+ return;
+ }
+ Object data = column.getData(Key.WIDTH);
+ final int headerWidth = data instanceof Integer ? (int) data : -1;
column.pack();
/*
* Workaround for Linux which doesn't consider the image width of
* search/filter row in TableColumn.pack() after having executed
* TableItem.setImage(null) for other rows than search/filter row.
*/
- boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
- if (IS_LINUX && (i == 0) && isCollapseFilter) {
+ if (IS_LINUX && (i == MARGIN_COLUMN_INDEX) && fCollapseFilterEnabled) {
column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
}
if (column.getWidth() < headerWidth) {
column.setWidth(headerWidth);
+ } else if (i != MARGIN_COLUMN_INDEX) {
+ column.setData(Key.WIDTH, column.getWidth());
}
}
/**
* Returns true if the column is a visible event column.
*
- * @param column the column
- * @return false if the column is the margin column or hidden, true otherwise
+ * @param column
+ * the column
+ * @return false if the column is the margin column or hidden, true
+ * otherwise
*/
private static boolean isVisibleEventColumn(TableColumn column) {
if (column.getData(Key.ASPECT) == TmfMarginColumn.MARGIN_ASPECT) {
return true;
}
+ /**
+ * Returns true if the column is expanded to take extra available space.
+ * This is the last non-zero-width visible column in the column order on
+ * Linux. This column's width should not be persisted.
+ *
+ * @param column
+ * the column
+ * @return true if the column is expanded.
+ */
+ private static boolean isExpanded(TableColumn column) {
+ if (IS_LINUX) {
+ Table table = column.getParent();
+ int[] order = table.getColumnOrder();
+ for (int i = order.length - 1; i >= 0; i--) {
+ TableColumn col = table.getColumn(order[i]);
+ if (col == column) {
+ return true;
+ }
+ if (col.getWidth() > 0) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Get the array of item strings (e.g., what to display in each cell of the
* table row) corresponding to the columns and trace event passed in
return fColumnOrder;
}
+ /**
+ * Get column widths
+ *
+ * @return the current visual widths of the receiver's columns
+ * @since 2.1
+ */
+ public int[] getColumnWidth() {
+ return fColumnSize;
+ }
+
+ /**
+ * Get whether the columns are resizable
+ *
+ * @return an array stating if each column is resizable
+ * @since 2.1
+ */
+ public boolean[] getColumnResizable() {
+ return fColumnResizable;
+ }
+
/**
* Sets the order that the columns in the receiver should be displayed in to
* the given argument which is described in terms of the zero-relative
fColumnOrder = fTable.getColumnOrder();
}
+ /**
+ * Sets the column width and resizability
+ *
+ * @param width
+ * an array of widths
+ * @param resizable
+ * an array of bools saying if a column is resizable or not
+ * @since 2.1
+ */
+ public void setColumnWidth(int[] width, boolean[] resizable) {
+ int length = fTable.getColumns().length;
+ if (width == null || resizable == null || resizable.length != length || width.length != length) {
+ return;
+ }
+ int i = 0;
+ for (TableColumn column : fTable.getColumns()) {
+ if (i != MARGIN_COLUMN_INDEX) {
+ column.setData(Key.WIDTH, width[i]);
+ column.setResizable(resizable[i]);
+ if (column.getResizable()) {
+ column.setWidth((int) column.getData(Key.WIDTH));
+ } else {
+ column.setWidth(0);
+ }
+ }
+ i++;
+ }
+ fColumnSize = width;
+ fColumnResizable = resizable;
+ /* Don't pack, it would override these settings */
+ fPackDone = true;
+ }
+
/**
* Notify this table that is got the UI focus.
*/
* @param site
* the site that the context menus will be registered for
*
- * @since 2.0
+ * @since 1.2
*/
public void registerContextMenus(IWorkbenchPartSite site) {
if (site instanceof IEditorSite) {
bookmark.setAttribute(IMarker.MESSAGE, message.toString());
bookmark.setAttribute(IMarker.LOCATION, location);
bookmark.setAttribute(ITmfMarker.MARKER_RANK, rank.toString());
- bookmark.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(new TmfNanoTimestamp(timestamp).getValue()));
+ bookmark.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(timestamp.toNanos()));
bookmark.setAttribute(ITmfMarker.MARKER_COLOR, dialog.getColorValue().toString());
id[0] = bookmark.getId();
}
try {
String timeString = marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null);
long time = Long.parseLong(timeString);
- tsBegin = new TmfNanoTimestamp(time);
+ tsBegin = TmfTimestamp.fromNanos(time);
String durationString = marker.getAttribute(ITmfMarker.MARKER_DURATION, (String) null);
long duration = Long.parseLong(durationString);
- tsEnd = new TmfNanoTimestamp(time + duration);
+ tsEnd = TmfTimestamp.fromNanos(time + duration);
} catch (NumberFormatException e) {
/* ignored */
}
final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
- TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
- TmfTimestamp tf = new TmfTimestamp(signal.getEndTime());
+ ITmfTimestamp ts = signal.getBeginTime();
+ ITmfTimestamp tf = signal.getEndTime();
@Override
public void handleSuccess() {
contextEnd = fTrace.seekEvent(te);
rankEnd = contextEnd.getRank();
contextEnd.dispose();
- /* To include all events at the begin time, seek at the next nanosecond and then use the previous rank */
+ /*
+ * To include all events at the begin time, seek at the
+ * next nanosecond and then use the previous rank
+ */
tb = timestampBegin.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
if (tb.compareTo(fTrace.getEndTime()) <= 0) {
contextBegin = fTrace.seekEvent(tb);
rankBegin = ITmfContext.UNKNOWN_RANK;
}
rankBegin = (rankBegin == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankBegin) - 1;
- /* If no events in selection range, select only the next event */
+ /*
+ * If no events in selection range, select only the next
+ * event
+ */
rankBegin = rankBegin >= rankEnd ? rankBegin : rankEnd;
} else {
tb = timestampBegin;
contextBegin = fTrace.seekEvent(tb);
rankBegin = contextBegin.getRank();
contextBegin.dispose();
- /* To include all events at the end time, seek at the next nanosecond and then use the previous rank */
+ /*
+ * To include all events at the end time, seek at the
+ * next nanosecond and then use the previous rank
+ */
te = timestampEnd.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
if (te.compareTo(fTrace.getEndTime()) <= 0) {
contextEnd = fTrace.seekEvent(te);
rankEnd = ITmfContext.UNKNOWN_RANK;
}
rankEnd = (rankEnd == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankEnd) - 1;
- /* If no events in selection range, select only the next event */
+ /*
+ * If no events in selection range, select only the next
+ * event
+ */
rankEnd = rankEnd >= rankBegin ? rankEnd : rankBegin;
}
return new Pair<>(Long.valueOf(rankBegin), Long.valueOf(rankEnd));
*/
private static final class TmfMarginColumn extends TmfEventTableColumn {
- private static final @NonNull ITmfEventAspect MARGIN_ASPECT = new ITmfEventAspect() {
+ private static final @NonNull ITmfEventAspect<String> MARGIN_ASPECT = new ITmfEventAspect<String>() {
@Override
public String getName() {
super(MARGIN_ASPECT);
}
}
-
-}
+}
\ No newline at end of file