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