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