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