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