-/*******************************************************************************\r
- * Copyright (c) 2010, 2011, 2012 Ericsson\r
- *\r
- * All rights reserved. This program and the accompanying materials are\r
- * made available under the terms of the Eclipse Public License v1.0 which\r
- * accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Francois Chouinard - Initial API and implementation\r
- * Patrick Tasse - Factored out from events view\r
- * Francois Chouinard - Replaced Table by TmfVirtualTable\r
- * Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)\r
- *******************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.viewers.events;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.regex.Pattern;\r
-import java.util.regex.PatternSyntaxException;\r
-\r
-import org.eclipse.core.resources.IFile;\r
-import org.eclipse.core.resources.IMarker;\r
-import org.eclipse.core.resources.IResource;\r
-import org.eclipse.core.runtime.CoreException;\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.core.runtime.jobs.Job;\r
-import org.eclipse.jface.action.Action;\r
-import org.eclipse.jface.action.IAction;\r
-import org.eclipse.jface.action.IMenuListener;\r
-import org.eclipse.jface.action.IMenuManager;\r
-import org.eclipse.jface.action.MenuManager;\r
-import org.eclipse.jface.action.Separator;\r
-import org.eclipse.jface.dialogs.InputDialog;\r
-import org.eclipse.jface.dialogs.MessageDialog;\r
-import org.eclipse.jface.resource.FontDescriptor;\r
-import org.eclipse.jface.resource.JFaceResources;\r
-import org.eclipse.jface.resource.LocalResourceManager;\r
-import org.eclipse.jface.window.Window;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Activator;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Messages;\r
-import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;\r
-import org.eclipse.linuxtools.tmf.core.component.TmfComponent;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;\r
-import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;\r
-import org.eclipse.linuxtools.tmf.core.event.TmfEventField;\r
-import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;\r
-import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;\r
-import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;\r
-import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;\r
-import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentUpdatedSignal;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;\r
-import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;\r
-import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;\r
-import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;\r
-import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;\r
-import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.SashForm;\r
-import org.eclipse.swt.custom.TableEditor;\r
-import org.eclipse.swt.events.FocusAdapter;\r
-import org.eclipse.swt.events.FocusEvent;\r
-import org.eclipse.swt.events.KeyAdapter;\r
-import org.eclipse.swt.events.KeyEvent;\r
-import org.eclipse.swt.events.MouseAdapter;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Font;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.layout.GridData;\r
-import org.eclipse.swt.layout.GridLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Label;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.swt.widgets.Menu;\r
-import org.eclipse.swt.widgets.MessageBox;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.TableItem;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.eclipse.ui.ide.IGotoMarker;\r
-import org.eclipse.ui.themes.ColorUtil;\r
-\r
-/**\r
- * The generic TMF Events table\r
- *\r
- * This is a view that will list events that are read from a trace.\r
- *\r
- * @version 1.0\r
- * @author Francois Chouinard\r
- * @author Patrick Tasse\r
- */\r
-public class TmfEventsTable extends TmfComponent implements IGotoMarker,\r
- IColorSettingsListener, ITmfEventsFilterProvider {\r
-\r
- private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(\r
- "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$\r
- private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$\r
- private static final Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(\r
- "icons/elcl16/search_match.gif"); //$NON-NLS-1$\r
- private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(\r
- "icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$\r
- private static final Image FILTER_IMAGE = Activator.getDefault()\r
- .getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$\r
- private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$\r
- private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;\r
- private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;\r
- private static final int MAX_CACHE_SIZE = 1000;\r
-\r
- /**\r
- * The events table search/filter keys\r
- *\r
- * @version 1.0\r
- * @author Patrick Tasse\r
- */\r
- public interface Key {\r
- /** Search text */\r
- String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$\r
-\r
- /** Search object */\r
- String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$\r
-\r
- /** Filter text */\r
- String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$\r
-\r
- /** Filter object */\r
- String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$\r
-\r
- /** Timestamp*/\r
- String TIMESTAMP = "$time"; //$NON-NLS-1$\r
-\r
- /** Rank */\r
- String RANK = "$rank"; //$NON-NLS-1$\r
-\r
- /** Field ID */\r
- String FIELD_ID = "$field_id"; //$NON-NLS-1$\r
-\r
- /** Bookmark indicator */\r
- String BOOKMARK = "$bookmark"; //$NON-NLS-1$\r
- }\r
-\r
- /**\r
- * The events table search/filter state\r
- *\r
- * @version 1.0\r
- * @author Patrick Tasse\r
- */\r
- public static enum HeaderState {\r
- /** A search is being run */\r
- SEARCH,\r
-\r
- /** A filter is applied */\r
- FILTER\r
- }\r
-\r
- interface Direction {\r
- int FORWARD = +1;\r
- int BACKWARD = -1;\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Table data\r
- // ------------------------------------------------------------------------\r
-\r
- protected Composite fComposite;\r
- protected SashForm fSashForm;\r
- protected TmfVirtualTable fTable;\r
- protected TmfRawEventViewer fRawViewer;\r
- protected ITmfTrace<?> fTrace;\r
- protected boolean fPackDone = false;\r
- protected HeaderState fHeaderState = HeaderState.SEARCH;\r
- protected long fSelectedRank = 0;\r
-\r
- // Filter data\r
- protected long fFilterMatchCount;\r
- protected long fFilterCheckCount;\r
- protected FilterThread fFilterThread;\r
- protected boolean fFilterThreadResume = false;\r
- protected final Object fFilterSyncObj = new Object();\r
- protected SearchThread fSearchThread;\r
- protected final Object fSearchSyncObj = new Object();\r
- protected List<ITmfEventsFilterListener> fEventsFilterListeners = new ArrayList<ITmfEventsFilterListener>();\r
-\r
- // Bookmark map <Rank, MarkerId>\r
- protected Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();\r
- protected IFile fBookmarksFile;\r
- protected long fPendingGotoRank = -1;\r
-\r
- // SWT resources\r
- protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());\r
- protected Color fGrayColor;\r
- protected Color fGreenColor;\r
- protected Font fBoldFont;\r
-\r
- // Table column names\r
- static private final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,\r
- Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,\r
- Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };\r
-\r
- static private final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),\r
- new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),\r
- new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };\r
-\r
- // Event cache\r
- private final TmfEventsCache fCache;\r
- private boolean fCacheUpdateBusy = false;\r
- private boolean fCacheUpdatePending = false;\r
- private boolean fCacheUpdateCompleted = false;\r
- private final Object fCacheUpdateSyncObj = new Object();\r
-\r
- private boolean fDisposeOnClose;\r
-\r
- // ------------------------------------------------------------------------\r
- // Constructor\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Basic constructor, will use default column data.\r
- *\r
- * @param parent\r
- * The parent composite UI object\r
- * @param cacheSize\r
- * The size of the event table cache\r
- */\r
- public TmfEventsTable(final Composite parent, final int cacheSize) {\r
- this(parent, cacheSize, COLUMN_DATA);\r
- }\r
-\r
- /**\r
- * Advanced constructor, where we also define which column data to use.\r
- *\r
- * @param parent\r
- * The parent composite UI object\r
- * @param cacheSize\r
- * The size of the event table cache\r
- * @param columnData\r
- * The column data to use for this table\r
- */\r
- public TmfEventsTable(final Composite parent, int cacheSize, final ColumnData[] columnData) {\r
- super("TmfEventsTable"); //$NON-NLS-1$\r
-\r
- fComposite = new Composite(parent, SWT.NONE);\r
- final GridLayout gl = new GridLayout(1, false);\r
- gl.marginHeight = 0;\r
- gl.marginWidth = 0;\r
- gl.verticalSpacing = 0;\r
- fComposite.setLayout(gl);\r
-\r
- fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);\r
- fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
-\r
- // Create a virtual table\r
- final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;\r
- fTable = new TmfVirtualTable(fSashForm, style);\r
-\r
- // Set the table layout\r
- final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);\r
- fTable.setLayoutData(layoutData);\r
-\r
- // Some cosmetic enhancements\r
- fTable.setHeaderVisible(true);\r
- fTable.setLinesVisible(true);\r
-\r
- // Set the columns\r
- setColumnHeaders(columnData);\r
-\r
- // Set the default column field ids if this is not a subclass\r
- if (Arrays.equals(columnData, COLUMN_DATA)) {\r
- fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);\r
- fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);\r
- fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);\r
- fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);\r
- fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);\r
- }\r
-\r
- // Set the frozen row for header row\r
- fTable.setFrozenRowCount(1);\r
-\r
- // Create the header row cell editor\r
- createHeaderEditor();\r
-\r
- // Handle the table item selection\r
- fTable.addSelectionListener(new SelectionAdapter() {\r
- @Override\r
- public void widgetSelected(final SelectionEvent e) {\r
- final TableItem[] selection = fTable.getSelection();\r
- if (selection.length > 0) {\r
- final TableItem selectedTableItem = selection[0];\r
- if (selectedTableItem != null) {\r
- if (selectedTableItem.getData(Key.RANK) instanceof Long) {\r
- fSelectedRank = (Long) selectedTableItem.getData(Key.RANK);\r
- fRawViewer.selectAndReveal((Long) selectedTableItem.getData(Key.RANK));\r
- }\r
- if (selectedTableItem.getData(Key.TIMESTAMP) instanceof TmfTimestamp) {\r
- final TmfTimestamp ts = (TmfTimestamp) selectedTableItem.getData(Key.TIMESTAMP);\r
- broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));\r
- }\r
- }\r
- }\r
- }\r
- });\r
-\r
- cacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());\r
- cacheSize = Math.min(cacheSize, MAX_CACHE_SIZE);\r
- fCache = new TmfEventsCache(cacheSize, this);\r
-\r
- // Handle the table item requests\r
- fTable.addListener(SWT.SetData, new Listener() {\r
-\r
- @Override\r
- public void handleEvent(final Event event) {\r
-\r
- final TableItem item = (TableItem) event.item;\r
- int index = event.index - 1; // -1 for the header row\r
-\r
- if (event.index == 0) {\r
- setHeaderRowItemData(item);\r
- return;\r
- }\r
-\r
- if (fTable.getData(Key.FILTER_OBJ) != null) {\r
- if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {\r
- setFilterStatusRowItemData(item);\r
- return;\r
- }\r
- index = index - 1; // -1 for top filter status row\r
- }\r
-\r
- final CachedEvent cachedEvent = fCache.getEvent(index);\r
- if (cachedEvent != null) {\r
- setItemData(item, cachedEvent.event, cachedEvent.rank);\r
- return;\r
- }\r
-\r
- // Else, fill the cache asynchronously (and off the UI thread)\r
- event.doit = false;\r
- }\r
- });\r
-\r
- fTable.addMouseListener(new MouseAdapter() {\r
- @Override\r
- public void mouseDoubleClick(final MouseEvent event) {\r
- if (event.button != 1) {\r
- return;\r
- }\r
- // Identify the selected row\r
- final Point point = new Point(event.x, event.y);\r
- final TableItem item = fTable.getItem(point);\r
- if (item != null) {\r
- final Rectangle imageBounds = item.getImageBounds(0);\r
- imageBounds.width = BOOKMARK_IMAGE.getBounds().width;\r
- if (imageBounds.contains(point)) {\r
- final Long rank = (Long) item.getData(Key.RANK);\r
- if (rank != null) {\r
- toggleBookmark(rank);\r
- }\r
- }\r
- }\r
- }\r
- });\r
-\r
- final Listener tooltipListener = new Listener () {\r
- Shell tooltipShell = null;\r
- @Override\r
- public void handleEvent(final Event event) {\r
- switch (event.type) {\r
- case SWT.MouseHover:\r
- final TableItem item = fTable.getItem(new Point(event.x, event.y));\r
- if (item == null) {\r
- return;\r
- }\r
- final Long rank = (Long) item.getData(Key.RANK);\r
- if (rank == null) {\r
- return;\r
- }\r
- final String tooltipText = (String) item.getData(Key.BOOKMARK);\r
- final Rectangle bounds = item.getImageBounds(0);\r
- bounds.width = BOOKMARK_IMAGE.getBounds().width;\r
- if (!bounds.contains(event.x,event.y)) {\r
- return;\r
- }\r
- if ((tooltipShell != null) && !tooltipShell.isDisposed()) {\r
- tooltipShell.dispose();\r
- }\r
- tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);\r
- tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
- final FillLayout layout = new FillLayout();\r
- layout.marginWidth = 2;\r
- tooltipShell.setLayout(layout);\r
- final Label label = new Label(tooltipShell, SWT.WRAP);\r
- String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$\r
- label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));\r
- label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
- label.setText(text);\r
- label.addListener(SWT.MouseExit, this);\r
- label.addListener(SWT.MouseDown, this);\r
- label.addListener(SWT.MouseWheel, this);\r
- final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);\r
- /*\r
- * Bug in Linux. The coordinates of the event have an origin that excludes the table header but\r
- * the method toDisplay() expects coordinates relative to an origin that includes the table header.\r
- */\r
- int y = event.y;\r
- if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$\r
- y += fTable.getHeaderHeight();\r
- }\r
- Point pt = fTable.toDisplay(event.x, y);\r
- pt.x += BOOKMARK_IMAGE.getBounds().width;\r
- pt.y += size.y;\r
- tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);\r
- tooltipShell.setVisible(true);\r
- break;\r
- case SWT.Dispose:\r
- case SWT.KeyDown:\r
- case SWT.MouseMove:\r
- case SWT.MouseExit:\r
- case SWT.MouseDown:\r
- case SWT.MouseWheel:\r
- if (tooltipShell != null) {\r
- tooltipShell.dispose();\r
- tooltipShell = null;\r
- }\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- };\r
-\r
- fTable.addListener(SWT.MouseHover, tooltipListener);\r
- fTable.addListener(SWT.Dispose, tooltipListener);\r
- fTable.addListener(SWT.KeyDown, tooltipListener);\r
- fTable.addListener(SWT.MouseMove, tooltipListener);\r
- fTable.addListener(SWT.MouseExit, tooltipListener);\r
- fTable.addListener(SWT.MouseDown, tooltipListener);\r
- fTable.addListener(SWT.MouseWheel, tooltipListener);\r
-\r
- // Create resources\r
- createResources();\r
-\r
- ColorSettingsManager.addColorSettingsListener(this);\r
-\r
- fTable.setItemCount(1); // +1 for header row\r
-\r
- fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);\r
-\r
- fRawViewer.addSelectionListener(new Listener() {\r
- @Override\r
- public void handleEvent(final Event e) {\r
- if (e.data instanceof Long) {\r
- final long rank = (Long) e.data;\r
- int index = (int) rank;\r
- if (fTable.getData(Key.FILTER_OBJ) != null) {\r
- index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
- }\r
- fTable.setSelection(index + 1); // +1 for header row\r
- fSelectedRank = rank;\r
- } else if (e.data instanceof ITmfLocation<?>) {\r
- // DOES NOT WORK: rank undefined in context from seekLocation()\r
- // ITmfLocation<?> location = (ITmfLocation<?>) e.data;\r
- // TmfContext context = fTrace.seekLocation(location);\r
- // fTable.setSelection((int) context.getRank());\r
- return;\r
- } else {\r
- return;\r
- }\r
- final TableItem[] selection = fTable.getSelection();\r
- if ((selection != null) && (selection.length > 0)) {\r
- final TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData(Key.TIMESTAMP);\r
- if (ts != null) {\r
- broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));\r
- }\r
- }\r
- }\r
- });\r
-\r
- fSashForm.setWeights(new int[] { 1, 1 });\r
- fRawViewer.setVisible(false);\r
-\r
- createPopupMenu();\r
- }\r
-\r
- protected void createPopupMenu() {\r
- final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {\r
- @Override\r
- public void run() {\r
- fTable.setVisible(true);\r
- fSashForm.layout();\r
- }\r
- };\r
-\r
- final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {\r
- @Override\r
- public void run() {\r
- fTable.setVisible(false);\r
- fSashForm.layout();\r
- }\r
- };\r
-\r
- final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {\r
- @Override\r
- public void run() {\r
- fRawViewer.setVisible(true);\r
- fSashForm.layout();\r
- final int index = fTable.getSelectionIndex();\r
- if (index >= +1) {\r
- fRawViewer.selectAndReveal(index - 1);\r
- }\r
- }\r
- };\r
-\r
- final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {\r
- @Override\r
- public void run() {\r
- fRawViewer.setVisible(false);\r
- fSashForm.layout();\r
- }\r
- };\r
-\r
- final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {\r
- @Override\r
- public void run() {\r
- fHeaderState = HeaderState.SEARCH;\r
- fTable.refresh();\r
- }\r
- };\r
-\r
- final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {\r
- @Override\r
- public void run() {\r
- fHeaderState = HeaderState.FILTER;\r
- fTable.refresh();\r
- }\r
- };\r
-\r
- final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {\r
- @Override\r
- public void run() {\r
- clearFilters();\r
- }\r
- };\r
-\r
- class ToggleBookmarkAction extends Action {\r
- long fRank;\r
-\r
- public ToggleBookmarkAction(final String text, final long rank) {\r
- super(text);\r
- fRank = rank;\r
- }\r
-\r
- @Override\r
- public void run() {\r
- toggleBookmark(fRank);\r
- }\r
- }\r
-\r
- final MenuManager tablePopupMenu = new MenuManager();\r
- tablePopupMenu.setRemoveAllWhenShown(true);\r
- tablePopupMenu.addMenuListener(new IMenuListener() {\r
- @Override\r
- public void menuAboutToShow(final IMenuManager manager) {\r
- if (fTable.getSelectionIndex() == 0) {\r
- // Right-click on header row\r
- if (fHeaderState == HeaderState.FILTER) {\r
- tablePopupMenu.add(showSearchBarAction);\r
- } else {\r
- tablePopupMenu.add(showFilterBarAction);\r
- }\r
- return;\r
- }\r
- final Point point = fTable.toControl(Display.getDefault().getCursorLocation());\r
- final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;\r
- if (item != null) {\r
- final Rectangle imageBounds = item.getImageBounds(0);\r
- imageBounds.width = BOOKMARK_IMAGE.getBounds().width;\r
- if (point.x <= (imageBounds.x + imageBounds.width)) {\r
- // Right-click on left margin\r
- final Long rank = (Long) item.getData(Key.RANK);\r
- if ((rank != null) && (fBookmarksFile != null)) {\r
- if (fBookmarksMap.containsKey(rank)) {\r
- tablePopupMenu.add(new ToggleBookmarkAction(\r
- Messages.TmfEventsTable_RemoveBookmarkActionText, rank));\r
- } else {\r
- tablePopupMenu.add(new ToggleBookmarkAction(\r
- Messages.TmfEventsTable_AddBookmarkActionText, rank));\r
- }\r
- }\r
- return;\r
- }\r
- }\r
- // Right-click on table\r
- if (fTable.isVisible() && fRawViewer.isVisible()) {\r
- tablePopupMenu.add(hideTableAction);\r
- tablePopupMenu.add(hideRawAction);\r
- } else if (!fTable.isVisible()) {\r
- tablePopupMenu.add(showTableAction);\r
- } else if (!fRawViewer.isVisible()) {\r
- tablePopupMenu.add(showRawAction);\r
- }\r
- tablePopupMenu.add(new Separator());\r
- tablePopupMenu.add(clearFiltersAction);\r
- final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();\r
- if (savedFilters.length > 0) {\r
- final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);\r
- for (final ITmfFilterTreeNode node : savedFilters) {\r
- if (node instanceof TmfFilterNode) {\r
- final TmfFilterNode filter = (TmfFilterNode) node;\r
- subMenu.add(new Action(filter.getFilterName()) {\r
- @Override\r
- public void run() {\r
- applyFilter(filter);\r
- }\r
- });\r
- }\r
- }\r
- tablePopupMenu.add(subMenu);\r
- }\r
- appendToTablePopupMenu(tablePopupMenu, item);\r
- }\r
- });\r
-\r
- final MenuManager rawViewerPopupMenu = new MenuManager();\r
- rawViewerPopupMenu.setRemoveAllWhenShown(true);\r
- rawViewerPopupMenu.addMenuListener(new IMenuListener() {\r
- @Override\r
- public void menuAboutToShow(final IMenuManager manager) {\r
- if (fTable.isVisible() && fRawViewer.isVisible()) {\r
- rawViewerPopupMenu.add(hideTableAction);\r
- rawViewerPopupMenu.add(hideRawAction);\r
- } else if (!fTable.isVisible()) {\r
- rawViewerPopupMenu.add(showTableAction);\r
- } else if (!fRawViewer.isVisible()) {\r
- rawViewerPopupMenu.add(showRawAction);\r
- }\r
- appendToRawPopupMenu(tablePopupMenu);\r
- }\r
- });\r
-\r
- Menu menu = tablePopupMenu.createContextMenu(fTable);\r
- fTable.setMenu(menu);\r
-\r
- menu = rawViewerPopupMenu.createContextMenu(fRawViewer);\r
- fRawViewer.setMenu(menu);\r
- }\r
-\r
- protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {\r
- // override to append more actions\r
- }\r
-\r
- protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {\r
- // override to append more actions\r
- }\r
-\r
- @Override\r
- public void dispose() {\r
- stopSearchThread();\r
- stopFilterThread();\r
- ColorSettingsManager.removeColorSettingsListener(this);\r
- fComposite.dispose();\r
- if ((fTrace != null) && fDisposeOnClose) {\r
- fTrace.dispose();\r
- }\r
- fResourceManager.dispose();\r
- super.dispose();\r
- }\r
-\r
- /**\r
- * Assign a layout data object to this view.\r
- *\r
- * @param layoutData\r
- * The layout data to assign\r
- */\r
- public void setLayoutData(final Object layoutData) {\r
- fComposite.setLayoutData(layoutData);\r
- }\r
-\r
- /**\r
- * Get the virtual table contained in this event table.\r
- *\r
- * @return The TMF virtual table\r
- */\r
- public TmfVirtualTable getTable() {\r
- return fTable;\r
- }\r
-\r
- /**\r
- * @param columnData\r
- *\r
- * FIXME: Add support for column selection\r
- */\r
- protected void setColumnHeaders(final ColumnData[] columnData) {\r
- fTable.setColumnHeaders(columnData);\r
- }\r
-\r
- protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {\r
- final ITmfEventField[] fields = extractItemFields(event);\r
- final String[] content = new String[fields.length];\r
- for (int i = 0; i < fields.length; i++) {\r
- content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$\r
- }\r
- item.setText(content);\r
- item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));\r
- item.setData(Key.RANK, rank);\r
-\r
- boolean bookmark = false;\r
- final Long markerId = fBookmarksMap.get(rank);\r
- if (markerId != null) {\r
- bookmark = true;\r
- try {\r
- final IMarker marker = fBookmarksFile.findMarker(markerId);\r
- item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));\r
- } catch (final CoreException e) {\r
- displayException(e);\r
- }\r
- } else {\r
- item.setData(Key.BOOKMARK, null);\r
- }\r
-\r
- boolean searchMatch = false;\r
- boolean searchNoMatch = false;\r
- final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);\r
- if (searchFilter != null) {\r
- if (searchFilter.matches(event)) {\r
- searchMatch = true;\r
- } else {\r
- searchNoMatch = true;\r
- }\r
- }\r
-\r
- final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);\r
- if (searchNoMatch) {\r
- item.setForeground(colorSetting.getDimmedForegroundColor());\r
- item.setBackground(colorSetting.getDimmedBackgroundColor());\r
- } else {\r
- item.setForeground(colorSetting.getForegroundColor());\r
- item.setBackground(colorSetting.getBackgroundColor());\r
- }\r
-\r
- if (searchMatch) {\r
- if (bookmark) {\r
- item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);\r
- } else {\r
- item.setImage(SEARCH_MATCH_IMAGE);\r
- }\r
- } else if (bookmark) {\r
- item.setImage(BOOKMARK_IMAGE);\r
- } else {\r
- item.setImage((Image) null);\r
- }\r
- }\r
-\r
- protected void setHeaderRowItemData(final TableItem item) {\r
- String txtKey = null;\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- item.setImage(SEARCH_IMAGE);\r
- txtKey = Key.SEARCH_TXT;\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- item.setImage(FILTER_IMAGE);\r
- txtKey = Key.FILTER_TXT;\r
- }\r
- item.setForeground(fGrayColor);\r
- for (int i = 0; i < fTable.getColumns().length; i++) {\r
- final TableColumn column = fTable.getColumns()[i];\r
- final String filter = (String) column.getData(txtKey);\r
- if (filter == null) {\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- item.setText(i, SEARCH_HINT);\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- item.setText(i, FILTER_HINT);\r
- }\r
- item.setForeground(i, fGrayColor);\r
- item.setFont(i, fTable.getFont());\r
- } else {\r
- item.setText(i, filter);\r
- item.setForeground(i, fGreenColor);\r
- item.setFont(i, fBoldFont);\r
- }\r
- }\r
- }\r
-\r
- protected void setFilterStatusRowItemData(final TableItem item) {\r
- for (int i = 0; i < fTable.getColumns().length; i++) {\r
- if (i == 0) {\r
- if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {\r
- item.setImage(FILTER_IMAGE);\r
- } else {\r
- item.setImage(STOP_IMAGE);\r
- }\r
- item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$\r
- } else {\r
- item.setText(i, ""); //$NON-NLS-1$\r
- }\r
- }\r
- item.setData(Key.TIMESTAMP, null);\r
- item.setData(Key.RANK, null);\r
- item.setForeground(null);\r
- item.setBackground(null);\r
- }\r
-\r
- protected void createHeaderEditor() {\r
- final TableEditor tableEditor = fTable.createTableEditor();\r
- tableEditor.horizontalAlignment = SWT.LEFT;\r
- tableEditor.verticalAlignment = SWT.CENTER;\r
- tableEditor.grabHorizontal = true;\r
- tableEditor.minimumWidth = 50;\r
-\r
- // Handle the header row selection\r
- fTable.addMouseListener(new MouseAdapter() {\r
- int columnIndex;\r
- TableColumn column;\r
- TableItem item;\r
-\r
- @Override\r
- public void mouseDown(final MouseEvent event) {\r
- if (event.button != 1) {\r
- return;\r
- }\r
- // Identify the selected row\r
- final Point point = new Point(event.x, event.y);\r
- item = fTable.getItem(point);\r
-\r
- // Header row selected\r
- if ((item != null) && (fTable.indexOf(item) == 0)) {\r
-\r
- // Icon selected\r
- if (item.getImageBounds(0).contains(point)) {\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- fHeaderState = HeaderState.FILTER;\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- fHeaderState = HeaderState.SEARCH;\r
- }\r
- fTable.refresh();\r
- return;\r
- }\r
-\r
- // Identify the selected column\r
- columnIndex = -1;\r
- for (int i = 0; i < fTable.getColumns().length; i++) {\r
- final Rectangle rect = item.getBounds(i);\r
- if (rect.contains(point)) {\r
- columnIndex = i;\r
- break;\r
- }\r
- }\r
-\r
- if (columnIndex == -1) {\r
- return;\r
- }\r
-\r
- column = fTable.getColumns()[columnIndex];\r
-\r
- String txtKey = null;\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- txtKey = Key.SEARCH_TXT;\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- txtKey = Key.FILTER_TXT;\r
- }\r
-\r
- // The control that will be the editor must be a child of the Table\r
- final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);\r
- final String headerString = (String) column.getData(txtKey);\r
- if (headerString != null) {\r
- newEditor.setText(headerString);\r
- }\r
- newEditor.addFocusListener(new FocusAdapter() {\r
- @Override\r
- public void focusLost(final FocusEvent e) {\r
- final boolean changed = updateHeader(newEditor.getText());\r
- if (changed) {\r
- applyHeader();\r
- }\r
- }\r
- });\r
- newEditor.addKeyListener(new KeyAdapter() {\r
- @Override\r
- public void keyPressed(final KeyEvent e) {\r
- if (e.character == SWT.CR) {\r
- updateHeader(newEditor.getText());\r
- applyHeader();\r
- } else if (e.character == SWT.ESC) {\r
- tableEditor.getEditor().dispose();\r
- }\r
- }\r
- });\r
- newEditor.selectAll();\r
- newEditor.setFocus();\r
- tableEditor.setEditor(newEditor, item, columnIndex);\r
- }\r
- }\r
-\r
- /*\r
- * returns true is value was changed\r
- */\r
- private boolean updateHeader(final String text) {\r
- String objKey = null;\r
- String txtKey = null;\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- objKey = Key.SEARCH_OBJ;\r
- txtKey = Key.SEARCH_TXT;\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- objKey = Key.FILTER_OBJ;\r
- txtKey = Key.FILTER_TXT;\r
- }\r
- if (text.trim().length() > 0) {\r
- try {\r
- final String regex = TmfFilterMatchesNode.regexFix(text);\r
- Pattern.compile(regex);\r
- if (regex.equals(column.getData(txtKey))) {\r
- tableEditor.getEditor().dispose();\r
- return false;\r
- }\r
- final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);\r
- String fieldId = (String) column.getData(Key.FIELD_ID);\r
- if (fieldId == null) {\r
- fieldId = column.getText();\r
- }\r
- filter.setField(fieldId);\r
- filter.setRegex(regex);\r
- column.setData(objKey, filter);\r
- column.setData(txtKey, regex);\r
- } catch (final PatternSyntaxException ex) {\r
- tableEditor.getEditor().dispose();\r
- MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
- ex.getDescription(), ex.getMessage());\r
- return false;\r
- }\r
- } else {\r
- if (column.getData(txtKey) == null) {\r
- tableEditor.getEditor().dispose();\r
- return false;\r
- }\r
- column.setData(objKey, null);\r
- column.setData(txtKey, null);\r
- }\r
- return true;\r
- }\r
-\r
- private void applyHeader() {\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- stopSearchThread();\r
- final TmfFilterAndNode filter = new TmfFilterAndNode(null);\r
- for (final TableColumn column : fTable.getColumns()) {\r
- final Object filterObj = column.getData(Key.SEARCH_OBJ);\r
- if (filterObj instanceof ITmfFilterTreeNode) {\r
- filter.addChild((ITmfFilterTreeNode) filterObj);\r
- }\r
- }\r
- if (filter.getChildrenCount() > 0) {\r
- fTable.setData(Key.SEARCH_OBJ, filter);\r
- fTable.refresh();\r
- searchNext();\r
- fireSearchApplied(filter);\r
- } else {\r
- fTable.setData(Key.SEARCH_OBJ, null);\r
- fTable.refresh();\r
- fireSearchApplied(null);\r
- }\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- final TmfFilterAndNode filter = new TmfFilterAndNode(null);\r
- for (final TableColumn column : fTable.getColumns()) {\r
- final Object filterObj = column.getData(Key.FILTER_OBJ);\r
- if (filterObj instanceof ITmfFilterTreeNode) {\r
- filter.addChild((ITmfFilterTreeNode) filterObj);\r
- }\r
- }\r
- if (filter.getChildrenCount() > 0) {\r
- applyFilter(filter);\r
- } else {\r
- clearFilters();\r
- }\r
- }\r
-\r
- tableEditor.getEditor().dispose();\r
- }\r
- });\r
-\r
- fTable.addKeyListener(new KeyAdapter() {\r
- @Override\r
- public void keyPressed(final KeyEvent e) {\r
- e.doit = false;\r
- if (e.character == SWT.ESC) {\r
- stopFilterThread();\r
- stopSearchThread();\r
- fTable.refresh();\r
- } else if (e.character == SWT.DEL) {\r
- if (fHeaderState == HeaderState.SEARCH) {\r
- stopSearchThread();\r
- for (final TableColumn column : fTable.getColumns()) {\r
- column.setData(Key.SEARCH_OBJ, null);\r
- column.setData(Key.SEARCH_TXT, null);\r
- }\r
- fTable.setData(Key.SEARCH_OBJ, null);\r
- fTable.refresh();\r
- fireSearchApplied(null);\r
- } else if (fHeaderState == HeaderState.FILTER) {\r
- clearFilters();\r
- }\r
- } else if (e.character == SWT.CR) {\r
- if ((e.stateMask & SWT.SHIFT) == 0) {\r
- searchNext();\r
- } else {\r
- searchPrevious();\r
- }\r
- }\r
- }\r
- });\r
- }\r
-\r
- protected void fireFilterApplied(final ITmfFilter filter) {\r
- for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {\r
- listener.filterApplied(filter, fTrace);\r
- }\r
- }\r
-\r
- protected void fireSearchApplied(final ITmfFilter filter) {\r
- for (final ITmfEventsFilterListener listener : fEventsFilterListeners) {\r
- listener.searchApplied(filter, fTrace);\r
- }\r
- }\r
-\r
- protected void startFilterThread() {\r
- synchronized (fFilterSyncObj) {\r
- final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
- if (fFilterThread == null || fFilterThread.filter != filter) {\r
- if (fFilterThread != null) {\r
- fFilterThread.cancel();\r
- fFilterThreadResume = false;\r
- }\r
- fFilterThread = new FilterThread(filter);\r
- fFilterThread.start();\r
- } else {\r
- fFilterThreadResume = true;\r
- }\r
- }\r
- }\r
-\r
- protected void stopFilterThread() {\r
- synchronized (fFilterSyncObj) {\r
- if (fFilterThread != null) {\r
- fFilterThread.cancel();\r
- fFilterThread = null;\r
- fFilterThreadResume = false;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * @since 1.1\r
- */\r
- protected void applyFilter(ITmfFilter filter) {\r
- stopFilterThread();\r
- stopSearchThread();\r
- fFilterMatchCount = 0;\r
- fFilterCheckCount = 0;\r
- fCache.applyFilter(filter);\r
- fTable.clearAll();\r
- fTable.setData(Key.FILTER_OBJ, filter);\r
- fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows\r
- startFilterThread();\r
- fireFilterApplied(filter);\r
- }\r
-\r
- protected void clearFilters() {\r
- if (fTable.getData(Key.FILTER_OBJ) == null) {\r
- return;\r
- }\r
- stopFilterThread();\r
- stopSearchThread();\r
- fCache.clearFilter();\r
- fTable.clearAll();\r
- for (final TableColumn column : fTable.getColumns()) {\r
- column.setData(Key.FILTER_OBJ, null);\r
- column.setData(Key.FILTER_TXT, null);\r
- }\r
- fTable.setData(Key.FILTER_OBJ, null);\r
- if (fTrace != null) {\r
- fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
- } else {\r
- fTable.setItemCount(1); // +1 for header row\r
- }\r
- fFilterMatchCount = 0;\r
- fFilterCheckCount = 0;\r
- if (fSelectedRank >= 0) {\r
- fTable.setSelection((int) fSelectedRank + 1); // +1 for header row\r
- } else {\r
- fTable.setSelection(0);\r
- }\r
- fireFilterApplied(null);\r
- }\r
-\r
- protected class FilterThread extends Thread {\r
- private final ITmfFilterTreeNode filter;\r
- private TmfDataRequest<ITmfEvent> request;\r
- private boolean refreshBusy = false;\r
- private boolean refreshPending = false;\r
- private final Object syncObj = new Object();\r
-\r
- public FilterThread(final ITmfFilterTreeNode filter) {\r
- super("Filter Thread"); //$NON-NLS-1$\r
- this.filter = filter;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- @Override\r
- public void run() {\r
- if (fTrace == null) {\r
- return;\r
- }\r
- final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);\r
- if (nbRequested <= 0) {\r
- return;\r
- }\r
- request = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, (int) fFilterCheckCount,\r
- nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {\r
- @Override\r
- public void handleData(final ITmfEvent event) {\r
- super.handleData(event);\r
- if (request.isCancelled()) {\r
- return;\r
- }\r
- if (filter.matches(event)) {\r
- final long rank = fFilterCheckCount;\r
- final int index = (int) fFilterMatchCount;\r
- fFilterMatchCount++;\r
- fCache.storeEvent(event.clone(), rank, index);\r
- refreshTable();\r
- } else if ((fFilterCheckCount % 100) == 0) {\r
- refreshTable();\r
- }\r
- fFilterCheckCount++;\r
- }\r
- };\r
- ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(request);\r
- try {\r
- request.waitForCompletion();\r
- } catch (final InterruptedException e) {\r
- }\r
- refreshTable();\r
- synchronized (fFilterSyncObj) {\r
- fFilterThread = null;\r
- if (fFilterThreadResume) {\r
- fFilterThreadResume = false;\r
- fFilterThread = new FilterThread(filter);\r
- fFilterThread.start();\r
- }\r
- }\r
- }\r
-\r
- public void refreshTable() {\r
- synchronized (syncObj) {\r
- if (refreshBusy) {\r
- refreshPending = true;\r
- return;\r
- }\r
- refreshBusy = true;\r
- }\r
- Display.getDefault().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (request.isCancelled()) {\r
- return;\r
- }\r
- if (fTable.isDisposed()) {\r
- return;\r
- }\r
- fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows\r
- fTable.refresh();\r
- synchronized (syncObj) {\r
- refreshBusy = false;\r
- if (refreshPending) {\r
- refreshPending = false;\r
- refreshTable();\r
- }\r
- }\r
- }\r
- });\r
- }\r
-\r
- public void cancel() {\r
- if (request != null) {\r
- request.cancel();\r
- }\r
- }\r
- }\r
-\r
- protected void searchNext() {\r
- synchronized (fSearchSyncObj) {\r
- if (fSearchThread != null) {\r
- return;\r
- }\r
- final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);\r
- if (searchFilter == null) {\r
- return;\r
- }\r
- final int selectionIndex = fTable.getSelectionIndex();\r
- int startIndex;\r
- if (selectionIndex > 0) {\r
- startIndex = selectionIndex; // -1 for header row, +1 for next event\r
- } else {\r
- // header row is selected, start at top event\r
- startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row\r
- }\r
- final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
- if (eventFilter != null)\r
- {\r
- startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row\r
- }\r
- fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);\r
- fSearchThread.schedule();\r
- }\r
- }\r
-\r
- protected void searchPrevious() {\r
- synchronized (fSearchSyncObj) {\r
- if (fSearchThread != null) {\r
- return;\r
- }\r
- final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);\r
- if (searchFilter == null) {\r
- return;\r
- }\r
- final int selectionIndex = fTable.getSelectionIndex();\r
- int startIndex;\r
- if (selectionIndex > 0) {\r
- startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event\r
- } else {\r
- // header row is selected, start at precedent of top event\r
- startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event\r
- }\r
- final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);\r
- if (eventFilter != null)\r
- {\r
- startIndex = startIndex - 1; // -1 for top filter status row\r
- }\r
- fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);\r
- fSearchThread.schedule();\r
- }\r
- }\r
-\r
- protected void stopSearchThread() {\r
- fPendingGotoRank = -1;\r
- synchronized (fSearchSyncObj) {\r
- if (fSearchThread != null) {\r
- fSearchThread.cancel();\r
- fSearchThread = null;\r
- }\r
- }\r
- }\r
-\r
- protected class SearchThread extends Job {\r
- protected ITmfFilterTreeNode searchFilter;\r
- protected ITmfFilterTreeNode eventFilter;\r
- protected int startIndex;\r
- protected int direction;\r
- protected long rank;\r
- protected long foundRank = -1;\r
- protected TmfDataRequest<ITmfEvent> request;\r
-\r
- public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,\r
- final long currentRank, final int direction) {\r
- super(Messages.TmfEventsTable_SearchingJobName);\r
- this.searchFilter = searchFilter;\r
- this.eventFilter = eventFilter;\r
- this.startIndex = startIndex;\r
- this.rank = currentRank;\r
- this.direction = direction;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- @Override\r
- protected IStatus run(final IProgressMonitor monitor) {\r
- if (fTrace == null) {\r
- return Status.OK_STATUS;\r
- }\r
- final Display display = Display.getDefault();\r
- if (startIndex < 0) {\r
- rank = (int) fTrace.getNbEvents() - 1;\r
- } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows\r
- rank = 0;\r
- } else {\r
- int idx = startIndex;\r
- while (foundRank == -1) {\r
- final CachedEvent event = fCache.peekEvent(idx);\r
- if (event == null) {\r
- break;\r
- }\r
- rank = event.rank;\r
- if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {\r
- foundRank = event.rank;\r
- break;\r
- }\r
- if (direction == Direction.FORWARD) {\r
- idx++;\r
- } else {\r
- idx--;\r
- }\r
- }\r
- if (foundRank == -1) {\r
- if (direction == Direction.FORWARD) {\r
- rank++;\r
- if (rank > (fTrace.getNbEvents() - 1)) {\r
- rank = 0;\r
- }\r
- } else {\r
- rank--;\r
- if (rank < 0) {\r
- rank = (int) fTrace.getNbEvents() - 1;\r
- }\r
- }\r
- }\r
- }\r
- final int startRank = (int) rank;\r
- boolean wrapped = false;\r
- while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {\r
- int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));\r
- if (direction == Direction.BACKWARD) {\r
- rank = Math.max(0, rank - fTrace.getCacheSize() + 1);\r
- }\r
- request = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {\r
- long currentRank = rank;\r
-\r
- @Override\r
- public void handleData(final ITmfEvent event) {\r
- super.handleData(event);\r
- if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {\r
- foundRank = currentRank;\r
- if (direction == Direction.FORWARD) {\r
- done();\r
- return;\r
- }\r
- }\r
- currentRank++;\r
- }\r
- };\r
- ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(request);\r
- try {\r
- request.waitForCompletion();\r
- if (request.isCancelled()) {\r
- return Status.OK_STATUS;\r
- }\r
- } catch (final InterruptedException e) {\r
- synchronized (fSearchSyncObj) {\r
- fSearchThread = null;\r
- }\r
- return Status.OK_STATUS;\r
- }\r
- if (foundRank == -1) {\r
- if (direction == Direction.FORWARD) {\r
- if (rank == 0) {\r
- synchronized (fSearchSyncObj) {\r
- fSearchThread = null;\r
- }\r
- return Status.OK_STATUS;\r
- }\r
- nbRequested = (int) rank;\r
- rank = 0;\r
- wrapped = true;\r
- } else {\r
- rank--;\r
- if (rank < 0) {\r
- rank = (int) fTrace.getNbEvents() - 1;\r
- wrapped = true;\r
- }\r
- if ((rank <= startRank) && wrapped) {\r
- synchronized (fSearchSyncObj) {\r
- fSearchThread = null;\r
- }\r
- return Status.OK_STATUS;\r
- }\r
- }\r
- }\r
- }\r
- int index = (int) foundRank;\r
- if (eventFilter != null) {\r
- index = fCache.getFilteredEventIndex(foundRank);\r
- }\r
- final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row\r
-\r
- display.asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (monitor.isCanceled()) {\r
- return;\r
- }\r
- if (fTable.isDisposed()) {\r
- return;\r
- }\r
- fTable.setSelection(selection);\r
- fSelectedRank = foundRank;\r
- synchronized (fSearchSyncObj) {\r
- fSearchThread = null;\r
- }\r
- }\r
- });\r
- return Status.OK_STATUS;\r
- }\r
-\r
- @Override\r
- protected void canceling() {\r
- request.cancel();\r
- synchronized (fSearchSyncObj) {\r
- fSearchThread = null;\r
- }\r
- }\r
- }\r
-\r
- protected void createResources() {\r
- fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable\r
- .getForeground().getRGB()));\r
- fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);\r
- fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));\r
- }\r
-\r
- protected void packColumns() {\r
- if (fPackDone) {\r
- return;\r
- }\r
- for (final TableColumn column : fTable.getColumns()) {\r
- final int headerWidth = column.getWidth();\r
- column.pack();\r
- if (column.getWidth() < headerWidth) {\r
- column.setWidth(headerWidth);\r
- }\r
- }\r
- fPackDone = true;\r
- }\r
-\r
- /**\r
- * @param event\r
- * @return\r
- *\r
- * FIXME: Add support for column selection\r
- */\r
- protected ITmfEventField[] extractItemFields(final ITmfEvent event) {\r
- ITmfEventField[] fields = new TmfEventField[0];\r
- if (event != null) {\r
- final String timestamp = event.getTimestamp().toString();\r
- final String source = event.getSource();\r
- final String type = event.getType().getName();\r
- final String reference = event.getReference();\r
- final ITmfEventField content = event.getContent();\r
- final String value = (content.getValue() != null) ? content.getValue().toString() : content.toString();\r
- fields = new TmfEventField[] {\r
- new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),\r
- new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),\r
- new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),\r
- new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),\r
- new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, value)\r
- };\r
- }\r
- return fields;\r
- }\r
-\r
- /**\r
- * Notify this table that is got the UI focus.\r
- */\r
- public void setFocus() {\r
- fTable.setFocus();\r
- }\r
-\r
- /**\r
- * Assign a new trace to this event table.\r
- *\r
- * @param trace\r
- * The trace to assign to this event table\r
- * @param disposeOnClose\r
- * true if the trace should be disposed when the table is\r
- * disposed\r
- */\r
- public void setTrace(final ITmfTrace<?> trace, final boolean disposeOnClose) {\r
- if ((fTrace != null) && fDisposeOnClose) {\r
- fTrace.dispose();\r
- }\r
- fTrace = trace;\r
- fPackDone = false;\r
- fSelectedRank = 0;\r
- fDisposeOnClose = disposeOnClose;\r
-\r
- // Perform the updates on the UI thread\r
- fTable.getDisplay().syncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- fTable.removeAll();\r
- fCache.setTrace(fTrace); // Clear the cache\r
- if (fTrace != null) {\r
- if (!fTable.isDisposed() && (fTrace != null)) {\r
- if (fTable.getData(Key.FILTER_OBJ) == null) {\r
- fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
- } else {\r
- stopFilterThread();\r
- fFilterMatchCount = 0;\r
- fFilterCheckCount = 0;\r
- fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows\r
- startFilterThread();\r
- }\r
- }\r
- fRawViewer.setTrace(fTrace);\r
- }\r
- }\r
- });\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Event cache\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Notify that the event cache has been updated\r
- *\r
- * @param completed\r
- * Also notify if the populating of the cache is complete, or\r
- * not.\r
- */\r
- public void cacheUpdated(final boolean completed) {\r
- synchronized (fCacheUpdateSyncObj) {\r
- if (fCacheUpdateBusy) {\r
- fCacheUpdatePending = true;\r
- fCacheUpdateCompleted = completed;\r
- return;\r
- }\r
- fCacheUpdateBusy = true;\r
- }\r
- // Event cache is now updated. Perform update on the UI thread\r
- if (!fTable.isDisposed()) {\r
- fTable.getDisplay().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!fTable.isDisposed()) {\r
- fTable.refresh();\r
- packColumns();\r
- }\r
- if (completed) {\r
- populateCompleted();\r
- }\r
- synchronized (fCacheUpdateSyncObj) {\r
- fCacheUpdateBusy = false;\r
- if (fCacheUpdatePending) {\r
- fCacheUpdatePending = false;\r
- cacheUpdated(fCacheUpdateCompleted);\r
- }\r
- }\r
- }\r
- });\r
- }\r
- }\r
-\r
- protected void populateCompleted() {\r
- // Nothing by default;\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Bookmark handling\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Add a bookmark to this event table.\r
- *\r
- * @param bookmarksFile\r
- * The file to use for the bookmarks\r
- */\r
- public void addBookmark(final IFile bookmarksFile) {\r
- fBookmarksFile = bookmarksFile;\r
- final TableItem[] selection = fTable.getSelection();\r
- if (selection.length > 0) {\r
- final TableItem tableItem = selection[0];\r
- if (tableItem.getData(Key.RANK) != null) {\r
- final StringBuffer defaultMessage = new StringBuffer();\r
- for (int i = 0; i < fTable.getColumns().length; i++) {\r
- if (i > 0)\r
- {\r
- defaultMessage.append(", "); //$NON-NLS-1$\r
- }\r
- defaultMessage.append(tableItem.getText(i));\r
- }\r
- final InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
- Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText,\r
- defaultMessage.toString(), null);\r
- if (dialog.open() == Window.OK) {\r
- final String message = dialog.getValue();\r
- try {\r
- final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);\r
- if (bookmark.exists()) {\r
- bookmark.setAttribute(IMarker.MESSAGE, message.toString());\r
- final long rank = (Long) tableItem.getData(Key.RANK);\r
- final int location = (int) rank;\r
- bookmark.setAttribute(IMarker.LOCATION, (Integer) location);\r
- fBookmarksMap.put(rank, bookmark.getId());\r
- fTable.refresh();\r
- }\r
- } catch (final CoreException e) {\r
- displayException(e);\r
- }\r
- }\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Remove a bookmark from this event table.\r
- *\r
- * @param bookmark\r
- * The bookmark to remove\r
- */\r
- public void removeBookmark(final IMarker bookmark) {\r
- for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {\r
- if (entry.getValue().equals(bookmark.getId())) {\r
- fBookmarksMap.remove(entry.getKey());\r
- fTable.refresh();\r
- return;\r
- }\r
- }\r
- }\r
-\r
- private void toggleBookmark(final long rank) {\r
- if (fBookmarksFile == null) {\r
- return;\r
- }\r
- if (fBookmarksMap.containsKey(rank)) {\r
- final Long markerId = fBookmarksMap.remove(rank);\r
- fTable.refresh();\r
- try {\r
- final IMarker bookmark = fBookmarksFile.findMarker(markerId);\r
- if (bookmark != null) {\r
- bookmark.delete();\r
- }\r
- } catch (final CoreException e) {\r
- displayException(e);\r
- }\r
- } else {\r
- addBookmark(fBookmarksFile);\r
- }\r
- }\r
-\r
- /**\r
- * Refresh the bookmarks assigned to this trace, from the contents of a\r
- * bookmark file.\r
- *\r
- * @param bookmarksFile\r
- * The bookmark file to use\r
- */\r
- public void refreshBookmarks(final IFile bookmarksFile) {\r
- fBookmarksFile = bookmarksFile;\r
- if (bookmarksFile == null) {\r
- fBookmarksMap.clear();\r
- fTable.refresh();\r
- return;\r
- }\r
- try {\r
- fBookmarksMap.clear();\r
- for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {\r
- final int location = bookmark.getAttribute(IMarker.LOCATION, -1);\r
- if (location != -1) {\r
- final long rank = location;\r
- fBookmarksMap.put(rank, bookmark.getId());\r
- }\r
- }\r
- fTable.refresh();\r
- } catch (final CoreException e) {\r
- displayException(e);\r
- }\r
- }\r
-\r
- @Override\r
- public void gotoMarker(final IMarker marker) {\r
- final int rank = marker.getAttribute(IMarker.LOCATION, -1);\r
- if (rank != -1) {\r
- int index = rank;\r
- if (fTable.getData(Key.FILTER_OBJ) != null) {\r
- index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
- } else if (rank >= fTable.getItemCount()) {\r
- fPendingGotoRank = rank;\r
- }\r
- fTable.setSelection(index + 1); // +1 for header row\r
- }\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Listeners\r
- // ------------------------------------------------------------------------\r
-\r
- /*\r
- * (non-Javadoc)\r
- *\r
- * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])\r
- */\r
- @Override\r
- public void colorSettingsChanged(final ColorSetting[] colorSettings) {\r
- fTable.refresh();\r
- }\r
-\r
- @Override\r
- public void addEventsFilterListener(final ITmfEventsFilterListener listener) {\r
- if (!fEventsFilterListeners.contains(listener)) {\r
- fEventsFilterListeners.add(listener);\r
- }\r
- }\r
-\r
- @Override\r
- public void removeEventsFilterListener(final ITmfEventsFilterListener listener) {\r
- fEventsFilterListeners.remove(listener);\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Signal handlers\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Handler for the experiment updated signal.\r
- *\r
- * @param signal\r
- * The incoming signal\r
- */\r
- @TmfSignalHandler\r
- public void experimentUpdated(final TmfExperimentUpdatedSignal signal) {\r
- if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) {\r
- return;\r
- }\r
- // Perform the refresh on the UI thread\r
- Display.getDefault().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!fTable.isDisposed() && (fTrace != null)) {\r
- if (fTable.getData(Key.FILTER_OBJ) == null) {\r
- fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
- if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row\r
- fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row\r
- fPendingGotoRank = -1;\r
- }\r
- } else {\r
- startFilterThread();\r
- }\r
- }\r
- if (!fRawViewer.isDisposed() && (fTrace != null)) {\r
- fRawViewer.refreshEventCount();\r
- }\r
- }\r
- });\r
- }\r
-\r
- /**\r
- * Handler for the trace updated signal\r
- *\r
- * @param signal\r
- * The incoming signal\r
- */\r
- @TmfSignalHandler\r
- public void traceUpdated(final TmfTraceUpdatedSignal signal) {\r
- if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {\r
- return;\r
- }\r
- // Perform the refresh on the UI thread\r
- Display.getDefault().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!fTable.isDisposed() && (fTrace != null)) {\r
- if (fTable.getData(Key.FILTER_OBJ) == null) {\r
- fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row\r
- if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row\r
- fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row\r
- fPendingGotoRank = -1;\r
- }\r
- } else {\r
- startFilterThread();\r
- }\r
- }\r
- if (!fRawViewer.isDisposed() && (fTrace != null)) {\r
- fRawViewer.refreshEventCount();\r
- }\r
- }\r
- });\r
- }\r
-\r
- /**\r
- * Handler for the time synch signal.\r
- *\r
- * @param signal\r
- * The incoming signal\r
- */\r
- @SuppressWarnings("unchecked")\r
- @TmfSignalHandler\r
- public void currentTimeUpdated(final TmfTimeSynchSignal signal) {\r
- if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {\r
-\r
- // Create a request for one event that will be queued after other ongoing requests. When this request is completed\r
- // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents\r
- // the method fTrace.getRank() from interfering and delaying ongoing requests.\r
- final TmfDataRequest<ITmfEvent> subRequest = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {\r
-\r
- TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());\r
-\r
- @Override\r
- public void handleData(final ITmfEvent event) {\r
- super.handleData(event);\r
- }\r
-\r
- @Override\r
- public void handleCompleted() {\r
- super.handleCompleted();\r
- if (fTrace == null) {\r
- return;\r
- }\r
-\r
- // Verify if the event is within the trace range and adjust if necessary\r
- ITmfTimestamp timestamp = ts;\r
- if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {\r
- timestamp = fTrace.getStartTime();\r
- }\r
- if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {\r
- timestamp = fTrace.getEndTime();\r
- }\r
-\r
- // Get the rank of the selected event in the table\r
- final ITmfContext context = fTrace.seekEvent(timestamp);\r
- final long rank = context.getRank();\r
- fSelectedRank = rank;\r
-\r
- fTable.getDisplay().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- // Return if table is disposed\r
- if (fTable.isDisposed()) {\r
- return;\r
- }\r
-\r
- int index = (int) rank;\r
- if (fTable.isDisposed()) {\r
- return;\r
- }\r
- if (fTable.getData(Key.FILTER_OBJ) != null)\r
- {\r
- index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row\r
- }\r
- fTable.setSelection(index + 1); // +1 for header row\r
- fRawViewer.selectAndReveal(rank);\r
- }\r
- });\r
- }\r
- };\r
-\r
- ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(subRequest);\r
- }\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Error handling\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Display an exception in a message box\r
- *\r
- * @param e the exception\r
- */\r
- private static void displayException(final Exception e) {\r
- final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());\r
- mb.setText(e.getClass().getName());\r
- mb.setMessage(e.getMessage());\r
- mb.open();\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2010, 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
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Francois Chouinard - Initial API and implementation
+ * Patrick Tasse - Factored out from events view
+ * Francois Chouinard - Replaced Table by TmfVirtualTable
+ * Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)
+ * Ansgar Radermacher - Support navigation to model URIs (Bug 396956)
+ * Bernd Hufmann - Updated call site and model URI implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.viewers.events;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+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.internal.tmf.ui.dialogs.MultiLineInputDialog;
+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.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.filter.ITmfFilter;
+import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;
+import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;
+import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest.ExecutionType;
+import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.signal.TmfEventFilterAppliedSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfEventSearchAppliedSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
+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.location.ITmfLocation;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
+import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
+import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
+import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
+import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
+import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
+import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;
+import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+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.Menu;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.dialogs.ListDialog;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.themes.ColorUtil;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * The generic TMF Events table
+ *
+ * This is a view that will list events that are read from a trace.
+ *
+ * @version 1.0
+ * @author Francois Chouinard
+ * @author Patrick Tasse
+ * @since 2.0
+ */
+public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
+
+ /**
+ * Empty string array, used by {@link #getItemStrings}.
+ * @since 3.0
+ */
+ protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ 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 Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(
+ "icons/elcl16/search_match.gif"); //$NON-NLS-1$
+ 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 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;
+
+ /**
+ * The events table search/filter keys
+ *
+ * @version 1.0
+ * @author Patrick Tasse
+ */
+ public interface Key {
+ /** Search text */
+ String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$
+
+ /** Search object */
+ String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$
+
+ /** Filter text */
+ String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$
+
+ /** Filter object */
+ String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
+
+ /** Timestamp */
+ String TIMESTAMP = "$time"; //$NON-NLS-1$
+
+ /** Rank */
+ String RANK = "$rank"; //$NON-NLS-1$
+
+ /** Field ID */
+ String FIELD_ID = "$field_id"; //$NON-NLS-1$
+
+ /** Bookmark indicator */
+ String BOOKMARK = "$bookmark"; //$NON-NLS-1$
+ }
+
+ /**
+ * The events table search/filter state
+ *
+ * @version 1.0
+ * @author Patrick Tasse
+ */
+ public static enum HeaderState {
+ /** A search is being run */
+ SEARCH,
+
+ /** A filter is applied */
+ FILTER
+ }
+
+ interface Direction {
+ int FORWARD = +1;
+ int BACKWARD = -1;
+ }
+
+ // ------------------------------------------------------------------------
+ // Table data
+ // ------------------------------------------------------------------------
+
+ /** The virtual event table */
+ protected TmfVirtualTable fTable;
+
+ private Composite fComposite;
+ private SashForm fSashForm;
+ private TmfRawEventViewer fRawViewer;
+ private ITmfTrace fTrace;
+ private boolean fPackDone = false;
+ private HeaderState fHeaderState = HeaderState.SEARCH;
+ private long fSelectedRank = 0;
+ private ITmfTimestamp fSelectedBeginTimestamp = null;
+ private IStatusLineManager fStatusLineManager = null;
+
+ // Filter data
+ private long fFilterMatchCount;
+ private long fFilterCheckCount;
+ private FilterThread fFilterThread;
+ private boolean fFilterThreadResume = false;
+ private final Object fFilterSyncObj = new Object();
+ private SearchThread fSearchThread;
+ private final Object fSearchSyncObj = new Object();
+
+ /**
+ * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
+ *
+ * @see #fireSelectionChanged
+ */
+ private ListenerList selectionChangedListeners = new ListenerList();
+
+ // Bookmark map <Rank, MarkerId>
+ private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
+ private IFile fBookmarksFile;
+ private long fPendingGotoRank = -1;
+
+ // SWT resources
+ private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
+ private Color fGrayColor;
+ private Color fGreenColor;
+ private Font fBoldFont;
+
+ // Table column names
+ private static final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
+ Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,
+ Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };
+
+ private static final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),
+ new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),
+ new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };
+
+ // Event cache
+ private final TmfEventsCache fCache;
+ private boolean fCacheUpdateBusy = false;
+ private boolean fCacheUpdatePending = false;
+ private boolean fCacheUpdateCompleted = false;
+ private final Object fCacheUpdateSyncObj = new Object();
+
+ private boolean fDisposeOnClose;
+
+ // ------------------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Basic constructor, will use default column data.
+ *
+ * @param parent
+ * The parent composite UI object
+ * @param cacheSize
+ * The size of the event table cache
+ */
+ public TmfEventsTable(final Composite parent, final int cacheSize) {
+ this(parent, cacheSize, COLUMN_DATA);
+ }
+
+ /**
+ * Advanced constructor, where we also define which column data to use.
+ *
+ * @param parent
+ * The parent composite UI object
+ * @param cacheSize
+ * The size of the event table cache
+ * @param columnData
+ * The column data to use for this table
+ */
+ public TmfEventsTable(final Composite parent, int cacheSize, final ColumnData[] columnData) {
+ super("TmfEventsTable"); //$NON-NLS-1$
+
+ fComposite = new Composite(parent, SWT.NONE);
+ final GridLayout gl = new GridLayout(1, false);
+ gl.marginHeight = 0;
+ gl.marginWidth = 0;
+ gl.verticalSpacing = 0;
+ fComposite.setLayout(gl);
+
+ fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
+ fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Create a virtual table
+ final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
+ fTable = new TmfVirtualTable(fSashForm, style);
+
+ // Set the table layout
+ final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ fTable.setLayoutData(layoutData);
+
+ // Some cosmetic enhancements
+ fTable.setHeaderVisible(true);
+ fTable.setLinesVisible(true);
+
+ // Set the columns
+ setColumnHeaders(columnData);
+
+ // Set the default column field ids if this is not a subclass
+ if (Arrays.equals(columnData, COLUMN_DATA)) {
+ fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);
+ fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);
+ fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);
+ fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);
+ fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);
+ }
+
+ // Set the frozen row for header row
+ fTable.setFrozenRowCount(1);
+
+ // Create the header row cell editor
+ createHeaderEditor();
+
+ // Handle the table item selection
+ fTable.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(final SelectionEvent e) {
+ if (e.item == null) {
+ return;
+ }
+ updateStatusLine(null);
+ if (fTable.getSelectionIndices().length > 0) {
+ if (e.item.getData(Key.RANK) instanceof Long) {
+ fSelectedRank = (Long) e.item.getData(Key.RANK);
+ fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
+ }
+ if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
+ final ITmfTimestamp ts = (ITmfTimestamp) e.item.getData(Key.TIMESTAMP);
+ if (fTable.getSelectionIndices().length == 1) {
+ fSelectedBeginTimestamp = ts;
+ }
+ if (fSelectedBeginTimestamp != null) {
+ if (fSelectedBeginTimestamp.compareTo(ts) <= 0) {
+ broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, fSelectedBeginTimestamp, ts));
+ if (fTable.getSelectionIndices().length == 2) {
+ updateStatusLine(ts.getDelta(fSelectedBeginTimestamp));
+ }
+ } else {
+ broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts, fSelectedBeginTimestamp));
+ updateStatusLine(fSelectedBeginTimestamp.getDelta(ts));
+ }
+ }
+ } else {
+ if (fTable.getSelectionIndices().length == 1) {
+ fSelectedBeginTimestamp = null;
+ }
+ }
+ }
+ if (e.item.getData() != null) {
+ fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
+ } else {
+ fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
+ }
+ }
+ });
+
+ int realCacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
+ realCacheSize = Math.min(realCacheSize, MAX_CACHE_SIZE);
+ fCache = new TmfEventsCache(realCacheSize, this);
+
+ // Handle the table item requests
+ fTable.addListener(SWT.SetData, new Listener() {
+
+ @Override
+ public void handleEvent(final Event event) {
+
+ final TableItem item = (TableItem) event.item;
+ int index = event.index - 1; // -1 for the header row
+
+ if (event.index == 0) {
+ setHeaderRowItemData(item);
+ return;
+ }
+
+ if (fTable.getData(Key.FILTER_OBJ) != null) {
+ if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
+ setFilterStatusRowItemData(item);
+ return;
+ }
+ index = index - 1; // -1 for top filter status row
+ }
+
+ final CachedEvent cachedEvent = fCache.getEvent(index);
+ if (cachedEvent != null) {
+ setItemData(item, cachedEvent.event, cachedEvent.rank);
+ return;
+ }
+
+ // Else, fill the cache asynchronously (and off the UI thread)
+ event.doit = false;
+ }
+ });
+
+ fTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDoubleClick(final MouseEvent event) {
+ if (event.button != 1) {
+ return;
+ }
+ // Identify the selected row
+ final Point point = new Point(event.x, event.y);
+ final TableItem item = fTable.getItem(point);
+ if (item != null) {
+ final Rectangle imageBounds = item.getImageBounds(0);
+ imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
+ if (imageBounds.contains(point)) {
+ final Long rank = (Long) item.getData(Key.RANK);
+ if (rank != null) {
+ toggleBookmark(rank);
+ }
+ }
+ }
+ }
+ });
+
+ final Listener tooltipListener = new Listener () {
+ Shell tooltipShell = null;
+ @Override
+ public void handleEvent(final Event event) {
+ switch (event.type) {
+ case SWT.MouseHover:
+ final TableItem item = fTable.getItem(new Point(event.x, event.y));
+ 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;
+ }
+ if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
+ tooltipShell.dispose();
+ }
+ tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
+ tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ final FillLayout layout = new FillLayout();
+ 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$
+ label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ label.setText(text);
+ label.addListener(SWT.MouseExit, this);
+ label.addListener(SWT.MouseDown, this);
+ label.addListener(SWT.MouseWheel, this);
+ final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ /*
+ * 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.
+ */
+ int y = event.y;
+ if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
+ y += fTable.getHeaderHeight();
+ }
+ Point pt = fTable.toDisplay(event.x, y);
+ pt.x += BOOKMARK_IMAGE.getBounds().width;
+ pt.y += item.getBounds().height;
+ tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
+ tooltipShell.setVisible(true);
+ break;
+ case SWT.Dispose:
+ case SWT.KeyDown:
+ case SWT.MouseMove:
+ case SWT.MouseExit:
+ case SWT.MouseDown:
+ case SWT.MouseWheel:
+ if (tooltipShell != null) {
+ tooltipShell.dispose();
+ tooltipShell = null;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ fTable.addListener(SWT.MouseHover, tooltipListener);
+ fTable.addListener(SWT.Dispose, tooltipListener);
+ fTable.addListener(SWT.KeyDown, tooltipListener);
+ fTable.addListener(SWT.MouseMove, tooltipListener);
+ fTable.addListener(SWT.MouseExit, tooltipListener);
+ fTable.addListener(SWT.MouseDown, tooltipListener);
+ fTable.addListener(SWT.MouseWheel, tooltipListener);
+
+ // Create resources
+ createResources();
+
+ ColorSettingsManager.addColorSettingsListener(this);
+
+ fTable.setItemCount(1); // +1 for header row
+
+ fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
+
+ fRawViewer.addSelectionListener(new Listener() {
+ @Override
+ public void handleEvent(final Event e) {
+ if (e.data instanceof Long) {
+ final long rank = (Long) e.data;
+ int index = (int) rank;
+ if (fTable.getData(Key.FILTER_OBJ) != null) {
+ index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+ }
+ fTable.setSelection(index + 1); // +1 for header row
+ fSelectedRank = rank;
+ updateStatusLine(null);
+ } else if (e.data instanceof ITmfLocation) {
+ // DOES NOT WORK: rank undefined in context from seekLocation()
+ // ITmfLocation<?> location = (ITmfLocation<?>) e.data;
+ // TmfContext context = fTrace.seekLocation(location);
+ // fTable.setSelection((int) context.getRank());
+ return;
+ } else {
+ return;
+ }
+ final TableItem[] selection = fTable.getSelection();
+ if ((selection != null) && (selection.length > 0)) {
+ final TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData(Key.TIMESTAMP);
+ if (ts != null) {
+ broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));
+ }
+ }
+ }
+ });
+
+ fSashForm.setWeights(new int[] { 1, 1 });
+ fRawViewer.setVisible(false);
+
+ createPopupMenu();
+ }
+
+ /**
+ * Create a pop-up menu.
+ */
+ protected void createPopupMenu() {
+ final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
+ @Override
+ public void run() {
+ fTable.setVisible(true);
+ fSashForm.layout();
+ }
+ };
+
+ final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
+ @Override
+ public void run() {
+ fTable.setVisible(false);
+ fSashForm.layout();
+ }
+ };
+
+ final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {
+ @Override
+ public void run() {
+ fRawViewer.setVisible(true);
+ fSashForm.layout();
+ final int index = fTable.getSelectionIndex();
+ if (index >= 1) {
+ fRawViewer.selectAndReveal(index - 1);
+ }
+ }
+ };
+
+ final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {
+ @Override
+ public void run() {
+ fRawViewer.setVisible(false);
+ fSashForm.layout();
+ }
+ };
+
+ final IAction openCallsiteAction = new Action(Messages.TmfEventsTable_OpenSourceCodeActionText) {
+ @Override
+ public void run() {
+ final TableItem items[] = fTable.getSelection();
+ if (items.length != 1) {
+ return;
+ }
+ final TableItem item = items[0];
+
+ final Object data = item.getData();
+ if (data instanceof ITmfSourceLookup) {
+ ITmfSourceLookup event = (ITmfSourceLookup) data;
+ ITmfCallsite cs = event.getCallsite();
+ if (cs == null || cs.getFileName() == null) {
+ return;
+ }
+ IMarker marker = null;
+ try {
+ String fileName = cs.getFileName();
+ final String trimmedPath = fileName.replaceAll("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ final ArrayList<IFile> files = new ArrayList<>();
+ ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
+ @Override
+ public boolean visit(IResource resource) throws CoreException {
+ if (resource instanceof IFile && resource.getFullPath().toString().endsWith(trimmedPath)) {
+ files.add((IFile) resource);
+ }
+ return true;
+ }
+ });
+ IFile file = null;
+ if (files.size() > 1) {
+ ListDialog dialog = new ListDialog(getTable().getShell());
+ dialog.setContentProvider(ArrayContentProvider.getInstance());
+ dialog.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ return ((IFile) element).getFullPath().toString();
+ }
+ });
+ dialog.setInput(files);
+ dialog.setTitle(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle);
+ dialog.setMessage(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle + '\n' + cs.toString());
+ dialog.open();
+ Object[] result = dialog.getResult();
+ if (result != null && result.length > 0) {
+ file = (IFile) result[0];
+ }
+ } else if (files.size() == 1) {
+ file = files.get(0);
+ }
+ if (file != null) {
+ marker = file.createMarker(IMarker.MARKER);
+ marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
+ IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
+ marker.delete();
+ } else if (files.size() == 0){
+ displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
+ }
+ } catch (CoreException e) {
+ displayException(e);
+ }
+ }
+ }
+ };
+
+ final IAction openModelAction = new Action(Messages.TmfEventsTable_OpenModelActionText) {
+ @Override
+ public void run() {
+
+ final TableItem items[] = fTable.getSelection();
+ if (items.length != 1) {
+ return;
+ }
+ final TableItem item = items[0];
+
+ final Object eventData = item.getData();
+ if (eventData instanceof ITmfModelLookup) {
+ String modelURI = ((ITmfModelLookup) eventData).getModelUri();
+
+ if (modelURI != null) {
+ IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ IFile file = null;
+ final URI uri = URI.createURI(modelURI);
+ if (uri.isPlatformResource()) {
+ IPath path = new Path(uri.toPlatformString(true));
+ file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+ } else if (uri.isFile() && !uri.isRelative()) {
+ file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
+ new Path(uri.toFileString()));
+ }
+
+ if (file != null) {
+ try {
+ /*
+ * create a temporary validation marker on the
+ * model file, remove it afterwards thus,
+ * navigation works with all model editors
+ * supporting the navigation to a marker
+ */
+ IMarker marker = file.createMarker(EValidator.MARKER);
+ marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
+ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
+
+ IDE.openEditor(activePage, marker, OpenStrategy.activateOnOpen());
+ marker.delete();
+ }
+ catch (CoreException e) {
+ displayException(e);
+ }
+ } else {
+ displayException(new FileNotFoundException('\'' + modelURI + '\'' + '\n' + Messages.TmfEventsTable_OpenModelUnsupportedURI));
+ }
+ }
+ }
+ }
+ };
+
+ final IAction exportToTextAction = new Action(Messages.TmfEventsTable_Export_to_text) {
+ @Override
+ public void run() {
+ IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IHandlerService handlerService = (IHandlerService) activePage.getActiveEditor().getSite().getService(IHandlerService.class);
+ ICommandService cmdService = (ICommandService) activePage.getActiveEditor().getSite().getService(ICommandService.class);
+ try {
+ HashMap<String, Object> parameters = new HashMap<>();
+ StringBuilder header = new StringBuilder();
+ boolean needTab = false;
+ for (TableColumn tc: fTable.getColumns()) {
+ if (needTab) {
+ header.append('\t');
+ }
+ header.append(tc.getText());
+ needTab = true;
+ }
+ Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
+ ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command,parameters);
+ IEvaluationContext context = handlerService.getCurrentState();
+ context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_HEADER_ID, header.toString());
+ context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_PARAMETER_ID, TmfEventsTable.this);
+ handlerService.executeCommandInContext(cmd, null, context);
+ } catch (ExecutionException e) {
+ displayException(e);
+ } catch (NotDefinedException e) {
+ displayException(e);
+ } catch (NotEnabledException e) {
+ displayException(e);
+ } catch (NotHandledException e) {
+ displayException(e);
+ }
+ }
+ };
+
+ final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
+ @Override
+ public void run() {
+ fHeaderState = HeaderState.SEARCH;
+ fTable.refresh();
+ }
+ };
+
+ final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
+ @Override
+ public void run() {
+ fHeaderState = HeaderState.FILTER;
+ fTable.refresh();
+ }
+ };
+
+ final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
+ @Override
+ public void run() {
+ clearFilters();
+ }
+ };
+
+ class ToggleBookmarkAction extends Action {
+ Long fRank;
+
+ public ToggleBookmarkAction(final String text, final Long rank) {
+ super(text);
+ fRank = rank;
+ }
+
+ @Override
+ public void run() {
+ toggleBookmark(fRank);
+ }
+ }
+
+ final MenuManager tablePopupMenu = new MenuManager();
+ tablePopupMenu.setRemoveAllWhenShown(true);
+ tablePopupMenu.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(final IMenuManager manager) {
+ if (fTable.getSelectionIndex() == 0) {
+ // Right-click on header row
+ if (fHeaderState == HeaderState.FILTER) {
+ tablePopupMenu.add(showSearchBarAction);
+ } else {
+ tablePopupMenu.add(showFilterBarAction);
+ }
+ return;
+ }
+ final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
+ final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
+ if (item != null) {
+ final Rectangle imageBounds = item.getImageBounds(0);
+ imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
+ if (point.x <= (imageBounds.x + imageBounds.width)) {
+ // Right-click on left margin
+ final Long rank = (Long) item.getData(Key.RANK);
+ if ((rank != null) && (fBookmarksFile != null)) {
+ if (fBookmarksMap.containsKey(rank)) {
+ tablePopupMenu.add(new ToggleBookmarkAction(
+ Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
+ } else {
+ tablePopupMenu.add(new ToggleBookmarkAction(
+ Messages.TmfEventsTable_AddBookmarkActionText, rank));
+ }
+ }
+ return;
+ }
+ }
+
+ // Right-click on table
+ if (fTable.isVisible() && fRawViewer.isVisible()) {
+ tablePopupMenu.add(hideTableAction);
+ tablePopupMenu.add(hideRawAction);
+ } else if (!fTable.isVisible()) {
+ tablePopupMenu.add(showTableAction);
+ } else if (!fRawViewer.isVisible()) {
+ tablePopupMenu.add(showRawAction);
+ }
+ tablePopupMenu.add(exportToTextAction);
+ tablePopupMenu.add(new Separator());
+
+ if (item != null) {
+ final Object data = item.getData();
+ Separator separator = null;
+ if (data instanceof ITmfSourceLookup) {
+ ITmfSourceLookup event = (ITmfSourceLookup) data;
+ if (event.getCallsite() != null) {
+ tablePopupMenu.add(openCallsiteAction);
+ separator = new Separator();
+ }
+ }
+
+ if (data instanceof ITmfModelLookup) {
+ ITmfModelLookup event = (ITmfModelLookup) data;
+ if (event.getModelUri() != null) {
+ tablePopupMenu.add(openModelAction);
+ separator = new Separator();
+ }
+
+ if (separator != null) {
+ tablePopupMenu.add(separator);
+ }
+ }
+ }
+
+ tablePopupMenu.add(clearFiltersAction);
+ final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
+ if (savedFilters.length > 0) {
+ final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
+ for (final ITmfFilterTreeNode node : savedFilters) {
+ if (node instanceof TmfFilterNode) {
+ final TmfFilterNode filter = (TmfFilterNode) node;
+ subMenu.add(new Action(filter.getFilterName()) {
+ @Override
+ public void run() {
+ applyFilter(filter);
+ }
+ });
+ }
+ }
+ tablePopupMenu.add(subMenu);
+ }
+ appendToTablePopupMenu(tablePopupMenu, item);
+ }
+ });
+
+ final MenuManager rawViewerPopupMenu = new MenuManager();
+ rawViewerPopupMenu.setRemoveAllWhenShown(true);
+ rawViewerPopupMenu.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(final IMenuManager manager) {
+ if (fTable.isVisible() && fRawViewer.isVisible()) {
+ rawViewerPopupMenu.add(hideTableAction);
+ rawViewerPopupMenu.add(hideRawAction);
+ } else if (!fTable.isVisible()) {
+ rawViewerPopupMenu.add(showTableAction);
+ } else if (!fRawViewer.isVisible()) {
+ rawViewerPopupMenu.add(showRawAction);
+ }
+ appendToRawPopupMenu(tablePopupMenu);
+ }
+ });
+
+ Menu menu = tablePopupMenu.createContextMenu(fTable);
+ fTable.setMenu(menu);
+
+ menu = rawViewerPopupMenu.createContextMenu(fRawViewer);
+ fRawViewer.setMenu(menu);
+ }
+
+
+ /**
+ * Append an item to the event table's pop-up menu.
+ *
+ * @param tablePopupMenu
+ * The menu manager
+ * @param selectedItem
+ * The item to append
+ */
+ protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
+ // override to append more actions
+ }
+
+ /**
+ * Append an item to the raw viewer's pop-up menu.
+ *
+ * @param rawViewerPopupMenu
+ * The menu manager
+ */
+ protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
+ // override to append more actions
+ }
+
+ @Override
+ public void dispose() {
+ stopSearchThread();
+ stopFilterThread();
+ ColorSettingsManager.removeColorSettingsListener(this);
+ fComposite.dispose();
+ if ((fTrace != null) && fDisposeOnClose) {
+ fTrace.dispose();
+ }
+ fResourceManager.dispose();
+ fRawViewer.dispose();
+ super.dispose();
+ }
+
+ /**
+ * Assign a layout data object to this view.
+ *
+ * @param layoutData
+ * The layout data to assign
+ */
+ public void setLayoutData(final Object layoutData) {
+ fComposite.setLayoutData(layoutData);
+ }
+
+ /**
+ * Get the virtual table contained in this event table.
+ *
+ * @return The TMF virtual table
+ */
+ public TmfVirtualTable getTable() {
+ return fTable;
+ }
+
+ /**
+ * @param columnData
+ *
+ * FIXME: Add support for column selection
+ */
+ protected void setColumnHeaders(final ColumnData[] columnData) {
+ fTable.setColumnHeaders(columnData);
+ }
+
+ /**
+ * Set a table item's data.
+ *
+ * @param item
+ * The item to set
+ * @param event
+ * Which trace event to link with this entry
+ * @param rank
+ * Which rank this event has in the trace/experiment
+ */
+ protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
+ item.setText(getItemStrings(event));
+ item.setData(event);
+ item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
+ item.setData(Key.RANK, rank);
+
+ final Collection<Long> markerIds = fBookmarksMap.get(rank);
+ if (!markerIds.isEmpty()) {
+ Joiner joiner = Joiner.on("\n -").skipNulls(); //$NON-NLS-1$
+ List<Object> parts = new ArrayList<>();
+ if (markerIds.size() > 1) {
+ parts.add(Messages.TmfEventsTable_MultipleBookmarksToolTip);
+ }
+ try {
+ for (long markerId : markerIds) {
+ final IMarker marker = fBookmarksFile.findMarker(markerId);
+ parts.add(marker.getAttribute(IMarker.MESSAGE));
+ }
+ } catch (CoreException e) {
+ displayException(e);
+ }
+ item.setData(Key.BOOKMARK, joiner.join(parts));
+ } else {
+ item.setData(Key.BOOKMARK, null);
+ }
+
+ boolean searchMatch = false;
+ boolean searchNoMatch = false;
+ final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
+ if (searchFilter != null) {
+ if (searchFilter.matches(event)) {
+ searchMatch = true;
+ } else {
+ searchNoMatch = true;
+ }
+ }
+
+ final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);
+ if (searchNoMatch) {
+ item.setForeground(colorSetting.getDimmedForegroundColor());
+ item.setBackground(colorSetting.getDimmedBackgroundColor());
+ } else {
+ item.setForeground(colorSetting.getForegroundColor());
+ item.setBackground(colorSetting.getBackgroundColor());
+ }
+
+ if (searchMatch) {
+ if (!markerIds.isEmpty()) {
+ item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
+ } else {
+ item.setImage(SEARCH_MATCH_IMAGE);
+ }
+ } else if (!markerIds.isEmpty()) {
+ item.setImage(BOOKMARK_IMAGE);
+ } else {
+ item.setImage((Image) null);
+ }
+ }
+
+ /**
+ * Set the item data of the header row.
+ *
+ * @param item
+ * The item to use as table header
+ */
+ protected void setHeaderRowItemData(final TableItem item) {
+ String txtKey = null;
+ if (fHeaderState == HeaderState.SEARCH) {
+ item.setImage(SEARCH_IMAGE);
+ txtKey = Key.SEARCH_TXT;
+ } else if (fHeaderState == HeaderState.FILTER) {
+ item.setImage(FILTER_IMAGE);
+ txtKey = Key.FILTER_TXT;
+ }
+ item.setForeground(fGrayColor);
+ for (int i = 0; i < fTable.getColumns().length; i++) {
+ final TableColumn column = fTable.getColumns()[i];
+ final String filter = (String) column.getData(txtKey);
+ if (filter == null) {
+ if (fHeaderState == HeaderState.SEARCH) {
+ item.setText(i, SEARCH_HINT);
+ } else if (fHeaderState == HeaderState.FILTER) {
+ item.setText(i, FILTER_HINT);
+ }
+ item.setForeground(i, fGrayColor);
+ item.setFont(i, fTable.getFont());
+ } else {
+ item.setText(i, filter);
+ item.setForeground(i, fGreenColor);
+ item.setFont(i, fBoldFont);
+ }
+ }
+ }
+
+ /**
+ * Set the item data of the "filter status" row.
+ *
+ * @param item
+ * The item to use as filter status row
+ */
+ protected void setFilterStatusRowItemData(final TableItem item) {
+ for (int i = 0; i < fTable.getColumns().length; i++) {
+ if (i == 0) {
+ if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
+ item.setImage(FILTER_IMAGE);
+ } else {
+ item.setImage(STOP_IMAGE);
+ }
+ item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
+ } else {
+ item.setText(i, ""); //$NON-NLS-1$
+ }
+ }
+ item.setData(null);
+ item.setData(Key.TIMESTAMP, null);
+ item.setData(Key.RANK, null);
+ item.setForeground(null);
+ item.setBackground(null);
+ }
+
+ /**
+ * Create an editor for the header.
+ */
+ protected void createHeaderEditor() {
+ final TableEditor tableEditor = fTable.createTableEditor();
+ tableEditor.horizontalAlignment = SWT.LEFT;
+ tableEditor.verticalAlignment = SWT.CENTER;
+ tableEditor.grabHorizontal = true;
+ tableEditor.minimumWidth = 50;
+
+ // Handle the header row selection
+ fTable.addMouseListener(new MouseAdapter() {
+ int columnIndex;
+ TableColumn column;
+ TableItem item;
+
+ @Override
+ public void mouseDown(final MouseEvent event) {
+ if (event.button != 1) {
+ return;
+ }
+ // Identify the selected row
+ final Point point = new Point(event.x, event.y);
+ item = fTable.getItem(point);
+
+ // Header row selected
+ if ((item != null) && (fTable.indexOf(item) == 0)) {
+
+ // Icon selected
+ if (item.getImageBounds(0).contains(point)) {
+ if (fHeaderState == HeaderState.SEARCH) {
+ fHeaderState = HeaderState.FILTER;
+ } else if (fHeaderState == HeaderState.FILTER) {
+ fHeaderState = HeaderState.SEARCH;
+ }
+ fTable.setSelection(0);
+ fTable.refresh();
+ return;
+ }
+
+ // Identify the selected column
+ columnIndex = -1;
+ for (int i = 0; i < fTable.getColumns().length; i++) {
+ final Rectangle rect = item.getBounds(i);
+ if (rect.contains(point)) {
+ columnIndex = i;
+ break;
+ }
+ }
+
+ if (columnIndex == -1) {
+ 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);
+ if (headerString != null) {
+ newEditor.setText(headerString);
+ }
+ newEditor.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusLost(final FocusEvent e) {
+ final boolean changed = updateHeader(newEditor.getText());
+ if (changed) {
+ applyHeader();
+ }
+ }
+ });
+ newEditor.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent e) {
+ if (e.character == SWT.CR) {
+ updateHeader(newEditor.getText());
+ applyHeader();
+
+ // 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();
+ }
+ }
+ });
+ newEditor.selectAll();
+ newEditor.setFocus();
+ tableEditor.setEditor(newEditor, item, columnIndex);
+ }
+ }
+
+ /*
+ * returns true is value was changed
+ */
+ private boolean updateHeader(final String text) {
+ 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 (text.trim().length() > 0) {
+ try {
+ final String regex = TmfFilterMatchesNode.regexFix(text);
+ Pattern.compile(regex);
+ if (regex.equals(column.getData(txtKey))) {
+ tableEditor.getEditor().dispose();
+ return false;
+ }
+ final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
+ String fieldId = (String) column.getData(Key.FIELD_ID);
+ if (fieldId == null) {
+ fieldId = column.getText();
+ }
+ filter.setField(fieldId);
+ filter.setRegex(regex);
+ column.setData(objKey, filter);
+ column.setData(txtKey, regex);
+ } catch (final PatternSyntaxException ex) {
+ tableEditor.getEditor().dispose();
+ MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+ ex.getDescription(), ex.getMessage());
+ return false;
+ }
+ } else {
+ if (column.getData(txtKey) == null) {
+ tableEditor.getEditor().dispose();
+ return false;
+ }
+ column.setData(objKey, null);
+ column.setData(txtKey, 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();
+ }
+ }
+
+ tableEditor.getEditor().dispose();
+ }
+ });
+
+ fTable.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent e) {
+ e.doit = false;
+ if (e.character == SWT.ESC) {
+ stopFilterThread();
+ stopSearchThread();
+ fTable.refresh();
+ } else if (e.character == SWT.DEL) {
+ if (fHeaderState == HeaderState.SEARCH) {
+ stopSearchThread();
+ for (final TableColumn column : fTable.getColumns()) {
+ column.setData(Key.SEARCH_OBJ, null);
+ column.setData(Key.SEARCH_TXT, null);
+ }
+ fTable.setData(Key.SEARCH_OBJ, null);
+ fTable.refresh();
+ fireSearchApplied(null);
+ } else if (fHeaderState == HeaderState.FILTER) {
+ clearFilters();
+ }
+ } else if (e.character == SWT.CR) {
+ if ((e.stateMask & SWT.SHIFT) == 0) {
+ searchNext();
+ } else {
+ searchPrevious();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Send an event indicating a filter has been applied.
+ *
+ * @param filter
+ * The filter that was just applied
+ */
+ protected void fireFilterApplied(final ITmfFilter filter) {
+ broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
+ }
+
+ /**
+ * Send an event indicating that a search has been applied.
+ *
+ * @param filter
+ * The search filter that was just applied
+ */
+ protected void fireSearchApplied(final ITmfFilter filter) {
+ broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
+ }
+
+ /**
+ * Start the filtering thread.
+ */
+ protected void startFilterThread() {
+ synchronized (fFilterSyncObj) {
+ final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+ if (fFilterThread == null || fFilterThread.filter != filter) {
+ if (fFilterThread != null) {
+ fFilterThread.cancel();
+ fFilterThreadResume = false;
+ }
+ fFilterThread = new FilterThread(filter);
+ fFilterThread.start();
+ } else {
+ fFilterThreadResume = true;
+ }
+ }
+ }
+
+ /**
+ * Stop the filtering thread.
+ */
+ protected void stopFilterThread() {
+ synchronized (fFilterSyncObj) {
+ if (fFilterThread != null) {
+ fFilterThread.cancel();
+ fFilterThread = null;
+ fFilterThreadResume = false;
+ }
+ }
+ }
+
+ /**
+ * Apply a filter.
+ *
+ * @param filter
+ * The filter to apply
+ * @since 1.1
+ */
+ protected void applyFilter(ITmfFilter filter) {
+ stopFilterThread();
+ stopSearchThread();
+ fFilterMatchCount = 0;
+ fFilterCheckCount = 0;
+ fCache.applyFilter(filter);
+ fTable.clearAll();
+ fTable.setData(Key.FILTER_OBJ, filter);
+ fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
+ startFilterThread();
+ fireFilterApplied(filter);
+ }
+
+ /**
+ * Clear all currently active filters.
+ */
+ protected void clearFilters() {
+ if (fTable.getData(Key.FILTER_OBJ) == null) {
+ return;
+ }
+ stopFilterThread();
+ stopSearchThread();
+ fCache.clearFilter();
+ fTable.clearAll();
+ for (final TableColumn column : fTable.getColumns()) {
+ column.setData(Key.FILTER_OBJ, null);
+ column.setData(Key.FILTER_TXT, null);
+ }
+ fTable.setData(Key.FILTER_OBJ, null);
+ if (fTrace != null) {
+ fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+ } else {
+ fTable.setItemCount(1); // +1 for header row
+ }
+ fFilterMatchCount = 0;
+ fFilterCheckCount = 0;
+ if (fSelectedRank >= 0) {
+ fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
+ } else {
+ fTable.setSelection(0);
+ }
+ fireFilterApplied(null);
+ updateStatusLine(null);
+ }
+
+ /**
+ * Wrapper Thread object for the filtering thread.
+ */
+ protected class FilterThread extends Thread {
+ private final ITmfFilterTreeNode filter;
+ private TmfEventRequest request;
+ private boolean refreshBusy = false;
+ private boolean refreshPending = false;
+ private final Object syncObj = new Object();
+
+ /**
+ * Constructor.
+ *
+ * @param filter
+ * The filter this thread will be processing
+ */
+ public FilterThread(final ITmfFilterTreeNode filter) {
+ super("Filter Thread"); //$NON-NLS-1$
+ this.filter = filter;
+ }
+
+ @Override
+ public void run() {
+ if (fTrace == null) {
+ return;
+ }
+ final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
+ if (nbRequested <= 0) {
+ return;
+ }
+ request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
+ (int) fFilterCheckCount, nbRequested, ExecutionType.BACKGROUND) {
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (request.isCancelled()) {
+ return;
+ }
+ 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) {
+ refreshTable();
+ }
+ fFilterCheckCount++;
+ }
+ };
+ ((ITmfEventProvider) fTrace).sendRequest(request);
+ try {
+ request.waitForCompletion();
+ } catch (final InterruptedException e) {
+ }
+ refreshTable();
+ synchronized (fFilterSyncObj) {
+ fFilterThread = null;
+ if (fFilterThreadResume) {
+ fFilterThreadResume = false;
+ fFilterThread = new FilterThread(filter);
+ fFilterThread.start();
+ }
+ }
+ }
+
+ /**
+ * Refresh the filter.
+ */
+ public void refreshTable() {
+ synchronized (syncObj) {
+ if (refreshBusy) {
+ refreshPending = true;
+ return;
+ }
+ refreshBusy = true;
+ }
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (request.isCancelled()) {
+ return;
+ }
+ if (fTable.isDisposed()) {
+ return;
+ }
+ fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
+ fTable.refresh();
+ synchronized (syncObj) {
+ refreshBusy = false;
+ if (refreshPending) {
+ refreshPending = false;
+ refreshTable();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Cancel this filtering thread.
+ */
+ public void cancel() {
+ if (request != null) {
+ request.cancel();
+ }
+ }
+ }
+
+ /**
+ * Go to the next item of a search.
+ */
+ protected void searchNext() {
+ synchronized (fSearchSyncObj) {
+ if (fSearchThread != null) {
+ return;
+ }
+ final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
+ if (searchFilter == null) {
+ return;
+ }
+ final int selectionIndex = fTable.getSelectionIndex();
+ int startIndex;
+ if (selectionIndex > 0) {
+ startIndex = selectionIndex; // -1 for header row, +1 for next event
+ } else {
+ // header row is selected, start at top event
+ startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
+ }
+ final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+ if (eventFilter != null) {
+ startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
+ }
+ fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
+ fSearchThread.schedule();
+ }
+ }
+
+ /**
+ * Go to the previous item of a search.
+ */
+ protected void searchPrevious() {
+ synchronized (fSearchSyncObj) {
+ if (fSearchThread != null) {
+ return;
+ }
+ final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
+ if (searchFilter == null) {
+ return;
+ }
+ final int selectionIndex = fTable.getSelectionIndex();
+ int startIndex;
+ if (selectionIndex > 0) {
+ startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
+ } else {
+ // header row is selected, start at precedent of top event
+ startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
+ }
+ final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
+ if (eventFilter != null) {
+ startIndex = startIndex - 1; // -1 for top filter status row
+ }
+ fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
+ fSearchThread.schedule();
+ }
+ }
+
+ /**
+ * Stop the search thread.
+ */
+ protected void stopSearchThread() {
+ fPendingGotoRank = -1;
+ synchronized (fSearchSyncObj) {
+ if (fSearchThread != null) {
+ fSearchThread.cancel();
+ fSearchThread = null;
+ }
+ }
+ }
+
+ /**
+ * Wrapper for the search thread.
+ */
+ protected class SearchThread extends Job {
+
+ private ITmfFilterTreeNode searchFilter;
+ private ITmfFilterTreeNode eventFilter;
+ private int startIndex;
+ private int direction;
+ private long rank;
+ private long foundRank = -1;
+ private TmfEventRequest request;
+ private ITmfTimestamp foundTimestamp = null;
+
+ /**
+ * Constructor.
+ *
+ * @param searchFilter
+ * The search filter
+ * @param eventFilter
+ * The event filter
+ * @param startIndex
+ * The index at which we should start searching
+ * @param currentRank
+ * The current rank
+ * @param direction
+ * In which direction should we search, forward or backwards
+ */
+ public SearchThread(final ITmfFilterTreeNode searchFilter,
+ final ITmfFilterTreeNode eventFilter, final int startIndex,
+ final long currentRank, final int direction) {
+ super(Messages.TmfEventsTable_SearchingJobName);
+ this.searchFilter = searchFilter;
+ this.eventFilter = eventFilter;
+ this.startIndex = startIndex;
+ this.rank = currentRank;
+ this.direction = direction;
+ }
+
+ @Override
+ protected IStatus run(final IProgressMonitor monitor) {
+ if (fTrace == null) {
+ return Status.OK_STATUS;
+ }
+ final Display display = Display.getDefault();
+ if (startIndex < 0) {
+ rank = (int) fTrace.getNbEvents() - 1;
+ } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
+ rank = 0;
+ } else {
+ int idx = startIndex;
+ while (foundRank == -1) {
+ final CachedEvent event = fCache.peekEvent(idx);
+ if (event == null) {
+ break;
+ }
+ rank = event.rank;
+ if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
+ foundRank = event.rank;
+ foundTimestamp = event.event.getTimestamp();
+ break;
+ }
+ if (direction == Direction.FORWARD) {
+ idx++;
+ } else {
+ idx--;
+ }
+ }
+ if (foundRank == -1) {
+ if (direction == Direction.FORWARD) {
+ rank++;
+ if (rank > (fTrace.getNbEvents() - 1)) {
+ rank = 0;
+ }
+ } else {
+ rank--;
+ if (rank < 0) {
+ rank = (int) fTrace.getNbEvents() - 1;
+ }
+ }
+ }
+ }
+ final int startRank = (int) rank;
+ boolean wrapped = false;
+ while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
+ int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
+ if (direction == Direction.BACKWARD) {
+ rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
+ }
+ request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
+ (int) rank, nbRequested, ExecutionType.BACKGROUND) {
+ long currentRank = rank;
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
+ foundRank = currentRank;
+ foundTimestamp = event.getTimestamp();
+ if (direction == Direction.FORWARD) {
+ done();
+ return;
+ }
+ }
+ currentRank++;
+ }
+ };
+ ((ITmfEventProvider) fTrace).sendRequest(request);
+ try {
+ request.waitForCompletion();
+ if (request.isCancelled()) {
+ return Status.OK_STATUS;
+ }
+ } catch (final InterruptedException e) {
+ synchronized (fSearchSyncObj) {
+ fSearchThread = null;
+ }
+ return Status.OK_STATUS;
+ }
+ if (foundRank == -1) {
+ if (direction == Direction.FORWARD) {
+ if (rank == 0) {
+ synchronized (fSearchSyncObj) {
+ fSearchThread = null;
+ }
+ return Status.OK_STATUS;
+ }
+ nbRequested = (int) rank;
+ rank = 0;
+ wrapped = true;
+ } else {
+ rank--;
+ if (rank < 0) {
+ rank = (int) fTrace.getNbEvents() - 1;
+ wrapped = true;
+ }
+ if ((rank <= startRank) && wrapped) {
+ synchronized (fSearchSyncObj) {
+ fSearchThread = null;
+ }
+ return Status.OK_STATUS;
+ }
+ }
+ }
+ }
+ int index = (int) foundRank;
+ if (eventFilter != null) {
+ index = fCache.getFilteredEventIndex(foundRank);
+ }
+ final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
+
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (monitor.isCanceled()) {
+ return;
+ }
+ if (fTable.isDisposed()) {
+ return;
+ }
+ fTable.setSelection(selection);
+ fSelectedRank = foundRank;
+ fRawViewer.selectAndReveal(fSelectedRank);
+ if (foundTimestamp != null) {
+ broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, foundTimestamp));
+ }
+ fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
+ synchronized (fSearchSyncObj) {
+ fSearchThread = null;
+ }
+ updateStatusLine(null);
+ }
+ });
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ protected void canceling() {
+ request.cancel();
+ synchronized (fSearchSyncObj) {
+ fSearchThread = null;
+ }
+ }
+ }
+
+ /**
+ * Create the resources.
+ */
+ protected void createResources() {
+ fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
+ .getForeground().getRGB()));
+ fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
+ fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
+ }
+
+ /**
+ * Pack the columns.
+ */
+ protected void packColumns() {
+ if (fPackDone) {
+ return;
+ }
+ fTable.setRedraw(false);
+
+ 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);
+ }
+
+ if (column.getWidth() < headerWidth) {
+ column.setWidth(headerWidth);
+ }
+ }
+
+ fTable.setRedraw(true);
+ fPackDone = true;
+ }
+
+ /**
+ * Get the contents of the row in the events table corresponding to an
+ * event. The order of the elements corresponds to the order of the columns.
+ *
+ * TODO Use column IDs, not indexes, so that the column order can be
+ * re-arranged.
+ *
+ * @param event
+ * The event printed in this row
+ * @return The event row entries
+ * @since 3.0
+ */
+ public String[] getItemStrings(ITmfEvent event) {
+ if (event == null) {
+ return EMPTY_STRING_ARRAY;
+ }
+ return new String[] {
+ event.getTimestamp().toString(),
+ event.getSource(),
+ event.getType().getName(),
+ event.getReference(),
+ event.getContent().toString()
+ };
+ }
+
+ /**
+ * Notify this table that is got the UI focus.
+ */
+ public void setFocus() {
+ fTable.setFocus();
+ }
+
+ /**
+ * Assign a new trace to this event table.
+ *
+ * @param trace
+ * The trace to assign to this event table
+ * @param disposeOnClose
+ * true if the trace should be disposed when the table is
+ * disposed
+ */
+ public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
+ if ((fTrace != null) && fDisposeOnClose) {
+ fTrace.dispose();
+ }
+ fTrace = trace;
+ fPackDone = false;
+ fSelectedRank = 0;
+ fDisposeOnClose = disposeOnClose;
+
+ // Perform the updates on the UI thread
+ fTable.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ fTable.removeAll();
+ fCache.setTrace(fTrace); // Clear the cache
+ if (fTrace != null) {
+ if (!fTable.isDisposed() && (fTrace != null)) {
+ if (fTable.getData(Key.FILTER_OBJ) == null) {
+ fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+ } else {
+ stopFilterThread();
+ fFilterMatchCount = 0;
+ fFilterCheckCount = 0;
+ fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
+ startFilterThread();
+ }
+ }
+ }
+ fRawViewer.setTrace(fTrace);
+ }
+ });
+ }
+
+ /**
+ * Assign the status line manager
+ *
+ * @param statusLineManager
+ * The status line manager, or null to disable status line messages
+ * @since 2.1
+ */
+ public void setStatusLineManager(IStatusLineManager statusLineManager) {
+ if (fStatusLineManager != null && statusLineManager == null) {
+ fStatusLineManager.setMessage(""); //$NON-NLS-1$
+ }
+ fStatusLineManager = statusLineManager;
+ }
+
+ private void updateStatusLine(ITmfTimestamp delta) {
+ if (fStatusLineManager != null) {
+ if (delta != null) {
+ fStatusLineManager.setMessage("\u0394: " + delta); //$NON-NLS-1$
+ } else {
+ fStatusLineManager.setMessage(null);
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Event cache
+ // ------------------------------------------------------------------------
+
+ /**
+ * Notify that the event cache has been updated
+ *
+ * @param completed
+ * Also notify if the populating of the cache is complete, or
+ * not.
+ */
+ public void cacheUpdated(final boolean completed) {
+ synchronized (fCacheUpdateSyncObj) {
+ if (fCacheUpdateBusy) {
+ fCacheUpdatePending = true;
+ fCacheUpdateCompleted = completed;
+ return;
+ }
+ fCacheUpdateBusy = true;
+ }
+ // Event cache is now updated. Perform update on the UI thread
+ if (!fTable.isDisposed()) {
+ fTable.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!fTable.isDisposed()) {
+ fTable.refresh();
+ packColumns();
+ }
+ if (completed) {
+ populateCompleted();
+ }
+ synchronized (fCacheUpdateSyncObj) {
+ fCacheUpdateBusy = false;
+ if (fCacheUpdatePending) {
+ fCacheUpdatePending = false;
+ cacheUpdated(fCacheUpdateCompleted);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Callback for when populating the table is complete.
+ */
+ protected void populateCompleted() {
+ // Nothing by default;
+ }
+
+ // ------------------------------------------------------------------------
+ // ISelectionProvider
+ // ------------------------------------------------------------------------
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ selectionChangedListeners.add(listener);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public ISelection getSelection() {
+ if (fTable == null || fTable.isDisposed()) {
+ return StructuredSelection.EMPTY;
+ }
+ List<Object> list = new ArrayList<>(fTable.getSelection().length);
+ for (TableItem item : fTable.getSelection()) {
+ if (item.getData() != null) {
+ list.add(item.getData());
+ }
+ }
+ return new StructuredSelection(list);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ selectionChangedListeners.remove(listener);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public void setSelection(ISelection selection) {
+ // not implemented
+ }
+
+ /**
+ * Notifies any selection changed listeners that the viewer's selection has changed.
+ * Only listeners registered at the time this method is called are notified.
+ *
+ * @param event a selection changed event
+ *
+ * @see ISelectionChangedListener#selectionChanged
+ * @since 2.0
+ */
+ protected void fireSelectionChanged(final SelectionChangedEvent event) {
+ Object[] listeners = selectionChangedListeners.getListeners();
+ for (int i = 0; i < listeners.length; ++i) {
+ final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
+ SafeRunnable.run(new SafeRunnable() {
+ @Override
+ public void run() {
+ l.selectionChanged(event);
+ }
+ });
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Bookmark handling
+ // ------------------------------------------------------------------------
+
+ /**
+ * Add a bookmark to this event table.
+ *
+ * @param bookmarksFile
+ * The file to use for the bookmarks
+ */
+ public void addBookmark(final IFile bookmarksFile) {
+ fBookmarksFile = bookmarksFile;
+ final TableItem[] selection = fTable.getSelection();
+ if (selection.length > 0) {
+ final TableItem tableItem = selection[0];
+ if (tableItem.getData(Key.RANK) != null) {
+ final StringBuffer defaultMessage = new StringBuffer();
+ for (int i = 0; i < fTable.getColumns().length; i++) {
+ if (i > 0) {
+ defaultMessage.append(", "); //$NON-NLS-1$
+ }
+ defaultMessage.append(tableItem.getText(i));
+ }
+ final InputDialog dialog = new MultiLineInputDialog(
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+ Messages.TmfEventsTable_AddBookmarkDialogTitle,
+ Messages.TmfEventsTable_AddBookmarkDialogMessage,
+ defaultMessage.toString());
+ if (dialog.open() == Window.OK) {
+ final String message = dialog.getValue();
+ try {
+ final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
+ if (bookmark.exists()) {
+ bookmark.setAttribute(IMarker.MESSAGE, message.toString());
+ final Long rank = (Long) tableItem.getData(Key.RANK);
+ final int location = rank.intValue();
+ bookmark.setAttribute(IMarker.LOCATION, Integer.valueOf(location));
+ fBookmarksMap.put(rank, bookmark.getId());
+ fTable.refresh();
+ }
+ } catch (final CoreException e) {
+ displayException(e);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Remove a bookmark from this event table.
+ *
+ * @param bookmark
+ * The bookmark to remove
+ */
+ public void removeBookmark(final IMarker bookmark) {
+ for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
+ if (entry.getValue().equals(bookmark.getId())) {
+ fBookmarksMap.remove(entry.getKey(), entry.getValue());
+ fTable.refresh();
+ return;
+ }
+ }
+ }
+
+ private void toggleBookmark(final Long rank) {
+ if (fBookmarksFile == null) {
+ return;
+ }
+ if (fBookmarksMap.containsKey(rank)) {
+ final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
+ fTable.refresh();
+ try {
+ for (long markerId : markerIds) {
+ final IMarker bookmark = fBookmarksFile.findMarker(markerId);
+ if (bookmark != null) {
+ bookmark.delete();
+ }
+ }
+ } catch (final CoreException e) {
+ displayException(e);
+ }
+ } else {
+ addBookmark(fBookmarksFile);
+ }
+ }
+
+ /**
+ * Refresh the bookmarks assigned to this trace, from the contents of a
+ * bookmark file.
+ *
+ * @param bookmarksFile
+ * The bookmark file to use
+ */
+ public void refreshBookmarks(final IFile bookmarksFile) {
+ fBookmarksFile = bookmarksFile;
+ if (bookmarksFile == null) {
+ fBookmarksMap.clear();
+ fTable.refresh();
+ return;
+ }
+ try {
+ fBookmarksMap.clear();
+ for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
+ final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
+ if (location != -1) {
+ final long rank = location;
+ fBookmarksMap.put(rank, bookmark.getId());
+ }
+ }
+ fTable.refresh();
+ } catch (final CoreException e) {
+ displayException(e);
+ }
+ }
+
+ @Override
+ public void gotoMarker(final IMarker marker) {
+ final int rank = marker.getAttribute(IMarker.LOCATION, -1);
+ if (rank != -1) {
+ int index = rank;
+ if (fTable.getData(Key.FILTER_OBJ) != null) {
+ index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+ } else if (rank >= fTable.getItemCount()) {
+ fPendingGotoRank = rank;
+ }
+ fSelectedRank = rank;
+ fTable.setSelection(index + 1); // +1 for header row
+ updateStatusLine(null);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Listeners
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void colorSettingsChanged(final ColorSetting[] colorSettings) {
+ fTable.refresh();
+ }
+
+ // ------------------------------------------------------------------------
+ // Signal handlers
+ // ------------------------------------------------------------------------
+
+ /**
+ * Handler for the trace updated signal
+ *
+ * @param signal
+ * The incoming signal
+ */
+ @TmfSignalHandler
+ public void traceUpdated(final TmfTraceUpdatedSignal signal) {
+ if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
+ return;
+ }
+ // Perform the refresh on the UI thread
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!fTable.isDisposed() && (fTrace != null)) {
+ if (fTable.getData(Key.FILTER_OBJ) == null) {
+ fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
+ if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
+ fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
+ fPendingGotoRank = -1;
+ updateStatusLine(null);
+ }
+ } else {
+ startFilterThread();
+ }
+ }
+ if (!fRawViewer.isDisposed() && (fTrace != null)) {
+ fRawViewer.refreshEventCount();
+ }
+ }
+ });
+ }
+
+ /**
+ * Handler for the time synch signal.
+ *
+ * @param signal
+ * The incoming signal
+ */
+ @TmfSignalHandler
+ public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
+ if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
+
+ // Create a request for one event that will be queued after other ongoing requests. When this request is completed
+ // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
+ // the method fTrace.getRank() from interfering and delaying ongoing requests.
+ final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
+ TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
+
+ TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ }
+
+ @Override
+ public void handleCompleted() {
+ super.handleCompleted();
+ if (fTrace == null) {
+ return;
+ }
+
+ // Verify if the event is within the trace range and adjust if necessary
+ ITmfTimestamp timestamp = ts;
+ if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
+ timestamp = fTrace.getStartTime();
+ }
+ if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
+ timestamp = fTrace.getEndTime();
+ }
+
+ // Get the rank of the selected event in the table
+ final ITmfContext context = fTrace.seekEvent(timestamp);
+ final long rank = context.getRank();
+ context.dispose();
+ fSelectedRank = rank;
+
+ fTable.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // Return if table is disposed
+ if (fTable.isDisposed()) {
+ return;
+ }
+
+ int index = (int) rank;
+ if (fTable.isDisposed()) {
+ return;
+ }
+ if (fTable.getData(Key.FILTER_OBJ) != null) {
+ index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
+ }
+ fTable.setSelection(index + 1); // +1 for header row
+ fRawViewer.selectAndReveal(rank);
+ updateStatusLine(null);
+ }
+ });
+ }
+ };
+
+ ((ITmfEventProvider) fTrace).sendRequest(subRequest);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Error handling
+ // ------------------------------------------------------------------------
+
+ /**
+ * Display an exception in a message box
+ *
+ * @param e the exception
+ */
+ private static void displayException(final Exception e) {
+ final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
+ mb.setText(e.getClass().getSimpleName());
+ mb.setMessage(e.getMessage());
+ mb.open();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void refresh() {
+ fCache.clear();
+ fTable.refresh();
+ fTable.redraw();
+ }
+}