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