TMF: Add some non null annotations to TmfTimeRange
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / events / TmfEventsTable.java
CommitLineData
db4721fb 1/*******************************************************************************
ed902a2b 2 * Copyright (c) 2010, 2015 Ericsson
db4721fb
PT
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
baafe54c
AM
10 * Francois Chouinard - Initial API and implementation, replaced Table by TmfVirtualTable
11 * Patrick Tasse - Factored out from events view,
12 * Filter implementation (inspired by www.eclipse.org/mat)
ac44ef71 13 * Ansgar Radermacher - Support navigation to model URIs (Bug 396956)
f47ed727 14 * Bernd Hufmann - Updated call site and model URI implementation
baafe54c 15 * Alexandre Montplaisir - Update to new column API
db4721fb
PT
16 *******************************************************************************/
17
2bdf0193 18package org.eclipse.tracecompass.tmf.ui.viewers.events;
db4721fb 19
5db5a3a4
AM
20import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
21
60fb38b8 22import java.io.FileNotFoundException;
db4721fb 23import java.util.ArrayList;
2db176a0 24import java.util.Collection;
db4721fb 25import java.util.HashMap;
baafe54c 26import java.util.LinkedList;
db4721fb 27import java.util.List;
db4721fb
PT
28import java.util.Map.Entry;
29import java.util.regex.Pattern;
30import java.util.regex.PatternSyntaxException;
31
d3de0920
XR
32import org.eclipse.core.commands.Command;
33import org.eclipse.core.commands.ExecutionException;
34import org.eclipse.core.commands.NotEnabledException;
35import org.eclipse.core.commands.NotHandledException;
36import org.eclipse.core.commands.ParameterizedCommand;
37import org.eclipse.core.commands.common.NotDefinedException;
38import org.eclipse.core.expressions.IEvaluationContext;
db4721fb
PT
39import org.eclipse.core.resources.IFile;
40import org.eclipse.core.resources.IMarker;
41import org.eclipse.core.resources.IResource;
60fb38b8 42import org.eclipse.core.resources.IResourceVisitor;
ac44ef71 43import org.eclipse.core.resources.ResourcesPlugin;
db4721fb 44import org.eclipse.core.runtime.CoreException;
ac44ef71 45import org.eclipse.core.runtime.IPath;
db4721fb
PT
46import org.eclipse.core.runtime.IProgressMonitor;
47import org.eclipse.core.runtime.IStatus;
93bfd50a 48import org.eclipse.core.runtime.ListenerList;
ac44ef71 49import org.eclipse.core.runtime.Path;
db4721fb
PT
50import org.eclipse.core.runtime.Status;
51import org.eclipse.core.runtime.jobs.Job;
ac44ef71
AR
52import org.eclipse.emf.common.util.URI;
53import org.eclipse.emf.ecore.EValidator;
46671228 54import org.eclipse.jdt.annotation.NonNull;
db4721fb
PT
55import org.eclipse.jface.action.Action;
56import org.eclipse.jface.action.IAction;
57import org.eclipse.jface.action.IMenuListener;
58import org.eclipse.jface.action.IMenuManager;
3f43dc48 59import org.eclipse.jface.action.IStatusLineManager;
db4721fb
PT
60import org.eclipse.jface.action.MenuManager;
61import org.eclipse.jface.action.Separator;
62import org.eclipse.jface.dialogs.InputDialog;
63import org.eclipse.jface.dialogs.MessageDialog;
64import org.eclipse.jface.resource.FontDescriptor;
65import org.eclipse.jface.resource.JFaceResources;
66import org.eclipse.jface.resource.LocalResourceManager;
ac44ef71 67import org.eclipse.jface.util.OpenStrategy;
93bfd50a 68import org.eclipse.jface.util.SafeRunnable;
60fb38b8 69import org.eclipse.jface.viewers.ArrayContentProvider;
93bfd50a
PT
70import org.eclipse.jface.viewers.ISelection;
71import org.eclipse.jface.viewers.ISelectionChangedListener;
72import org.eclipse.jface.viewers.ISelectionProvider;
60fb38b8 73import org.eclipse.jface.viewers.LabelProvider;
93bfd50a
PT
74import org.eclipse.jface.viewers.SelectionChangedEvent;
75import org.eclipse.jface.viewers.StructuredSelection;
db4721fb 76import org.eclipse.jface.window.Window;
db4721fb
PT
77import org.eclipse.swt.SWT;
78import org.eclipse.swt.custom.SashForm;
79import org.eclipse.swt.custom.TableEditor;
6817308e
PT
80import org.eclipse.swt.events.ControlAdapter;
81import org.eclipse.swt.events.ControlEvent;
db4721fb
PT
82import org.eclipse.swt.events.FocusAdapter;
83import org.eclipse.swt.events.FocusEvent;
84import org.eclipse.swt.events.KeyAdapter;
85import org.eclipse.swt.events.KeyEvent;
86import org.eclipse.swt.events.MouseAdapter;
87import org.eclipse.swt.events.MouseEvent;
88import org.eclipse.swt.events.SelectionAdapter;
89import org.eclipse.swt.events.SelectionEvent;
90import org.eclipse.swt.graphics.Color;
91import org.eclipse.swt.graphics.Font;
92import org.eclipse.swt.graphics.Image;
93import org.eclipse.swt.graphics.Point;
94import org.eclipse.swt.graphics.Rectangle;
95import org.eclipse.swt.layout.FillLayout;
96import org.eclipse.swt.layout.GridData;
97import org.eclipse.swt.layout.GridLayout;
98import org.eclipse.swt.widgets.Composite;
99import org.eclipse.swt.widgets.Display;
100import org.eclipse.swt.widgets.Event;
101import org.eclipse.swt.widgets.Label;
102import org.eclipse.swt.widgets.Listener;
103import org.eclipse.swt.widgets.Menu;
104import org.eclipse.swt.widgets.MessageBox;
105import org.eclipse.swt.widgets.Shell;
106import org.eclipse.swt.widgets.TableColumn;
107import org.eclipse.swt.widgets.TableItem;
108import org.eclipse.swt.widgets.Text;
6cfc180e 109import org.eclipse.tracecompass.common.core.NonNullUtils;
2bdf0193
AM
110import org.eclipse.tracecompass.internal.tmf.core.filter.TmfCollapseFilter;
111import org.eclipse.tracecompass.internal.tmf.ui.Activator;
112import org.eclipse.tracecompass.internal.tmf.ui.Messages;
113import org.eclipse.tracecompass.internal.tmf.ui.commands.ExportToTextCommandHandler;
114import org.eclipse.tracecompass.internal.tmf.ui.dialogs.MultiLineInputDialog;
115import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
116import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
117import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
9447c7ee 118import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
40dfafb3 119import org.eclipse.tracecompass.tmf.core.event.aspect.TmfContentFieldAspect;
2bdf0193
AM
120import org.eclipse.tracecompass.tmf.core.event.collapse.ITmfCollapsibleEvent;
121import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfCallsite;
122import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfModelLookup;
123import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfSourceLookup;
124import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
125import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode;
126import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode;
127import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
128import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
129import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
130import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
131import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal;
132import org.eclipse.tracecompass.tmf.core.signal.TmfEventSearchAppliedSignal;
133import org.eclipse.tracecompass.tmf.core.signal.TmfEventSelectedSignal;
134import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
135import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal;
136import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
137import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
138import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
139import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
140import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
141import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
b04903a2 142import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
2bdf0193
AM
143import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
144import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
145import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
146import org.eclipse.tracecompass.tmf.ui.viewers.events.columns.TmfEventTableColumn;
2bdf0193
AM
147import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSetting;
148import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager;
149import org.eclipse.tracecompass.tmf.ui.views.colors.IColorSettingsListener;
150import org.eclipse.tracecompass.tmf.ui.views.filter.FilterManager;
151import org.eclipse.tracecompass.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
152import org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.TmfVirtualTable;
ac44ef71 153import org.eclipse.ui.IWorkbenchPage;
db4721fb 154import org.eclipse.ui.PlatformUI;
d3de0920 155import org.eclipse.ui.commands.ICommandService;
60fb38b8 156import org.eclipse.ui.dialogs.ListDialog;
d3de0920 157import org.eclipse.ui.handlers.IHandlerService;
ac44ef71 158import org.eclipse.ui.ide.IDE;
db4721fb
PT
159import org.eclipse.ui.ide.IGotoMarker;
160import org.eclipse.ui.themes.ColorUtil;
161
2db176a0
PT
162import com.google.common.base.Joiner;
163import com.google.common.collect.HashMultimap;
baafe54c 164import com.google.common.collect.ImmutableList;
2db176a0
PT
165import com.google.common.collect.Multimap;
166
db4721fb
PT
167/**
168 * The generic TMF Events table
169 *
170 * This is a view that will list events that are read from a trace.
171 *
db4721fb
PT
172 * @author Francois Chouinard
173 * @author Patrick Tasse
174 */
faa38350 175public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
db4721fb 176
cf37ad9f
AM
177 /**
178 * Empty string array, used by {@link #getItemStrings}.
cf37ad9f 179 */
46671228 180 protected static final @NonNull String[] EMPTY_STRING_ARRAY = new String[0];
cf37ad9f 181
baafe54c
AM
182 /**
183 * Empty string
baafe54c
AM
184 */
185 protected static final @NonNull String EMPTY_STRING = ""; //$NON-NLS-1$
186
03142470
BH
187 private static final boolean IS_LINUX = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
188
db4721fb
PT
189 private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
190 "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
191 private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
192 private static final Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(
193 "icons/elcl16/search_match.gif"); //$NON-NLS-1$
194 private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
195 "icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$
196 private static final Image FILTER_IMAGE = Activator.getDefault()
197 .getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$
198 private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$
199 private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;
200 private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
201 private static final int MAX_CACHE_SIZE = 1000;
202
03142470
BH
203 private static final int MARGIN_COLUMN_INDEX = 0;
204 private static final int FILTER_SUMMARY_INDEX = 1;
205 private static final int EVENT_COLUMNS_START_INDEX = MARGIN_COLUMN_INDEX + 1;
206
db4721fb
PT
207 /**
208 * The events table search/filter keys
209 *
210 * @version 1.0
211 * @author Patrick Tasse
212 */
213 public interface Key {
214 /** Search text */
215 String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$
216
217 /** Search object */
218 String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$
219
220 /** Filter text */
221 String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$
222
223 /** Filter object */
224 String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
225
dadf6e1a 226 /** Timestamp */
db4721fb
PT
227 String TIMESTAMP = "$time"; //$NON-NLS-1$
228
229 /** Rank */
230 String RANK = "$rank"; //$NON-NLS-1$
231
db4721fb
PT
232 /** Bookmark indicator */
233 String BOOKMARK = "$bookmark"; //$NON-NLS-1$
c409f16b
AM
234
235 /** Event aspect represented by this column */
236 String ASPECT = "$aspect"; //$NON-NLS-1$
db4721fb
PT
237 }
238
239 /**
240 * The events table search/filter state
241 *
242 * @version 1.0
243 * @author Patrick Tasse
244 */
245 public static enum HeaderState {
246 /** A search is being run */
247 SEARCH,
248
249 /** A filter is applied */
250 FILTER
251 }
252
253 interface Direction {
254 int FORWARD = +1;
255 int BACKWARD = -1;
256 }
257
258 // ------------------------------------------------------------------------
259 // Table data
260 // ------------------------------------------------------------------------
261
a0a88f65 262 /** The virtual event table */
db4721fb 263 protected TmfVirtualTable fTable;
a0a88f65
AM
264
265 private Composite fComposite;
266 private SashForm fSashForm;
267 private TmfRawEventViewer fRawViewer;
268 private ITmfTrace fTrace;
03142470 269 volatile private boolean fPackDone = false;
a0a88f65
AM
270 private HeaderState fHeaderState = HeaderState.SEARCH;
271 private long fSelectedRank = 0;
3f43dc48
PT
272 private ITmfTimestamp fSelectedBeginTimestamp = null;
273 private IStatusLineManager fStatusLineManager = null;
db4721fb
PT
274
275 // Filter data
a0a88f65
AM
276 private long fFilterMatchCount;
277 private long fFilterCheckCount;
278 private FilterThread fFilterThread;
279 private boolean fFilterThreadResume = false;
280 private final Object fFilterSyncObj = new Object();
281 private SearchThread fSearchThread;
282 private final Object fSearchSyncObj = new Object();
db4721fb 283
93bfd50a
PT
284 /**
285 * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
286 *
287 * @see #fireSelectionChanged
288 */
289 private ListenerList selectionChangedListeners = new ListenerList();
290
db4721fb 291 // Bookmark map <Rank, MarkerId>
2db176a0 292 private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
a0a88f65
AM
293 private IFile fBookmarksFile;
294 private long fPendingGotoRank = -1;
db4721fb
PT
295
296 // SWT resources
a0a88f65
AM
297 private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
298 private Color fGrayColor;
299 private Color fGreenColor;
300 private Font fBoldFont;
db4721fb 301
baafe54c 302 private final List<TmfEventTableColumn> fColumns = new LinkedList<>();
db4721fb
PT
303
304 // Event cache
305 private final TmfEventsCache fCache;
306 private boolean fCacheUpdateBusy = false;
307 private boolean fCacheUpdatePending = false;
308 private boolean fCacheUpdateCompleted = false;
309 private final Object fCacheUpdateSyncObj = new Object();
310
311 private boolean fDisposeOnClose;
312
313 // ------------------------------------------------------------------------
a0a88f65 314 // Constructors
db4721fb
PT
315 // ------------------------------------------------------------------------
316
317 /**
baafe54c 318 * Basic constructor, using the default set of columns
db4721fb
PT
319 *
320 * @param parent
321 * The parent composite UI object
322 * @param cacheSize
323 * The size of the event table cache
324 */
325 public TmfEventsTable(final Composite parent, final int cacheSize) {
b04903a2 326 this(parent, cacheSize, TmfTrace.BASE_ASPECTS);
db4721fb
PT
327 }
328
329 /**
baafe54c 330 * Legacy constructor, using ColumnData to define columns
db4721fb
PT
331 *
332 * @param parent
333 * The parent composite UI object
334 * @param cacheSize
335 * The size of the event table cache
336 * @param columnData
40dfafb3 337 * The column data array
baafe54c
AM
338 * @deprecated Deprecated constructor, use
339 * {@link #TmfEventsTable(Composite, int, Collection)}
340 */
341 @Deprecated
342 public TmfEventsTable(final Composite parent, int cacheSize,
2bdf0193 343 final org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
baafe54c
AM
344 /*
345 * We'll do a "best-effort" to keep trace types still using this API to
40dfafb3 346 * keep working, by defining a TmfEventTableColumn for each
baafe54c
AM
347 * ColumnData they passed.
348 */
349 this(parent, cacheSize, convertFromColumnData(columnData));
350 }
351
352 @Deprecated
8192209b 353 private static @NonNull Iterable<ITmfEventAspect> convertFromColumnData(
2bdf0193 354 org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
baafe54c 355
b04903a2 356 ImmutableList.Builder<ITmfEventAspect> builder = new ImmutableList.Builder<>();
2bdf0193 357 for (org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData col : columnData) {
9447c7ee
AM
358 String fieldName = col.header;
359 if (fieldName != null) {
40dfafb3 360 builder.add(new TmfContentFieldAspect(fieldName, fieldName));
baafe54c
AM
361 }
362 }
5db5a3a4 363 return checkNotNull(builder.build());
baafe54c
AM
364 }
365
366 /**
367 * Standard constructor, where we define which columns to use.
368 *
369 * @param parent
370 * The parent composite UI object
371 * @param cacheSize
372 * The size of the event table cache
b04903a2
AM
373 * @param aspects
374 * The event aspects to display in this table. One column per
375 * aspect will be created.
baafe54c
AM
376 * <p>
377 * The iteration order of this collection will correspond to the
b04903a2 378 * initial ordering of the columns in the table.
baafe54c 379 * </p>
db4721fb 380 */
baafe54c 381 public TmfEventsTable(final Composite parent, int cacheSize,
8192209b 382 @NonNull Iterable<ITmfEventAspect> aspects) {
db4721fb
PT
383 super("TmfEventsTable"); //$NON-NLS-1$
384
385 fComposite = new Composite(parent, SWT.NONE);
386 final GridLayout gl = new GridLayout(1, false);
387 gl.marginHeight = 0;
388 gl.marginWidth = 0;
389 gl.verticalSpacing = 0;
390 fComposite.setLayout(gl);
391
392 fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
393 fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
394
395 // Create a virtual table
3f43dc48 396 final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION;
db4721fb
PT
397 fTable = new TmfVirtualTable(fSashForm, style);
398
399 // Set the table layout
400 final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
401 fTable.setLayoutData(layoutData);
402
403 // Some cosmetic enhancements
404 fTable.setHeaderVisible(true);
405 fTable.setLinesVisible(true);
406
baafe54c 407 // Setup the columns
8192209b
AM
408 for (ITmfEventAspect aspect : aspects) {
409 if (aspect != null) {
410 fColumns.add(new TmfEventTableColumn(aspect));
b04903a2 411 }
db4721fb
PT
412 }
413
03142470
BH
414 TmfMarginColumn collapseCol = new TmfMarginColumn();
415 fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
416
baafe54c 417 // Create the UI columns in the table
2fcd5eea
AM
418 for (TmfEventTableColumn col : fColumns) {
419 TableColumn column = fTable.newTableColumn(SWT.LEFT);
420 column.setText(col.getHeaderName());
421 column.setToolTipText(col.getHeaderTooltip());
c409f16b 422 column.setData(Key.ASPECT, col.getEventAspect());
2fcd5eea 423 column.pack();
03142470
BH
424 if (col instanceof TmfMarginColumn) {
425 column.setResizable(false);
6817308e
PT
426 column.addControlListener(new ControlAdapter() {
427 /*
428 * Make sure that the margin column is always first
429 */
430 @Override
431 public void controlMoved(ControlEvent e) {
432 int[] order = fTable.getColumnOrder();
433 if (order[0] == MARGIN_COLUMN_INDEX) {
434 return;
435 }
436 for (int i = order.length - 1; i > 0; i--) {
437 if (order[i] == MARGIN_COLUMN_INDEX) {
438 order[i] = order[i - 1];
439 order[i - 1] = MARGIN_COLUMN_INDEX;
440 }
441 }
442 fTable.setColumnOrder(order);
443 }
444 });
445 } else {
446 column.setMoveable(true);
03142470 447 }
2fcd5eea 448 }
baafe54c 449
db4721fb
PT
450 // Set the frozen row for header row
451 fTable.setFrozenRowCount(1);
452
453 // Create the header row cell editor
454 createHeaderEditor();
455
456 // Handle the table item selection
457 fTable.addSelectionListener(new SelectionAdapter() {
458 @Override
459 public void widgetSelected(final SelectionEvent e) {
3f43dc48
PT
460 if (e.item == null) {
461 return;
462 }
463 updateStatusLine(null);
464 if (fTable.getSelectionIndices().length > 0) {
465 if (e.item.getData(Key.RANK) instanceof Long) {
466 fSelectedRank = (Long) e.item.getData(Key.RANK);
467 fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK));
468 }
469 if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) {
6cfc180e 470 final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP));
3f43dc48
PT
471 if (fTable.getSelectionIndices().length == 1) {
472 fSelectedBeginTimestamp = ts;
473 }
6cfc180e
GB
474 ITmfTimestamp selectedBeginTimestamp = fSelectedBeginTimestamp;
475 if (selectedBeginTimestamp != null) {
476 if (selectedBeginTimestamp.compareTo(ts) <= 0) {
477 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, selectedBeginTimestamp, ts));
3f43dc48 478 if (fTable.getSelectionIndices().length == 2) {
6cfc180e 479 updateStatusLine(ts.getDelta(selectedBeginTimestamp));
3f43dc48
PT
480 }
481 } else {
6cfc180e 482 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, checkNotNull(ts), checkNotNull(fSelectedBeginTimestamp)));
dadf6e1a 483 updateStatusLine(fSelectedBeginTimestamp.getDelta(ts));
3f43dc48 484 }
db4721fb 485 }
3f43dc48
PT
486 } else {
487 if (fTable.getSelectionIndices().length == 1) {
488 fSelectedBeginTimestamp = null;
db4721fb
PT
489 }
490 }
491 }
6b44794a
MK
492 if (e.item.getData() instanceof ITmfEvent) {
493 broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) e.item.getData()));
3f43dc48
PT
494 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(e.item.getData())));
495 } else {
496 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
497 }
db4721fb
PT
498 }
499 });
500
41b5c37f
AM
501 int realCacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
502 realCacheSize = Math.min(realCacheSize, MAX_CACHE_SIZE);
503 fCache = new TmfEventsCache(realCacheSize, this);
db4721fb
PT
504
505 // Handle the table item requests
506 fTable.addListener(SWT.SetData, new Listener() {
507
508 @Override
509 public void handleEvent(final Event event) {
510
511 final TableItem item = (TableItem) event.item;
512 int index = event.index - 1; // -1 for the header row
513
514 if (event.index == 0) {
515 setHeaderRowItemData(item);
516 return;
517 }
518
519 if (fTable.getData(Key.FILTER_OBJ) != null) {
520 if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
521 setFilterStatusRowItemData(item);
522 return;
523 }
524 index = index - 1; // -1 for top filter status row
525 }
526
527 final CachedEvent cachedEvent = fCache.getEvent(index);
528 if (cachedEvent != null) {
03142470 529 setItemData(item, cachedEvent, cachedEvent.rank);
db4721fb
PT
530 return;
531 }
532
533 // Else, fill the cache asynchronously (and off the UI thread)
534 event.doit = false;
535 }
536 });
537
538 fTable.addMouseListener(new MouseAdapter() {
539 @Override
540 public void mouseDoubleClick(final MouseEvent event) {
541 if (event.button != 1) {
542 return;
543 }
544 // Identify the selected row
545 final Point point = new Point(event.x, event.y);
546 final TableItem item = fTable.getItem(point);
547 if (item != null) {
548 final Rectangle imageBounds = item.getImageBounds(0);
549 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
550 if (imageBounds.contains(point)) {
551 final Long rank = (Long) item.getData(Key.RANK);
552 if (rank != null) {
553 toggleBookmark(rank);
554 }
555 }
556 }
557 }
558 });
559
560 final Listener tooltipListener = new Listener () {
561 Shell tooltipShell = null;
562 @Override
563 public void handleEvent(final Event event) {
564 switch (event.type) {
565 case SWT.MouseHover:
566 final TableItem item = fTable.getItem(new Point(event.x, event.y));
567 if (item == null) {
568 return;
569 }
570 final Long rank = (Long) item.getData(Key.RANK);
571 if (rank == null) {
572 return;
573 }
574 final String tooltipText = (String) item.getData(Key.BOOKMARK);
575 final Rectangle bounds = item.getImageBounds(0);
576 bounds.width = BOOKMARK_IMAGE.getBounds().width;
577 if (!bounds.contains(event.x,event.y)) {
578 return;
579 }
580 if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
581 tooltipShell.dispose();
582 }
583 tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
584 tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
585 final FillLayout layout = new FillLayout();
586 layout.marginWidth = 2;
587 tooltipShell.setLayout(layout);
588 final Label label = new Label(tooltipShell, SWT.WRAP);
03142470 589 String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
db4721fb
PT
590 label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
591 label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
592 label.setText(text);
593 label.addListener(SWT.MouseExit, this);
594 label.addListener(SWT.MouseDown, this);
595 label.addListener(SWT.MouseWheel, this);
596 final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
597 /*
598 * Bug in Linux. The coordinates of the event have an origin that excludes the table header but
599 * the method toDisplay() expects coordinates relative to an origin that includes the table header.
600 */
601 int y = event.y;
03142470 602 if (IS_LINUX) {
db4721fb
PT
603 y += fTable.getHeaderHeight();
604 }
605 Point pt = fTable.toDisplay(event.x, y);
606 pt.x += BOOKMARK_IMAGE.getBounds().width;
3be2946f 607 pt.y += item.getBounds().height;
db4721fb
PT
608 tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
609 tooltipShell.setVisible(true);
610 break;
611 case SWT.Dispose:
612 case SWT.KeyDown:
613 case SWT.MouseMove:
614 case SWT.MouseExit:
615 case SWT.MouseDown:
616 case SWT.MouseWheel:
617 if (tooltipShell != null) {
618 tooltipShell.dispose();
619 tooltipShell = null;
620 }
621 break;
622 default:
623 break;
624 }
625 }
626 };
627
628 fTable.addListener(SWT.MouseHover, tooltipListener);
629 fTable.addListener(SWT.Dispose, tooltipListener);
630 fTable.addListener(SWT.KeyDown, tooltipListener);
631 fTable.addListener(SWT.MouseMove, tooltipListener);
632 fTable.addListener(SWT.MouseExit, tooltipListener);
633 fTable.addListener(SWT.MouseDown, tooltipListener);
634 fTable.addListener(SWT.MouseWheel, tooltipListener);
635
636 // Create resources
637 createResources();
638
639 ColorSettingsManager.addColorSettingsListener(this);
640
641 fTable.setItemCount(1); // +1 for header row
642
643 fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
644
645 fRawViewer.addSelectionListener(new Listener() {
646 @Override
647 public void handleEvent(final Event e) {
648 if (e.data instanceof Long) {
649 final long rank = (Long) e.data;
650 int index = (int) rank;
651 if (fTable.getData(Key.FILTER_OBJ) != null) {
652 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
653 }
654 fTable.setSelection(index + 1); // +1 for header row
655 fSelectedRank = rank;
3f43dc48 656 updateStatusLine(null);
1e1bef82 657 } else if (e.data instanceof ITmfLocation) {
db4721fb
PT
658 // DOES NOT WORK: rank undefined in context from seekLocation()
659 // ITmfLocation<?> location = (ITmfLocation<?>) e.data;
660 // TmfContext context = fTrace.seekLocation(location);
661 // fTable.setSelection((int) context.getRank());
662 return;
663 } else {
664 return;
665 }
666 final TableItem[] selection = fTable.getSelection();
667 if ((selection != null) && (selection.length > 0)) {
476a18ba
PT
668 TableItem item = fTable.getSelection()[0];
669 final TmfTimestamp ts = (TmfTimestamp) item.getData(Key.TIMESTAMP);
db4721fb
PT
670 if (ts != null) {
671 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));
672 }
476a18ba
PT
673 if (item.getData() instanceof ITmfEvent) {
674 broadcast(new TmfEventSelectedSignal(TmfEventsTable.this, (ITmfEvent) item.getData()));
675 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, new StructuredSelection(item.getData())));
676 } else {
677 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, StructuredSelection.EMPTY));
678 }
db4721fb
PT
679 }
680 }
681 });
682
683 fSashForm.setWeights(new int[] { 1, 1 });
684 fRawViewer.setVisible(false);
685
686 createPopupMenu();
687 }
688
baafe54c
AM
689 // ------------------------------------------------------------------------
690 // Operations
691 // ------------------------------------------------------------------------
692
a0a88f65
AM
693 /**
694 * Create a pop-up menu.
695 */
db4721fb
PT
696 protected void createPopupMenu() {
697 final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
698 @Override
699 public void run() {
700 fTable.setVisible(true);
701 fSashForm.layout();
702 }
703 };
704
705 final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
706 @Override
707 public void run() {
708 fTable.setVisible(false);
709 fSashForm.layout();
710 }
711 };
712
713 final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {
714 @Override
715 public void run() {
716 fRawViewer.setVisible(true);
717 fSashForm.layout();
718 final int index = fTable.getSelectionIndex();
dadf6e1a 719 if (index >= 1) {
db4721fb
PT
720 fRawViewer.selectAndReveal(index - 1);
721 }
722 }
723 };
724
725 final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {
726 @Override
727 public void run() {
728 fRawViewer.setVisible(false);
729 fSashForm.layout();
730 }
731 };
732
029df6e3 733 final IAction openCallsiteAction = new Action(Messages.TmfEventsTable_OpenSourceCodeActionText) {
60fb38b8
PT
734 @Override
735 public void run() {
736 final TableItem items[] = fTable.getSelection();
737 if (items.length != 1) {
738 return;
739 }
740 final TableItem item = items[0];
741
742 final Object data = item.getData();
f47ed727
BH
743 if (data instanceof ITmfSourceLookup) {
744 ITmfSourceLookup event = (ITmfSourceLookup) data;
745 ITmfCallsite cs = event.getCallsite();
60fb38b8
PT
746 if (cs == null || cs.getFileName() == null) {
747 return;
748 }
749 IMarker marker = null;
750 try {
751 String fileName = cs.getFileName();
03142470 752 final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
507b1336 753 final ArrayList<IFile> files = new ArrayList<>();
60fb38b8
PT
754 ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
755 @Override
756 public boolean visit(IResource resource) throws CoreException {
757 if (resource instanceof IFile && resource.getFullPath().toString().endsWith(trimmedPath)) {
758 files.add((IFile) resource);
759 }
760 return true;
761 }
762 });
763 IFile file = null;
764 if (files.size() > 1) {
765 ListDialog dialog = new ListDialog(getTable().getShell());
766 dialog.setContentProvider(ArrayContentProvider.getInstance());
767 dialog.setLabelProvider(new LabelProvider() {
768 @Override
769 public String getText(Object element) {
770 return ((IFile) element).getFullPath().toString();
771 }
772 });
773 dialog.setInput(files);
029df6e3
JCK
774 dialog.setTitle(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle);
775 dialog.setMessage(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle + '\n' + cs.toString());
60fb38b8
PT
776 dialog.open();
777 Object[] result = dialog.getResult();
778 if (result != null && result.length > 0) {
779 file = (IFile) result[0];
780 }
781 } else if (files.size() == 1) {
782 file = files.get(0);
783 }
784 if (file != null) {
785 marker = file.createMarker(IMarker.MARKER);
786 marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
787 IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
788 marker.delete();
789 } else if (files.size() == 0){
029df6e3 790 displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
60fb38b8 791 }
60fb38b8 792 } catch (CoreException e) {
8e95e814 793 displayException(e);
60fb38b8
PT
794 }
795 }
796 }
797 };
798
799 final IAction openModelAction = new Action(Messages.TmfEventsTable_OpenModelActionText) {
ac44ef71
AR
800 @Override
801 public void run() {
802
803 final TableItem items[] = fTable.getSelection();
804 if (items.length != 1) {
805 return;
806 }
807 final TableItem item = items[0];
808
809 final Object eventData = item.getData();
f47ed727
BH
810 if (eventData instanceof ITmfModelLookup) {
811 String modelURI = ((ITmfModelLookup) eventData).getModelUri();
ac44ef71
AR
812
813 if (modelURI != null) {
814 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
815
816 IFile file = null;
817 final URI uri = URI.createURI(modelURI);
818 if (uri.isPlatformResource()) {
819 IPath path = new Path(uri.toPlatformString(true));
820 file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
821 } else if (uri.isFile() && !uri.isRelative()) {
822 file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
823 new Path(uri.toFileString()));
824 }
825
826 if (file != null) {
827 try {
828 /*
829 * create a temporary validation marker on the
830 * model file, remove it afterwards thus,
831 * navigation works with all model editors
832 * supporting the navigation to a marker
833 */
834 IMarker marker = file.createMarker(EValidator.MARKER);
835 marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
836 marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
837
838 IDE.openEditor(activePage, marker, OpenStrategy.activateOnOpen());
839 marker.delete();
840 }
841 catch (CoreException e) {
842 displayException(e);
843 }
60fb38b8
PT
844 } else {
845 displayException(new FileNotFoundException('\'' + modelURI + '\'' + '\n' + Messages.TmfEventsTable_OpenModelUnsupportedURI));
ac44ef71
AR
846 }
847 }
848 }
849 }
850 };
60fb38b8 851
d3de0920
XR
852 final IAction exportToTextAction = new Action(Messages.TmfEventsTable_Export_to_text) {
853 @Override
854 public void run() {
855 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
346fa221
MAL
856 Object handlerServiceObject = activePage.getActiveEditor().getSite().getService(IHandlerService.class);
857 IHandlerService handlerService = (IHandlerService) handlerServiceObject;
858 Object cmdServiceObject = activePage.getActiveEditor().getSite().getService(ICommandService.class);
859 ICommandService cmdService = (ICommandService) cmdServiceObject;
d3de0920 860 try {
507b1336 861 HashMap<String, Object> parameters = new HashMap<>();
d3de0920 862 Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
03142470
BH
863 ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command, parameters);
864
d3de0920 865 IEvaluationContext context = handlerService.getCurrentState();
6817308e
PT
866 List<TmfEventTableColumn> exportColumns = new ArrayList<>();
867 for (int i : fTable.getColumnOrder()) {
868 // Omit the margin column
869 if (i >= EVENT_COLUMNS_START_INDEX) {
870 exportColumns.add(fColumns.get(i));
871 }
872 }
03142470
BH
873 context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, exportColumns);
874
d3de0920 875 handlerService.executeCommandInContext(cmd, null, context);
23c625f1 876 } catch (ExecutionException | NotDefinedException | NotEnabledException | NotHandledException e) {
d3de0920
XR
877 displayException(e);
878 }
879 }
880 };
881
db4721fb
PT
882 final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
883 @Override
884 public void run() {
885 fHeaderState = HeaderState.SEARCH;
886 fTable.refresh();
887 }
888 };
889
890 final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
891 @Override
892 public void run() {
893 fHeaderState = HeaderState.FILTER;
894 fTable.refresh();
895 }
896 };
897
898 final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
899 @Override
900 public void run() {
901 clearFilters();
902 }
903 };
904
03142470
BH
905 final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
906 @Override
907 public void run() {
908 applyFilter(new TmfCollapseFilter());
909 }
910 };
911
db4721fb 912 class ToggleBookmarkAction extends Action {
0126a8ca 913 Long fRank;
db4721fb 914
0126a8ca 915 public ToggleBookmarkAction(final String text, final Long rank) {
db4721fb
PT
916 super(text);
917 fRank = rank;
918 }
919
920 @Override
921 public void run() {
922 toggleBookmark(fRank);
923 }
924 }
925
926 final MenuManager tablePopupMenu = new MenuManager();
927 tablePopupMenu.setRemoveAllWhenShown(true);
928 tablePopupMenu.addMenuListener(new IMenuListener() {
929 @Override
930 public void menuAboutToShow(final IMenuManager manager) {
931 if (fTable.getSelectionIndex() == 0) {
932 // Right-click on header row
933 if (fHeaderState == HeaderState.FILTER) {
934 tablePopupMenu.add(showSearchBarAction);
935 } else {
936 tablePopupMenu.add(showFilterBarAction);
937 }
938 return;
939 }
940 final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
941 final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
942 if (item != null) {
943 final Rectangle imageBounds = item.getImageBounds(0);
944 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
945 if (point.x <= (imageBounds.x + imageBounds.width)) {
946 // Right-click on left margin
947 final Long rank = (Long) item.getData(Key.RANK);
948 if ((rank != null) && (fBookmarksFile != null)) {
949 if (fBookmarksMap.containsKey(rank)) {
950 tablePopupMenu.add(new ToggleBookmarkAction(
951 Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
952 } else {
953 tablePopupMenu.add(new ToggleBookmarkAction(
954 Messages.TmfEventsTable_AddBookmarkActionText, rank));
955 }
956 }
957 return;
958 }
959 }
60fb38b8 960
db4721fb
PT
961 // Right-click on table
962 if (fTable.isVisible() && fRawViewer.isVisible()) {
963 tablePopupMenu.add(hideTableAction);
964 tablePopupMenu.add(hideRawAction);
965 } else if (!fTable.isVisible()) {
966 tablePopupMenu.add(showTableAction);
967 } else if (!fRawViewer.isVisible()) {
968 tablePopupMenu.add(showRawAction);
969 }
d3de0920 970 tablePopupMenu.add(exportToTextAction);
db4721fb 971 tablePopupMenu.add(new Separator());
60fb38b8 972
ac44ef71 973 if (item != null) {
60fb38b8 974 final Object data = item.getData();
f47ed727
BH
975 Separator separator = null;
976 if (data instanceof ITmfSourceLookup) {
977 ITmfSourceLookup event = (ITmfSourceLookup) data;
60fb38b8
PT
978 if (event.getCallsite() != null) {
979 tablePopupMenu.add(openCallsiteAction);
980 separator = new Separator();
981 }
f47ed727
BH
982 }
983
984 if (data instanceof ITmfModelLookup) {
985 ITmfModelLookup event = (ITmfModelLookup) data;
986 if (event.getModelUri() != null) {
60fb38b8
PT
987 tablePopupMenu.add(openModelAction);
988 separator = new Separator();
989 }
f47ed727 990
60fb38b8
PT
991 if (separator != null) {
992 tablePopupMenu.add(separator);
ac44ef71
AR
993 }
994 }
995 }
60fb38b8 996
03142470
BH
997 // only show collapse filter if at least one trace can be collapsed
998 boolean isCollapsible = false;
999 if (fTrace != null) {
c14c0757 1000 for (ITmfTrace trace : TmfTraceManager.getTraceSet(fTrace)) {
03142470
BH
1001 Class <? extends ITmfEvent> eventClass = trace.getEventType();
1002 isCollapsible = ITmfCollapsibleEvent.class.isAssignableFrom(eventClass);
1003 if (isCollapsible) {
1004 break;
1005 }
1006 }
1007 }
1008
1009 if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
1010 tablePopupMenu.add(collapseAction);
1011 tablePopupMenu.add(new Separator());
1012 }
1013
db4721fb
PT
1014 tablePopupMenu.add(clearFiltersAction);
1015 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
1016 if (savedFilters.length > 0) {
1017 final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
1018 for (final ITmfFilterTreeNode node : savedFilters) {
1019 if (node instanceof TmfFilterNode) {
1020 final TmfFilterNode filter = (TmfFilterNode) node;
1021 subMenu.add(new Action(filter.getFilterName()) {
1022 @Override
1023 public void run() {
1024 applyFilter(filter);
1025 }
1026 });
1027 }
1028 }
1029 tablePopupMenu.add(subMenu);
1030 }
1031 appendToTablePopupMenu(tablePopupMenu, item);
1032 }
1033 });
1034
1035 final MenuManager rawViewerPopupMenu = new MenuManager();
1036 rawViewerPopupMenu.setRemoveAllWhenShown(true);
1037 rawViewerPopupMenu.addMenuListener(new IMenuListener() {
1038 @Override
1039 public void menuAboutToShow(final IMenuManager manager) {
1040 if (fTable.isVisible() && fRawViewer.isVisible()) {
1041 rawViewerPopupMenu.add(hideTableAction);
1042 rawViewerPopupMenu.add(hideRawAction);
1043 } else if (!fTable.isVisible()) {
1044 rawViewerPopupMenu.add(showTableAction);
1045 } else if (!fRawViewer.isVisible()) {
1046 rawViewerPopupMenu.add(showRawAction);
1047 }
1048 appendToRawPopupMenu(tablePopupMenu);
1049 }
1050 });
1051
1052 Menu menu = tablePopupMenu.createContextMenu(fTable);
1053 fTable.setMenu(menu);
1054
1055 menu = rawViewerPopupMenu.createContextMenu(fRawViewer);
1056 fRawViewer.setMenu(menu);
1057 }
1058
f8177ba2 1059
a0a88f65
AM
1060 /**
1061 * Append an item to the event table's pop-up menu.
1062 *
1063 * @param tablePopupMenu
1064 * The menu manager
1065 * @param selectedItem
1066 * The item to append
1067 */
db4721fb
PT
1068 protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
1069 // override to append more actions
1070 }
1071
a0a88f65
AM
1072 /**
1073 * Append an item to the raw viewer's pop-up menu.
1074 *
1075 * @param rawViewerPopupMenu
1076 * The menu manager
1077 */
db4721fb
PT
1078 protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
1079 // override to append more actions
1080 }
1081
1082 @Override
1083 public void dispose() {
1084 stopSearchThread();
1085 stopFilterThread();
1086 ColorSettingsManager.removeColorSettingsListener(this);
1087 fComposite.dispose();
1088 if ((fTrace != null) && fDisposeOnClose) {
1089 fTrace.dispose();
1090 }
1091 fResourceManager.dispose();
3a97628a 1092 fRawViewer.dispose();
db4721fb
PT
1093 super.dispose();
1094 }
1095
1096 /**
1097 * Assign a layout data object to this view.
1098 *
1099 * @param layoutData
1100 * The layout data to assign
1101 */
1102 public void setLayoutData(final Object layoutData) {
1103 fComposite.setLayoutData(layoutData);
1104 }
1105
1106 /**
1107 * Get the virtual table contained in this event table.
1108 *
1109 * @return The TMF virtual table
1110 */
1111 public TmfVirtualTable getTable() {
1112 return fTable;
1113 }
1114
1115 /**
1116 * @param columnData
baafe54c
AM
1117 * columnData
1118 * @deprecated The column headers are now set at the constructor, this
1119 * shouldn't be called anymore.
db4721fb 1120 */
baafe54c 1121 @Deprecated
2bdf0193 1122 protected void setColumnHeaders(final org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData [] columnData) {
baafe54c 1123 /* No-op */
db4721fb
PT
1124 }
1125
a0a88f65
AM
1126 /**
1127 * Set a table item's data.
1128 *
1129 * @param item
1130 * The item to set
1131 * @param event
1132 * Which trace event to link with this entry
1133 * @param rank
1134 * Which rank this event has in the trace/experiment
1135 */
db4721fb 1136 protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
03142470 1137 String[] itemStrings = getItemStrings(fColumns, event);
32528869
BH
1138
1139 // Get the actual ITmfEvent from the CachedEvent
1140 ITmfEvent tmfEvent = event;
1141 if (event instanceof CachedEvent) {
1142 tmfEvent = ((CachedEvent) event).event;
1143 }
03142470 1144 item.setText(itemStrings);
32528869
BH
1145 item.setData(tmfEvent);
1146 item.setData(Key.TIMESTAMP, new TmfTimestamp(tmfEvent.getTimestamp()));
db4721fb
PT
1147 item.setData(Key.RANK, rank);
1148
2db176a0
PT
1149 final Collection<Long> markerIds = fBookmarksMap.get(rank);
1150 if (!markerIds.isEmpty()) {
1151 Joiner joiner = Joiner.on("\n -").skipNulls(); //$NON-NLS-1$
1152 List<Object> parts = new ArrayList<>();
1153 if (markerIds.size() > 1) {
1154 parts.add(Messages.TmfEventsTable_MultipleBookmarksToolTip);
1155 }
db4721fb 1156 try {
2db176a0
PT
1157 for (long markerId : markerIds) {
1158 final IMarker marker = fBookmarksFile.findMarker(markerId);
1159 parts.add(marker.getAttribute(IMarker.MESSAGE));
1160 }
1161 } catch (CoreException e) {
db4721fb
PT
1162 displayException(e);
1163 }
2db176a0 1164 item.setData(Key.BOOKMARK, joiner.join(parts));
db4721fb
PT
1165 } else {
1166 item.setData(Key.BOOKMARK, null);
1167 }
1168
1169 boolean searchMatch = false;
1170 boolean searchNoMatch = false;
1171 final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
1172 if (searchFilter != null) {
32528869 1173 if (searchFilter.matches(tmfEvent)) {
db4721fb
PT
1174 searchMatch = true;
1175 } else {
1176 searchNoMatch = true;
1177 }
1178 }
1179
32528869 1180 final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(tmfEvent);
db4721fb
PT
1181 if (searchNoMatch) {
1182 item.setForeground(colorSetting.getDimmedForegroundColor());
1183 item.setBackground(colorSetting.getDimmedBackgroundColor());
1184 } else {
1185 item.setForeground(colorSetting.getForegroundColor());
1186 item.setBackground(colorSetting.getBackgroundColor());
1187 }
1188
1189 if (searchMatch) {
2db176a0 1190 if (!markerIds.isEmpty()) {
db4721fb
PT
1191 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
1192 } else {
1193 item.setImage(SEARCH_MATCH_IMAGE);
1194 }
2db176a0 1195 } else if (!markerIds.isEmpty()) {
db4721fb
PT
1196 item.setImage(BOOKMARK_IMAGE);
1197 } else {
1198 item.setImage((Image) null);
1199 }
03142470
BH
1200
1201 if ((itemStrings[MARGIN_COLUMN_INDEX] != null) && !itemStrings[MARGIN_COLUMN_INDEX].isEmpty()) {
1202 packMarginColumn();
1203 }
db4721fb
PT
1204 }
1205
a0a88f65
AM
1206 /**
1207 * Set the item data of the header row.
1208 *
1209 * @param item
1210 * The item to use as table header
1211 */
db4721fb
PT
1212 protected void setHeaderRowItemData(final TableItem item) {
1213 String txtKey = null;
1214 if (fHeaderState == HeaderState.SEARCH) {
1215 item.setImage(SEARCH_IMAGE);
1216 txtKey = Key.SEARCH_TXT;
1217 } else if (fHeaderState == HeaderState.FILTER) {
1218 item.setImage(FILTER_IMAGE);
1219 txtKey = Key.FILTER_TXT;
1220 }
1221 item.setForeground(fGrayColor);
03142470
BH
1222 // Ignore collapse and image column
1223 for (int i = EVENT_COLUMNS_START_INDEX; i < fTable.getColumns().length; i++) {
db4721fb
PT
1224 final TableColumn column = fTable.getColumns()[i];
1225 final String filter = (String) column.getData(txtKey);
1226 if (filter == null) {
1227 if (fHeaderState == HeaderState.SEARCH) {
1228 item.setText(i, SEARCH_HINT);
1229 } else if (fHeaderState == HeaderState.FILTER) {
1230 item.setText(i, FILTER_HINT);
1231 }
1232 item.setForeground(i, fGrayColor);
1233 item.setFont(i, fTable.getFont());
1234 } else {
1235 item.setText(i, filter);
1236 item.setForeground(i, fGreenColor);
1237 item.setFont(i, fBoldFont);
1238 }
1239 }
1240 }
1241
a0a88f65
AM
1242 /**
1243 * Set the item data of the "filter status" row.
1244 *
1245 * @param item
1246 * The item to use as filter status row
1247 */
db4721fb
PT
1248 protected void setFilterStatusRowItemData(final TableItem item) {
1249 for (int i = 0; i < fTable.getColumns().length; i++) {
03142470 1250 if (i == MARGIN_COLUMN_INDEX) {
db4721fb
PT
1251 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
1252 item.setImage(FILTER_IMAGE);
1253 } else {
1254 item.setImage(STOP_IMAGE);
1255 }
03142470
BH
1256 }
1257
1258 if (i == FILTER_SUMMARY_INDEX) {
1259 item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
db4721fb 1260 } else {
03142470 1261 item.setText(i, EMPTY_STRING);
db4721fb
PT
1262 }
1263 }
93bfd50a 1264 item.setData(null);
db4721fb
PT
1265 item.setData(Key.TIMESTAMP, null);
1266 item.setData(Key.RANK, null);
1267 item.setForeground(null);
1268 item.setBackground(null);
1269 }
1270
a0a88f65
AM
1271 /**
1272 * Create an editor for the header.
1273 */
db4721fb
PT
1274 protected void createHeaderEditor() {
1275 final TableEditor tableEditor = fTable.createTableEditor();
1276 tableEditor.horizontalAlignment = SWT.LEFT;
1277 tableEditor.verticalAlignment = SWT.CENTER;
1278 tableEditor.grabHorizontal = true;
1279 tableEditor.minimumWidth = 50;
1280
1281 // Handle the header row selection
1282 fTable.addMouseListener(new MouseAdapter() {
1283 int columnIndex;
1284 TableColumn column;
1285 TableItem item;
1286
1287 @Override
1288 public void mouseDown(final MouseEvent event) {
1289 if (event.button != 1) {
1290 return;
1291 }
1292 // Identify the selected row
1293 final Point point = new Point(event.x, event.y);
1294 item = fTable.getItem(point);
1295
1296 // Header row selected
1297 if ((item != null) && (fTable.indexOf(item) == 0)) {
1298
1299 // Icon selected
1300 if (item.getImageBounds(0).contains(point)) {
1301 if (fHeaderState == HeaderState.SEARCH) {
1302 fHeaderState = HeaderState.FILTER;
1303 } else if (fHeaderState == HeaderState.FILTER) {
1304 fHeaderState = HeaderState.SEARCH;
1305 }
3f43dc48 1306 fTable.setSelection(0);
db4721fb
PT
1307 fTable.refresh();
1308 return;
1309 }
1310
1311 // Identify the selected column
1312 columnIndex = -1;
1313 for (int i = 0; i < fTable.getColumns().length; i++) {
1314 final Rectangle rect = item.getBounds(i);
1315 if (rect.contains(point)) {
1316 columnIndex = i;
1317 break;
1318 }
1319 }
1320
1321 if (columnIndex == -1) {
1322 return;
1323 }
1324
1325 column = fTable.getColumns()[columnIndex];
1326
1327 String txtKey = null;
1328 if (fHeaderState == HeaderState.SEARCH) {
1329 txtKey = Key.SEARCH_TXT;
1330 } else if (fHeaderState == HeaderState.FILTER) {
1331 txtKey = Key.FILTER_TXT;
1332 }
1333
1334 // The control that will be the editor must be a child of the Table
1335 final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
1336 final String headerString = (String) column.getData(txtKey);
1337 if (headerString != null) {
1338 newEditor.setText(headerString);
1339 }
1340 newEditor.addFocusListener(new FocusAdapter() {
1341 @Override
1342 public void focusLost(final FocusEvent e) {
1343 final boolean changed = updateHeader(newEditor.getText());
1344 if (changed) {
1345 applyHeader();
1346 }
1347 }
1348 });
1349 newEditor.addKeyListener(new KeyAdapter() {
1350 @Override
1351 public void keyPressed(final KeyEvent e) {
1352 if (e.character == SWT.CR) {
1353 updateHeader(newEditor.getText());
1354 applyHeader();
d81b17ea
MAL
1355
1356 // Set focus on the table so that the next carriage return goes to the next result
1357 TmfEventsTable.this.getTable().setFocus();
db4721fb
PT
1358 } else if (e.character == SWT.ESC) {
1359 tableEditor.getEditor().dispose();
1360 }
1361 }
1362 });
1363 newEditor.selectAll();
1364 newEditor.setFocus();
1365 tableEditor.setEditor(newEditor, item, columnIndex);
1366 }
1367 }
1368
1369 /*
1370 * returns true is value was changed
1371 */
1372 private boolean updateHeader(final String text) {
1373 String objKey = null;
1374 String txtKey = null;
1375 if (fHeaderState == HeaderState.SEARCH) {
1376 objKey = Key.SEARCH_OBJ;
1377 txtKey = Key.SEARCH_TXT;
1378 } else if (fHeaderState == HeaderState.FILTER) {
1379 objKey = Key.FILTER_OBJ;
1380 txtKey = Key.FILTER_TXT;
1381 }
1382 if (text.trim().length() > 0) {
1383 try {
1384 final String regex = TmfFilterMatchesNode.regexFix(text);
1385 Pattern.compile(regex);
1386 if (regex.equals(column.getData(txtKey))) {
1387 tableEditor.getEditor().dispose();
1388 return false;
1389 }
ec34bf48 1390 final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
c409f16b
AM
1391 ITmfEventAspect aspect = (ITmfEventAspect) column.getData(Key.ASPECT);
1392 filter.setEventAspect(aspect);
db4721fb
PT
1393 filter.setRegex(regex);
1394 column.setData(objKey, filter);
1395 column.setData(txtKey, regex);
1396 } catch (final PatternSyntaxException ex) {
1397 tableEditor.getEditor().dispose();
1398 MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1399 ex.getDescription(), ex.getMessage());
1400 return false;
1401 }
1402 } else {
1403 if (column.getData(txtKey) == null) {
1404 tableEditor.getEditor().dispose();
1405 return false;
1406 }
1407 column.setData(objKey, null);
1408 column.setData(txtKey, null);
1409 }
1410 return true;
1411 }
1412
1413 private void applyHeader() {
1414 if (fHeaderState == HeaderState.SEARCH) {
1415 stopSearchThread();
1416 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
3dca7aa5
AM
1417 for (final TableColumn col : fTable.getColumns()) {
1418 final Object filterObj = col.getData(Key.SEARCH_OBJ);
db4721fb
PT
1419 if (filterObj instanceof ITmfFilterTreeNode) {
1420 filter.addChild((ITmfFilterTreeNode) filterObj);
1421 }
1422 }
1423 if (filter.getChildrenCount() > 0) {
1424 fTable.setData(Key.SEARCH_OBJ, filter);
1425 fTable.refresh();
1426 searchNext();
1427 fireSearchApplied(filter);
1428 } else {
1429 fTable.setData(Key.SEARCH_OBJ, null);
1430 fTable.refresh();
1431 fireSearchApplied(null);
1432 }
1433 } else if (fHeaderState == HeaderState.FILTER) {
1434 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
3dca7aa5
AM
1435 for (final TableColumn col : fTable.getColumns()) {
1436 final Object filterObj = col.getData(Key.FILTER_OBJ);
db4721fb
PT
1437 if (filterObj instanceof ITmfFilterTreeNode) {
1438 filter.addChild((ITmfFilterTreeNode) filterObj);
1439 }
1440 }
1441 if (filter.getChildrenCount() > 0) {
1442 applyFilter(filter);
1443 } else {
1444 clearFilters();
1445 }
1446 }
1447
1448 tableEditor.getEditor().dispose();
1449 }
1450 });
1451
1452 fTable.addKeyListener(new KeyAdapter() {
1453 @Override
1454 public void keyPressed(final KeyEvent e) {
1455 e.doit = false;
1456 if (e.character == SWT.ESC) {
1457 stopFilterThread();
1458 stopSearchThread();
1459 fTable.refresh();
1460 } else if (e.character == SWT.DEL) {
1461 if (fHeaderState == HeaderState.SEARCH) {
1462 stopSearchThread();
1463 for (final TableColumn column : fTable.getColumns()) {
1464 column.setData(Key.SEARCH_OBJ, null);
1465 column.setData(Key.SEARCH_TXT, null);
1466 }
1467 fTable.setData(Key.SEARCH_OBJ, null);
1468 fTable.refresh();
1469 fireSearchApplied(null);
1470 } else if (fHeaderState == HeaderState.FILTER) {
1471 clearFilters();
1472 }
1473 } else if (e.character == SWT.CR) {
1474 if ((e.stateMask & SWT.SHIFT) == 0) {
1475 searchNext();
1476 } else {
1477 searchPrevious();
1478 }
1479 }
1480 }
1481 });
1482 }
1483
a0a88f65
AM
1484 /**
1485 * Send an event indicating a filter has been applied.
1486 *
1487 * @param filter
1488 * The filter that was just applied
1489 */
db4721fb 1490 protected void fireFilterApplied(final ITmfFilter filter) {
faa38350 1491 broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
db4721fb
PT
1492 }
1493
a0a88f65
AM
1494 /**
1495 * Send an event indicating that a search has been applied.
1496 *
1497 * @param filter
1498 * The search filter that was just applied
1499 */
db4721fb 1500 protected void fireSearchApplied(final ITmfFilter filter) {
faa38350 1501 broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
db4721fb
PT
1502 }
1503
a0a88f65
AM
1504 /**
1505 * Start the filtering thread.
1506 */
db4721fb
PT
1507 protected void startFilterThread() {
1508 synchronized (fFilterSyncObj) {
1509 final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1510 if (fFilterThread == null || fFilterThread.filter != filter) {
1511 if (fFilterThread != null) {
1512 fFilterThread.cancel();
1513 fFilterThreadResume = false;
1514 }
1515 fFilterThread = new FilterThread(filter);
1516 fFilterThread.start();
1517 } else {
1518 fFilterThreadResume = true;
1519 }
1520 }
1521 }
1522
a0a88f65
AM
1523 /**
1524 * Stop the filtering thread.
1525 */
db4721fb
PT
1526 protected void stopFilterThread() {
1527 synchronized (fFilterSyncObj) {
1528 if (fFilterThread != null) {
1529 fFilterThread.cancel();
1530 fFilterThread = null;
1531 fFilterThreadResume = false;
1532 }
1533 }
1534 }
1535
1536 /**
a0a88f65
AM
1537 * Apply a filter.
1538 *
1539 * @param filter
1540 * The filter to apply
db4721fb
PT
1541 */
1542 protected void applyFilter(ITmfFilter filter) {
f29f8868
BH
1543 stopFilterThread();
1544 stopSearchThread();
db4721fb
PT
1545 fFilterMatchCount = 0;
1546 fFilterCheckCount = 0;
1547 fCache.applyFilter(filter);
1548 fTable.clearAll();
1549 fTable.setData(Key.FILTER_OBJ, filter);
1550 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1551 startFilterThread();
1552 fireFilterApplied(filter);
1553 }
1554
a0a88f65
AM
1555 /**
1556 * Clear all currently active filters.
1557 */
db4721fb
PT
1558 protected void clearFilters() {
1559 if (fTable.getData(Key.FILTER_OBJ) == null) {
1560 return;
1561 }
1562 stopFilterThread();
1563 stopSearchThread();
1564 fCache.clearFilter();
1565 fTable.clearAll();
1566 for (final TableColumn column : fTable.getColumns()) {
1567 column.setData(Key.FILTER_OBJ, null);
1568 column.setData(Key.FILTER_TXT, null);
1569 }
1570 fTable.setData(Key.FILTER_OBJ, null);
1571 if (fTrace != null) {
1572 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1573 } else {
1574 fTable.setItemCount(1); // +1 for header row
1575 }
1576 fFilterMatchCount = 0;
1577 fFilterCheckCount = 0;
1578 if (fSelectedRank >= 0) {
1579 fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
1580 } else {
1581 fTable.setSelection(0);
1582 }
1583 fireFilterApplied(null);
3f43dc48 1584 updateStatusLine(null);
03142470
BH
1585
1586 // Set original width
1587 fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
1588 packMarginColumn();
db4721fb
PT
1589 }
1590
a0a88f65
AM
1591 /**
1592 * Wrapper Thread object for the filtering thread.
1593 */
db4721fb
PT
1594 protected class FilterThread extends Thread {
1595 private final ITmfFilterTreeNode filter;
fd3f1eff 1596 private TmfEventRequest request;
db4721fb
PT
1597 private boolean refreshBusy = false;
1598 private boolean refreshPending = false;
1599 private final Object syncObj = new Object();
1600
a0a88f65
AM
1601 /**
1602 * Constructor.
1603 *
1604 * @param filter
1605 * The filter this thread will be processing
1606 */
db4721fb
PT
1607 public FilterThread(final ITmfFilterTreeNode filter) {
1608 super("Filter Thread"); //$NON-NLS-1$
1609 this.filter = filter;
1610 }
1611
1612 @Override
1613 public void run() {
1614 if (fTrace == null) {
1615 return;
1616 }
1617 final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
1618 if (nbRequested <= 0) {
1619 return;
1620 }
fd3f1eff
AM
1621 request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
1622 (int) fFilterCheckCount, nbRequested, ExecutionType.BACKGROUND) {
db4721fb
PT
1623 @Override
1624 public void handleData(final ITmfEvent event) {
1625 super.handleData(event);
1626 if (request.isCancelled()) {
1627 return;
1628 }
03142470 1629 boolean refresh = false;
db4721fb
PT
1630 if (filter.matches(event)) {
1631 final long rank = fFilterCheckCount;
1632 final int index = (int) fFilterMatchCount;
1633 fFilterMatchCount++;
bd54d363 1634 fCache.storeEvent(event, rank, index);
03142470
BH
1635 refresh = true;
1636 } else {
1637 if (filter instanceof TmfCollapseFilter) {
1638 fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
1639 }
1640 }
1641
1642 if (refresh || (fFilterCheckCount % 100) == 0) {
db4721fb
PT
1643 refreshTable();
1644 }
1645 fFilterCheckCount++;
1646 }
1647 };
fd3f1eff 1648 ((ITmfEventProvider) fTrace).sendRequest(request);
db4721fb
PT
1649 try {
1650 request.waitForCompletion();
1651 } catch (final InterruptedException e) {
1652 }
1653 refreshTable();
1654 synchronized (fFilterSyncObj) {
1655 fFilterThread = null;
1656 if (fFilterThreadResume) {
1657 fFilterThreadResume = false;
1658 fFilterThread = new FilterThread(filter);
1659 fFilterThread.start();
1660 }
1661 }
1662 }
1663
a0a88f65
AM
1664 /**
1665 * Refresh the filter.
1666 */
db4721fb
PT
1667 public void refreshTable() {
1668 synchronized (syncObj) {
1669 if (refreshBusy) {
1670 refreshPending = true;
1671 return;
1672 }
1673 refreshBusy = true;
1674 }
1675 Display.getDefault().asyncExec(new Runnable() {
1676 @Override
1677 public void run() {
1678 if (request.isCancelled()) {
1679 return;
1680 }
1681 if (fTable.isDisposed()) {
1682 return;
1683 }
1684 fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
1685 fTable.refresh();
1686 synchronized (syncObj) {
1687 refreshBusy = false;
1688 if (refreshPending) {
1689 refreshPending = false;
1690 refreshTable();
1691 }
1692 }
1693 }
1694 });
1695 }
1696
a0a88f65
AM
1697 /**
1698 * Cancel this filtering thread.
1699 */
db4721fb
PT
1700 public void cancel() {
1701 if (request != null) {
1702 request.cancel();
1703 }
1704 }
1705 }
1706
a0a88f65
AM
1707 /**
1708 * Go to the next item of a search.
1709 */
db4721fb
PT
1710 protected void searchNext() {
1711 synchronized (fSearchSyncObj) {
1712 if (fSearchThread != null) {
1713 return;
1714 }
1715 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1716 if (searchFilter == null) {
1717 return;
1718 }
1719 final int selectionIndex = fTable.getSelectionIndex();
1720 int startIndex;
1721 if (selectionIndex > 0) {
1722 startIndex = selectionIndex; // -1 for header row, +1 for next event
1723 } else {
1724 // header row is selected, start at top event
1725 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
1726 }
1727 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 1728 if (eventFilter != null) {
db4721fb
PT
1729 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
1730 }
1731 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
1732 fSearchThread.schedule();
1733 }
1734 }
1735
a0a88f65
AM
1736 /**
1737 * Go to the previous item of a search.
1738 */
db4721fb
PT
1739 protected void searchPrevious() {
1740 synchronized (fSearchSyncObj) {
1741 if (fSearchThread != null) {
1742 return;
1743 }
1744 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1745 if (searchFilter == null) {
1746 return;
1747 }
1748 final int selectionIndex = fTable.getSelectionIndex();
1749 int startIndex;
1750 if (selectionIndex > 0) {
1751 startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
1752 } else {
1753 // header row is selected, start at precedent of top event
1754 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
1755 }
1756 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 1757 if (eventFilter != null) {
db4721fb
PT
1758 startIndex = startIndex - 1; // -1 for top filter status row
1759 }
1760 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
1761 fSearchThread.schedule();
1762 }
1763 }
1764
a0a88f65
AM
1765 /**
1766 * Stop the search thread.
1767 */
db4721fb
PT
1768 protected void stopSearchThread() {
1769 fPendingGotoRank = -1;
1770 synchronized (fSearchSyncObj) {
1771 if (fSearchThread != null) {
1772 fSearchThread.cancel();
1773 fSearchThread = null;
1774 }
1775 }
1776 }
1777
a0a88f65
AM
1778 /**
1779 * Wrapper for the search thread.
1780 */
db4721fb 1781 protected class SearchThread extends Job {
a0a88f65
AM
1782
1783 private ITmfFilterTreeNode searchFilter;
1784 private ITmfFilterTreeNode eventFilter;
1785 private int startIndex;
1786 private int direction;
1787 private long rank;
1788 private long foundRank = -1;
fd3f1eff 1789 private TmfEventRequest request;
ae3ffd37 1790 private ITmfTimestamp foundTimestamp = null;
db4721fb 1791
a0a88f65
AM
1792 /**
1793 * Constructor.
1794 *
1795 * @param searchFilter
1796 * The search filter
1797 * @param eventFilter
1798 * The event filter
1799 * @param startIndex
1800 * The index at which we should start searching
1801 * @param currentRank
1802 * The current rank
1803 * @param direction
1804 * In which direction should we search, forward or backwards
1805 */
1806 public SearchThread(final ITmfFilterTreeNode searchFilter,
1807 final ITmfFilterTreeNode eventFilter, final int startIndex,
db4721fb
PT
1808 final long currentRank, final int direction) {
1809 super(Messages.TmfEventsTable_SearchingJobName);
1810 this.searchFilter = searchFilter;
1811 this.eventFilter = eventFilter;
1812 this.startIndex = startIndex;
1813 this.rank = currentRank;
1814 this.direction = direction;
1815 }
1816
1817 @Override
1818 protected IStatus run(final IProgressMonitor monitor) {
1819 if (fTrace == null) {
1820 return Status.OK_STATUS;
1821 }
1822 final Display display = Display.getDefault();
1823 if (startIndex < 0) {
1824 rank = (int) fTrace.getNbEvents() - 1;
1825 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
1826 rank = 0;
1827 } else {
1828 int idx = startIndex;
1829 while (foundRank == -1) {
1830 final CachedEvent event = fCache.peekEvent(idx);
1831 if (event == null) {
1832 break;
1833 }
1834 rank = event.rank;
1835 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
1836 foundRank = event.rank;
ae3ffd37 1837 foundTimestamp = event.event.getTimestamp();
db4721fb
PT
1838 break;
1839 }
1840 if (direction == Direction.FORWARD) {
1841 idx++;
1842 } else {
1843 idx--;
1844 }
1845 }
1846 if (foundRank == -1) {
1847 if (direction == Direction.FORWARD) {
1848 rank++;
1849 if (rank > (fTrace.getNbEvents() - 1)) {
1850 rank = 0;
1851 }
1852 } else {
1853 rank--;
1854 if (rank < 0) {
1855 rank = (int) fTrace.getNbEvents() - 1;
1856 }
1857 }
1858 }
1859 }
1860 final int startRank = (int) rank;
1861 boolean wrapped = false;
1862 while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
1863 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1864 if (direction == Direction.BACKWARD) {
1865 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1866 }
fd3f1eff
AM
1867 request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
1868 (int) rank, nbRequested, ExecutionType.BACKGROUND) {
db4721fb
PT
1869 long currentRank = rank;
1870
1871 @Override
1872 public void handleData(final ITmfEvent event) {
1873 super.handleData(event);
1874 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
1875 foundRank = currentRank;
ae3ffd37 1876 foundTimestamp = event.getTimestamp();
db4721fb
PT
1877 if (direction == Direction.FORWARD) {
1878 done();
1879 return;
1880 }
1881 }
1882 currentRank++;
1883 }
1884 };
fd3f1eff 1885 ((ITmfEventProvider) fTrace).sendRequest(request);
db4721fb
PT
1886 try {
1887 request.waitForCompletion();
1888 if (request.isCancelled()) {
1889 return Status.OK_STATUS;
1890 }
1891 } catch (final InterruptedException e) {
1892 synchronized (fSearchSyncObj) {
1893 fSearchThread = null;
1894 }
1895 return Status.OK_STATUS;
1896 }
1897 if (foundRank == -1) {
1898 if (direction == Direction.FORWARD) {
1899 if (rank == 0) {
1900 synchronized (fSearchSyncObj) {
1901 fSearchThread = null;
1902 }
1903 return Status.OK_STATUS;
1904 }
1905 nbRequested = (int) rank;
1906 rank = 0;
1907 wrapped = true;
1908 } else {
1909 rank--;
1910 if (rank < 0) {
1911 rank = (int) fTrace.getNbEvents() - 1;
1912 wrapped = true;
1913 }
1914 if ((rank <= startRank) && wrapped) {
1915 synchronized (fSearchSyncObj) {
1916 fSearchThread = null;
1917 }
1918 return Status.OK_STATUS;
1919 }
1920 }
1921 }
1922 }
1923 int index = (int) foundRank;
1924 if (eventFilter != null) {
1925 index = fCache.getFilteredEventIndex(foundRank);
1926 }
1927 final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1928
1929 display.asyncExec(new Runnable() {
1930 @Override
1931 public void run() {
1932 if (monitor.isCanceled()) {
1933 return;
1934 }
1935 if (fTable.isDisposed()) {
1936 return;
1937 }
1938 fTable.setSelection(selection);
1939 fSelectedRank = foundRank;
ae3ffd37
PT
1940 fRawViewer.selectAndReveal(fSelectedRank);
1941 if (foundTimestamp != null) {
1942 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, foundTimestamp));
1943 }
a56ec2b8 1944 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
db4721fb
PT
1945 synchronized (fSearchSyncObj) {
1946 fSearchThread = null;
1947 }
3f43dc48 1948 updateStatusLine(null);
db4721fb
PT
1949 }
1950 });
1951 return Status.OK_STATUS;
1952 }
1953
1954 @Override
1955 protected void canceling() {
1956 request.cancel();
1957 synchronized (fSearchSyncObj) {
1958 fSearchThread = null;
1959 }
1960 }
1961 }
1962
a0a88f65
AM
1963 /**
1964 * Create the resources.
1965 */
db4721fb
PT
1966 protected void createResources() {
1967 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
1968 .getForeground().getRGB()));
1969 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1970 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1971 }
1972
a0a88f65
AM
1973 /**
1974 * Pack the columns.
1975 */
db4721fb
PT
1976 protected void packColumns() {
1977 if (fPackDone) {
1978 return;
1979 }
d3bc98ee 1980 fTable.setRedraw(false);
03142470
BH
1981 try {
1982 TableColumn tableColumns[] = fTable.getColumns();
1983 for (int i = 0; i < tableColumns.length; i++) {
1984 final TableColumn column = tableColumns[i];
1985 packSingleColumn(i, column);
1986 }
1987 } finally {
1988 // Make sure that redraw is always enabled.
1989 fTable.setRedraw(true);
1990 }
1991 fPackDone = true;
1992 }
d3bc98ee 1993
f29f8868 1994
03142470
BH
1995 private void packMarginColumn() {
1996 TableColumn[] columns = fTable.getColumns();
1997 if (columns.length > 0) {
1998 packSingleColumn(0, columns[0]);
1999 }
2000 }
f29f8868 2001
03142470
BH
2002 private void packSingleColumn(int i, final TableColumn column) {
2003 final int headerWidth = column.getWidth();
2004 column.pack();
2005 // Workaround for Linux which doesn't consider the image width of
2006 // search/filter row in TableColumn.pack() after having executed
2007 // TableItem.setImage((Image)null) for other rows than search/filter row.
2008 boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
2009 if (IS_LINUX && (i == 0) && isCollapseFilter) {
2010 column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
db4721fb 2011 }
d3bc98ee 2012
03142470
BH
2013 if (column.getWidth() < headerWidth) {
2014 column.setWidth(headerWidth);
2015 }
db4721fb
PT
2016 }
2017
d3de0920 2018 /**
baafe54c
AM
2019 * Get the array of item strings (e.g., what to display in each cell of the
2020 * table row) corresponding to the columns and trace event passed in
2021 * parameter. The order of the Strings in the returned array will correspond
2022 * to the iteration order of 'columns'.
d3de0920 2023 *
baafe54c
AM
2024 * <p>
2025 * To ensure consistent results, make sure only call this within a scope
2026 * synchronized on 'columns'! If the order of 'columns' changes right after
2027 * this method is called, the returned value won't be ordered correctly
2028 * anymore.
2029 */
2030 private static String[] getItemStrings(List<TmfEventTableColumn> columns, ITmfEvent event) {
2031 if (event == null) {
2032 return EMPTY_STRING_ARRAY;
2033 }
2034 synchronized (columns) {
2035 List<String> itemStrings = new ArrayList<>(columns.size());
2036 for (TmfEventTableColumn column : columns) {
03142470
BH
2037 ITmfEvent passedEvent = event;
2038 if (!(column instanceof TmfMarginColumn) && (event instanceof CachedEvent)) {
2039 // Make sure that the event object from the trace is passed
2040 // to all columns but the TmfMarginColumn
2041 passedEvent = ((CachedEvent) event).event;
2042 }
2043 if (passedEvent == null) {
2044 itemStrings.add(EMPTY_STRING);
2045 } else {
2046 itemStrings.add(column.getItemString(passedEvent));
2047 }
2048
baafe54c
AM
2049 }
2050 return itemStrings.toArray(new String[0]);
2051 }
2052 }
2053
2054 /**
2055 * Get the contents of the row in the events table corresponding to an
2056 * event. The order of the elements corresponds to the current order of the
2057 * columns.
a0a88f65 2058 *
db4721fb 2059 * @param event
cf37ad9f
AM
2060 * The event printed in this row
2061 * @return The event row entries
db4721fb 2062 */
cf37ad9f 2063 public String[] getItemStrings(ITmfEvent event) {
6817308e
PT
2064 List<TmfEventTableColumn> columns = new ArrayList<>();
2065 for (int i : fTable.getColumnOrder()) {
2066 columns.add(fColumns.get(i));
2067 }
2068 return getItemStrings(columns, event);
db4721fb
PT
2069 }
2070
2071 /**
2072 * Notify this table that is got the UI focus.
2073 */
2074 public void setFocus() {
2075 fTable.setFocus();
2076 }
2077
2078 /**
2079 * Assign a new trace to this event table.
2080 *
2081 * @param trace
2082 * The trace to assign to this event table
2083 * @param disposeOnClose
2084 * true if the trace should be disposed when the table is
2085 * disposed
2086 */
2087 public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
2088 if ((fTrace != null) && fDisposeOnClose) {
2089 fTrace.dispose();
2090 }
2091 fTrace = trace;
2092 fPackDone = false;
2093 fSelectedRank = 0;
2094 fDisposeOnClose = disposeOnClose;
2095
2096 // Perform the updates on the UI thread
2097 fTable.getDisplay().syncExec(new Runnable() {
2098 @Override
2099 public void run() {
2100 fTable.removeAll();
2101 fCache.setTrace(fTrace); // Clear the cache
2102 if (fTrace != null) {
2103 if (!fTable.isDisposed() && (fTrace != null)) {
2104 if (fTable.getData(Key.FILTER_OBJ) == null) {
2105 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
2106 } else {
2107 stopFilterThread();
2108 fFilterMatchCount = 0;
2109 fFilterCheckCount = 0;
2110 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
2111 startFilterThread();
2112 }
2113 }
db4721fb 2114 }
ea279a69 2115 fRawViewer.setTrace(fTrace);
db4721fb
PT
2116 }
2117 });
2118 }
2119
3f43dc48
PT
2120 /**
2121 * Assign the status line manager
2122 *
2123 * @param statusLineManager
2124 * The status line manager, or null to disable status line messages
3f43dc48
PT
2125 */
2126 public void setStatusLineManager(IStatusLineManager statusLineManager) {
2127 if (fStatusLineManager != null && statusLineManager == null) {
03142470 2128 fStatusLineManager.setMessage(EMPTY_STRING);
3f43dc48
PT
2129 }
2130 fStatusLineManager = statusLineManager;
2131 }
2132
2133 private void updateStatusLine(ITmfTimestamp delta) {
2134 if (fStatusLineManager != null) {
2135 if (delta != null) {
2136 fStatusLineManager.setMessage("\u0394: " + delta); //$NON-NLS-1$
2137 } else {
2138 fStatusLineManager.setMessage(null);
2139 }
2140 }
2141 }
2142
db4721fb
PT
2143 // ------------------------------------------------------------------------
2144 // Event cache
2145 // ------------------------------------------------------------------------
2146
2147 /**
2148 * Notify that the event cache has been updated
2149 *
2150 * @param completed
2151 * Also notify if the populating of the cache is complete, or
2152 * not.
2153 */
2154 public void cacheUpdated(final boolean completed) {
2155 synchronized (fCacheUpdateSyncObj) {
2156 if (fCacheUpdateBusy) {
2157 fCacheUpdatePending = true;
2158 fCacheUpdateCompleted = completed;
2159 return;
2160 }
2161 fCacheUpdateBusy = true;
2162 }
2163 // Event cache is now updated. Perform update on the UI thread
2164 if (!fTable.isDisposed()) {
2165 fTable.getDisplay().asyncExec(new Runnable() {
2166 @Override
2167 public void run() {
2168 if (!fTable.isDisposed()) {
2169 fTable.refresh();
2170 packColumns();
2171 }
2172 if (completed) {
2173 populateCompleted();
2174 }
2175 synchronized (fCacheUpdateSyncObj) {
2176 fCacheUpdateBusy = false;
2177 if (fCacheUpdatePending) {
2178 fCacheUpdatePending = false;
2179 cacheUpdated(fCacheUpdateCompleted);
2180 }
2181 }
2182 }
2183 });
2184 }
2185 }
2186
a0a88f65
AM
2187 /**
2188 * Callback for when populating the table is complete.
2189 */
db4721fb
PT
2190 protected void populateCompleted() {
2191 // Nothing by default;
2192 }
2193
93bfd50a
PT
2194 // ------------------------------------------------------------------------
2195 // ISelectionProvider
2196 // ------------------------------------------------------------------------
2197
93bfd50a
PT
2198 @Override
2199 public void addSelectionChangedListener(ISelectionChangedListener listener) {
2200 selectionChangedListeners.add(listener);
2201 }
2202
93bfd50a
PT
2203 @Override
2204 public ISelection getSelection() {
2205 if (fTable == null || fTable.isDisposed()) {
2206 return StructuredSelection.EMPTY;
2207 }
507b1336 2208 List<Object> list = new ArrayList<>(fTable.getSelection().length);
93bfd50a
PT
2209 for (TableItem item : fTable.getSelection()) {
2210 if (item.getData() != null) {
2211 list.add(item.getData());
2212 }
2213 }
2214 return new StructuredSelection(list);
2215 }
2216
93bfd50a
PT
2217 @Override
2218 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
2219 selectionChangedListeners.remove(listener);
2220 }
2221
93bfd50a
PT
2222 @Override
2223 public void setSelection(ISelection selection) {
2224 // not implemented
2225 }
2226
2227 /**
2228 * Notifies any selection changed listeners that the viewer's selection has changed.
2229 * Only listeners registered at the time this method is called are notified.
2230 *
2231 * @param event a selection changed event
2232 *
2233 * @see ISelectionChangedListener#selectionChanged
93bfd50a
PT
2234 */
2235 protected void fireSelectionChanged(final SelectionChangedEvent event) {
2236 Object[] listeners = selectionChangedListeners.getListeners();
2237 for (int i = 0; i < listeners.length; ++i) {
2238 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
2239 SafeRunnable.run(new SafeRunnable() {
faa38350 2240 @Override
93bfd50a
PT
2241 public void run() {
2242 l.selectionChanged(event);
2243 }
2244 });
2245 }
2246 }
2247
db4721fb
PT
2248 // ------------------------------------------------------------------------
2249 // Bookmark handling
2250 // ------------------------------------------------------------------------
2251
2252 /**
2253 * Add a bookmark to this event table.
2254 *
2255 * @param bookmarksFile
2256 * The file to use for the bookmarks
2257 */
2258 public void addBookmark(final IFile bookmarksFile) {
2259 fBookmarksFile = bookmarksFile;
2260 final TableItem[] selection = fTable.getSelection();
2261 if (selection.length > 0) {
2262 final TableItem tableItem = selection[0];
2263 if (tableItem.getData(Key.RANK) != null) {
2264 final StringBuffer defaultMessage = new StringBuffer();
2265 for (int i = 0; i < fTable.getColumns().length; i++) {
60fb38b8 2266 if (i > 0) {
db4721fb
PT
2267 defaultMessage.append(", "); //$NON-NLS-1$
2268 }
2269 defaultMessage.append(tableItem.getText(i));
2270 }
3be2946f
PT
2271 final InputDialog dialog = new MultiLineInputDialog(
2272 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
2273 Messages.TmfEventsTable_AddBookmarkDialogTitle,
2274 Messages.TmfEventsTable_AddBookmarkDialogMessage,
2275 defaultMessage.toString());
db4721fb
PT
2276 if (dialog.open() == Window.OK) {
2277 final String message = dialog.getValue();
2278 try {
2279 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
2280 if (bookmark.exists()) {
2281 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
0126a8ca
AM
2282 final Long rank = (Long) tableItem.getData(Key.RANK);
2283 final int location = rank.intValue();
2284 bookmark.setAttribute(IMarker.LOCATION, Integer.valueOf(location));
db4721fb
PT
2285 fBookmarksMap.put(rank, bookmark.getId());
2286 fTable.refresh();
2287 }
2288 } catch (final CoreException e) {
2289 displayException(e);
2290 }
2291 }
2292 }
2293 }
2294
2295 }
2296
2297 /**
2298 * Remove a bookmark from this event table.
2299 *
2300 * @param bookmark
2301 * The bookmark to remove
2302 */
2303 public void removeBookmark(final IMarker bookmark) {
2db176a0 2304 for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
db4721fb 2305 if (entry.getValue().equals(bookmark.getId())) {
2db176a0 2306 fBookmarksMap.remove(entry.getKey(), entry.getValue());
db4721fb
PT
2307 fTable.refresh();
2308 return;
2309 }
2310 }
2311 }
2312
0126a8ca 2313 private void toggleBookmark(final Long rank) {
db4721fb
PT
2314 if (fBookmarksFile == null) {
2315 return;
2316 }
2317 if (fBookmarksMap.containsKey(rank)) {
2db176a0 2318 final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
db4721fb
PT
2319 fTable.refresh();
2320 try {
2db176a0
PT
2321 for (long markerId : markerIds) {
2322 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
2323 if (bookmark != null) {
2324 bookmark.delete();
2325 }
db4721fb
PT
2326 }
2327 } catch (final CoreException e) {
2328 displayException(e);
2329 }
2330 } else {
2331 addBookmark(fBookmarksFile);
2332 }
2333 }
2334
2335 /**
2336 * Refresh the bookmarks assigned to this trace, from the contents of a
2337 * bookmark file.
2338 *
2339 * @param bookmarksFile
2340 * The bookmark file to use
2341 */
2342 public void refreshBookmarks(final IFile bookmarksFile) {
2343 fBookmarksFile = bookmarksFile;
2344 if (bookmarksFile == null) {
2345 fBookmarksMap.clear();
2346 fTable.refresh();
2347 return;
2348 }
2349 try {
2350 fBookmarksMap.clear();
2351 for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
2352 final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
2353 if (location != -1) {
2354 final long rank = location;
2355 fBookmarksMap.put(rank, bookmark.getId());
2356 }
2357 }
2358 fTable.refresh();
2359 } catch (final CoreException e) {
2360 displayException(e);
2361 }
2362 }
2363
2364 @Override
2365 public void gotoMarker(final IMarker marker) {
2366 final int rank = marker.getAttribute(IMarker.LOCATION, -1);
2367 if (rank != -1) {
2368 int index = rank;
2369 if (fTable.getData(Key.FILTER_OBJ) != null) {
2370 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
2371 } else if (rank >= fTable.getItemCount()) {
2372 fPendingGotoRank = rank;
2373 }
a56ec2b8 2374 fSelectedRank = rank;
db4721fb 2375 fTable.setSelection(index + 1); // +1 for header row
3f43dc48 2376 updateStatusLine(null);
db4721fb
PT
2377 }
2378 }
2379
2380 // ------------------------------------------------------------------------
2381 // Listeners
2382 // ------------------------------------------------------------------------
2383
db4721fb
PT
2384 @Override
2385 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
2386 fTable.refresh();
2387 }
2388
db4721fb
PT
2389 // ------------------------------------------------------------------------
2390 // Signal handlers
2391 // ------------------------------------------------------------------------
2392
db4721fb
PT
2393 /**
2394 * Handler for the trace updated signal
2395 *
2396 * @param signal
2397 * The incoming signal
2398 */
2399 @TmfSignalHandler
2400 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
2401 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
2402 return;
2403 }
2404 // Perform the refresh on the UI thread
2405 Display.getDefault().asyncExec(new Runnable() {
2406 @Override
2407 public void run() {
2408 if (!fTable.isDisposed() && (fTrace != null)) {
2409 if (fTable.getData(Key.FILTER_OBJ) == null) {
2410 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
2411 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
2412 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
2413 fPendingGotoRank = -1;
3f43dc48 2414 updateStatusLine(null);
db4721fb
PT
2415 }
2416 } else {
2417 startFilterThread();
2418 }
2419 }
2420 if (!fRawViewer.isDisposed() && (fTrace != null)) {
2421 fRawViewer.refreshEventCount();
2422 }
2423 }
2424 });
2425 }
2426
2427 /**
2428 * Handler for the time synch signal.
2429 *
2430 * @param signal
2431 * The incoming signal
2432 */
2433 @TmfSignalHandler
2434 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
2435 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
2436
2437 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
2438 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
2439 // the method fTrace.getRank() from interfering and delaying ongoing requests.
fd3f1eff
AM
2440 final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
2441 TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
db4721fb 2442
0fcf3b09 2443 TmfTimestamp ts = new TmfTimestamp(signal.getBeginTime());
db4721fb
PT
2444
2445 @Override
2446 public void handleData(final ITmfEvent event) {
2447 super.handleData(event);
2448 }
2449
2450 @Override
73fce654
AM
2451 public void handleSuccess() {
2452 super.handleSuccess();
db4721fb
PT
2453 if (fTrace == null) {
2454 return;
2455 }
2456
2457 // Verify if the event is within the trace range and adjust if necessary
2458 ITmfTimestamp timestamp = ts;
065cc19b 2459 if (timestamp.compareTo(fTrace.getStartTime()) == -1) {
db4721fb
PT
2460 timestamp = fTrace.getStartTime();
2461 }
065cc19b 2462 if (timestamp.compareTo(fTrace.getEndTime()) == 1) {
db4721fb
PT
2463 timestamp = fTrace.getEndTime();
2464 }
2465
2466 // Get the rank of the selected event in the table
2467 final ITmfContext context = fTrace.seekEvent(timestamp);
2468 final long rank = context.getRank();
4c9f2944 2469 context.dispose();
db4721fb
PT
2470 fSelectedRank = rank;
2471
2472 fTable.getDisplay().asyncExec(new Runnable() {
2473 @Override
2474 public void run() {
2475 // Return if table is disposed
2476 if (fTable.isDisposed()) {
2477 return;
2478 }
2479
2480 int index = (int) rank;
2481 if (fTable.isDisposed()) {
2482 return;
2483 }
60fb38b8 2484 if (fTable.getData(Key.FILTER_OBJ) != null) {
db4721fb
PT
2485 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
2486 }
2487 fTable.setSelection(index + 1); // +1 for header row
2488 fRawViewer.selectAndReveal(rank);
3f43dc48 2489 updateStatusLine(null);
db4721fb
PT
2490 }
2491 });
2492 }
2493 };
2494
fd3f1eff 2495 ((ITmfEventProvider) fTrace).sendRequest(subRequest);
db4721fb
PT
2496 }
2497 }
2498
2499 // ------------------------------------------------------------------------
2500 // Error handling
2501 // ------------------------------------------------------------------------
2502
2503 /**
2504 * Display an exception in a message box
2505 *
2506 * @param e the exception
2507 */
2508 private static void displayException(final Exception e) {
2509 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
60fb38b8 2510 mb.setText(e.getClass().getSimpleName());
db4721fb
PT
2511 mb.setMessage(e.getMessage());
2512 mb.open();
2513 }
2514
f8177ba2 2515 /**
ae09c4ad 2516 * Refresh the table
f8177ba2
FC
2517 */
2518 public void refresh() {
2519 fCache.clear();
2520 fTable.refresh();
2521 fTable.redraw();
2522 }
03142470
BH
2523
2524 /**
9447c7ee
AM
2525 * Margin column for images and special text (e.g. collapse count)
2526 */
2527 private static final class TmfMarginColumn extends TmfEventTableColumn {
2528
2529 private static final @NonNull ITmfEventAspect MARGIN_ASPECT = new ITmfEventAspect() {
2530
2531 @Override
2532 public String getName() {
2533 return EMPTY_STRING;
2534 }
2535
2536 @Override
2537 public String resolve(ITmfEvent event) {
2538 if (!(event instanceof CachedEvent) || ((CachedEvent) event).repeatCount == 0) {
2539 return EMPTY_STRING;
2540 }
2541 return "+" + ((CachedEvent) event).repeatCount; //$NON-NLS-1$
2542 }
2543
2544 @Override
2545 public String getHelpText() {
2546 return EMPTY_STRING;
2547 }
9447c7ee
AM
2548 };
2549
2550 /**
2551 * Constructor
2552 */
2553 public TmfMarginColumn() {
2554 super(MARGIN_ASPECT);
2555 }
2556 }
03142470 2557
db4721fb 2558}
This page took 0.311335 seconds and 5 git commands to generate.