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