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