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