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