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