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