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