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