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