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