lttng.control: Improve regular expression recognition for pre-MI LTTng
[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 }
1103
1104 String fileName = cs.getFileName();
1105 final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
1106 File fileToOpen = new File(trimmedPath);
1107
1108 try {
1109 if (fileToOpen.exists() && fileToOpen.isFile()) {
1110 /*
1111 * The path points to a "real" file, attempt to open
1112 * that
1113 */
1114 IFileStore fileStore = EFS.getLocalFileSystem().getStore(fileToOpen.toURI());
1115 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
1116
1117 IEditorPart editor = IDE.openEditorOnFileStore(page, fileStore);
1118 if (editor instanceof ITextEditor) {
1119 /*
1120 * Calculate the "document offset" corresponding to
1121 * the line number, then seek there.
1122 */
1123 ITextEditor textEditor = (ITextEditor) editor;
1124 int lineNumber = Long.valueOf(cs.getLineNumber()).intValue();
1125 IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
1126
1127 IRegion region = document.getLineInformation(lineNumber - 1);
1128 if (region != null) {
1129 textEditor.selectAndReveal(region.getOffset(), region.getLength());
1130 }
8936d4f2 1131 }
61bbd27d
AM
1132
1133 } else {
1134 /*
1135 * The file was not found on disk, attempt to find it in
1136 * the workspace instead.
1137 */
1138 IMarker marker = null;
507b1336 1139 final ArrayList<IFile> files = new ArrayList<>();
fb4e9adc 1140 IPath p = new Path(trimmedPath);
60fb38b8
PT
1141 ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
1142 @Override
1143 public boolean visit(IResource resource) throws CoreException {
fb4e9adc 1144 if (resource instanceof IFile && resource.getFullPath().toString().endsWith(p.lastSegment())) {
60fb38b8
PT
1145 files.add((IFile) resource);
1146 }
1147 return true;
1148 }
1149 });
1150 IFile file = null;
1151 if (files.size() > 1) {
1152 ListDialog dialog = new ListDialog(getTable().getShell());
1153 dialog.setContentProvider(ArrayContentProvider.getInstance());
1154 dialog.setLabelProvider(new LabelProvider() {
1155 @Override
1156 public String getText(Object element) {
1157 return ((IFile) element).getFullPath().toString();
1158 }
1159 });
1160 dialog.setInput(files);
029df6e3
JCK
1161 dialog.setTitle(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle);
1162 dialog.setMessage(Messages.TmfEventsTable_OpenSourceCodeSelectFileDialogTitle + '\n' + cs.toString());
60fb38b8
PT
1163 dialog.open();
1164 Object[] result = dialog.getResult();
1165 if (result != null && result.length > 0) {
1166 file = (IFile) result[0];
1167 }
1168 } else if (files.size() == 1) {
1169 file = files.get(0);
1170 }
1171 if (file != null) {
1172 marker = file.createMarker(IMarker.MARKER);
1173 marker.setAttribute(IMarker.LINE_NUMBER, Long.valueOf(cs.getLineNumber()).intValue());
1174 IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker);
1175 marker.delete();
61bbd27d 1176 } else if (files.isEmpty()) {
029df6e3 1177 displayException(new FileNotFoundException('\'' + cs.toString() + '\'' + '\n' + Messages.TmfEventsTable_OpenSourceCodeNotFound));
60fb38b8 1178 }
60fb38b8 1179 }
61bbd27d
AM
1180 } catch (BadLocationException | CoreException e) {
1181 displayException(e);
60fb38b8
PT
1182 }
1183 }
1184 };
1185
1186 final IAction openModelAction = new Action(Messages.TmfEventsTable_OpenModelActionText) {
ac44ef71
AR
1187 @Override
1188 public void run() {
1189
1190 final TableItem items[] = fTable.getSelection();
1191 if (items.length != 1) {
1192 return;
1193 }
1194 final TableItem item = items[0];
1195
1196 final Object eventData = item.getData();
f47ed727
BH
1197 if (eventData instanceof ITmfModelLookup) {
1198 String modelURI = ((ITmfModelLookup) eventData).getModelUri();
ac44ef71
AR
1199
1200 if (modelURI != null) {
1201 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
1202
1203 IFile file = null;
1204 final URI uri = URI.createURI(modelURI);
1205 if (uri.isPlatformResource()) {
1206 IPath path = new Path(uri.toPlatformString(true));
1207 file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
1208 } else if (uri.isFile() && !uri.isRelative()) {
1209 file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
1210 new Path(uri.toFileString()));
1211 }
1212
1213 if (file != null) {
1214 try {
1215 /*
1216 * create a temporary validation marker on the
1217 * model file, remove it afterwards thus,
1218 * navigation works with all model editors
1219 * supporting the navigation to a marker
1220 */
1221 IMarker marker = file.createMarker(EValidator.MARKER);
1222 marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
1223 marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
1224
1225 IDE.openEditor(activePage, marker, OpenStrategy.activateOnOpen());
1226 marker.delete();
8b7eb3cd 1227 } catch (CoreException e) {
ac44ef71
AR
1228 displayException(e);
1229 }
60fb38b8
PT
1230 } else {
1231 displayException(new FileNotFoundException('\'' + modelURI + '\'' + '\n' + Messages.TmfEventsTable_OpenModelUnsupportedURI));
ac44ef71
AR
1232 }
1233 }
1234 }
1235 }
1236 };
60fb38b8 1237
d3de0920
XR
1238 final IAction exportToTextAction = new Action(Messages.TmfEventsTable_Export_to_text) {
1239 @Override
1240 public void run() {
1241 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
346fa221
MAL
1242 Object handlerServiceObject = activePage.getActiveEditor().getSite().getService(IHandlerService.class);
1243 IHandlerService handlerService = (IHandlerService) handlerServiceObject;
1244 Object cmdServiceObject = activePage.getActiveEditor().getSite().getService(ICommandService.class);
1245 ICommandService cmdService = (ICommandService) cmdServiceObject;
d3de0920 1246 try {
507b1336 1247 HashMap<String, Object> parameters = new HashMap<>();
d3de0920 1248 Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
03142470
BH
1249 ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command, parameters);
1250
d3de0920 1251 IEvaluationContext context = handlerService.getCurrentState();
6817308e
PT
1252 List<TmfEventTableColumn> exportColumns = new ArrayList<>();
1253 for (int i : fTable.getColumnOrder()) {
9ba0a108
PT
1254 TableColumn column = fTable.getColumns()[i];
1255 // Omit the margin column and hidden columns
7697e148 1256 if (isVisibleEventColumn(column)) {
6817308e
PT
1257 exportColumns.add(fColumns.get(i));
1258 }
1259 }
03142470
BH
1260 context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, exportColumns);
1261
d3de0920 1262 handlerService.executeCommandInContext(cmd, null, context);
23c625f1 1263 } catch (ExecutionException | NotDefinedException | NotEnabledException | NotHandledException e) {
d3de0920
XR
1264 displayException(e);
1265 }
1266 }
1267 };
1268
0a08e17d 1269 final IAction addAsFilterAction = new Action(Messages.TmfEventsTable_AddAsFilterText) {
db4721fb
PT
1270 @Override
1271 public void run() {
0a08e17d 1272 applySearchAsFilter();
db4721fb
PT
1273 }
1274 };
1275
1276 final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
1277 @Override
1278 public void run() {
1279 clearFilters();
1280 }
1281 };
1282
03142470
BH
1283 final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
1284 @Override
1285 public void run() {
1286 applyFilter(new TmfCollapseFilter());
1287 }
1288 };
1289
db4721fb 1290 class ToggleBookmarkAction extends Action {
0126a8ca 1291 Long fRank;
db4721fb 1292
0126a8ca 1293 public ToggleBookmarkAction(final String text, final Long rank) {
db4721fb
PT
1294 super(text);
1295 fRank = rank;
1296 }
1297
1298 @Override
1299 public void run() {
1300 toggleBookmark(fRank);
1301 }
1302 }
1303
3bccf3b1
MAL
1304 fHeaderPopupMenuManager = new MenuManager();
1305 fHeaderPopupMenuManager.setRemoveAllWhenShown(true);
1306 fHeaderPopupMenuManager.addMenuListener(new IMenuListener() {
be42703d
MK
1307 @Override
1308 public void menuAboutToShow(IMenuManager manager) {
1309 for (int index : fTable.getColumnOrder()) {
1310 if (fTable.getColumns()[index].getData(Key.WIDTH) != null) {
3bccf3b1 1311 fHeaderPopupMenuManager.add(createHeaderAction(fTable.getColumns()[index]));
be42703d
MK
1312 }
1313 }
3bccf3b1
MAL
1314 fHeaderPopupMenuManager.add(new Separator());
1315 fHeaderPopupMenuManager.add(createResetHeaderAction());
be42703d
MK
1316 }
1317 });
1318
3bccf3b1
MAL
1319 fTablePopupMenuManager = new MenuManager();
1320 fTablePopupMenuManager.setRemoveAllWhenShown(true);
1321 fTablePopupMenuManager.addMenuListener(new IMenuListener() {
db4721fb
PT
1322 @Override
1323 public void menuAboutToShow(final IMenuManager manager) {
9ba0a108 1324 if (fTable.getSelectionIndices().length == 1 && fTable.getSelectionIndices()[0] == 0) {
db4721fb 1325 // Right-click on header row
0a08e17d
PT
1326 if (fHeaderState == HeaderState.SEARCH) {
1327 fTablePopupMenuManager.add(addAsFilterAction);
db4721fb
PT
1328 }
1329 return;
1330 }
665990bb 1331 final Point point = fTable.toControl(fLastMenuCursorLocation);
db4721fb
PT
1332 final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
1333 if (item != null) {
1334 final Rectangle imageBounds = item.getImageBounds(0);
1335 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
1336 if (point.x <= (imageBounds.x + imageBounds.width)) {
1337 // Right-click on left margin
1338 final Long rank = (Long) item.getData(Key.RANK);
1339 if ((rank != null) && (fBookmarksFile != null)) {
1340 if (fBookmarksMap.containsKey(rank)) {
3bccf3b1 1341 fTablePopupMenuManager.add(new ToggleBookmarkAction(
db4721fb
PT
1342 Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
1343 } else {
3bccf3b1 1344 fTablePopupMenuManager.add(new ToggleBookmarkAction(
db4721fb
PT
1345 Messages.TmfEventsTable_AddBookmarkActionText, rank));
1346 }
1347 }
1348 return;
1349 }
1350 }
60fb38b8 1351
db4721fb 1352 // Right-click on table
9ba0a108 1353 if (fSelectedRank != -1 && fSelectedBeginRank != -1) {
3bccf3b1
MAL
1354 fTablePopupMenuManager.add(copyAction);
1355 fTablePopupMenuManager.add(new Separator());
9ba0a108 1356 }
db4721fb 1357 if (fTable.isVisible() && fRawViewer.isVisible()) {
3bccf3b1
MAL
1358 fTablePopupMenuManager.add(hideTableAction);
1359 fTablePopupMenuManager.add(hideRawAction);
db4721fb 1360 } else if (!fTable.isVisible()) {
3bccf3b1 1361 fTablePopupMenuManager.add(showTableAction);
db4721fb 1362 } else if (!fRawViewer.isVisible()) {
3bccf3b1 1363 fTablePopupMenuManager.add(showRawAction);
db4721fb 1364 }
3bccf3b1
MAL
1365 fTablePopupMenuManager.add(exportToTextAction);
1366 fTablePopupMenuManager.add(new Separator());
60fb38b8 1367
ac44ef71 1368 if (item != null) {
60fb38b8 1369 final Object data = item.getData();
f47ed727
BH
1370 Separator separator = null;
1371 if (data instanceof ITmfSourceLookup) {
1372 ITmfSourceLookup event = (ITmfSourceLookup) data;
60fb38b8 1373 if (event.getCallsite() != null) {
3bccf3b1 1374 fTablePopupMenuManager.add(openCallsiteAction);
60fb38b8
PT
1375 separator = new Separator();
1376 }
f47ed727
BH
1377 }
1378
1379 if (data instanceof ITmfModelLookup) {
1380 ITmfModelLookup event = (ITmfModelLookup) data;
1381 if (event.getModelUri() != null) {
3bccf3b1 1382 fTablePopupMenuManager.add(openModelAction);
60fb38b8
PT
1383 separator = new Separator();
1384 }
f47ed727 1385
60fb38b8 1386 if (separator != null) {
3bccf3b1 1387 fTablePopupMenuManager.add(separator);
ac44ef71
AR
1388 }
1389 }
1390 }
60fb38b8 1391
8b7eb3cd
MK
1392 /*
1393 * Only show collapse filter if at least one trace can be
1394 * collapsed.
1395 */
03142470
BH
1396 boolean isCollapsible = false;
1397 if (fTrace != null) {
c14c0757 1398 for (ITmfTrace trace : TmfTraceManager.getTraceSet(fTrace)) {
8b7eb3cd 1399 Class<? extends ITmfEvent> eventClass = trace.getEventType();
03142470
BH
1400 isCollapsible = ITmfCollapsibleEvent.class.isAssignableFrom(eventClass);
1401 if (isCollapsible) {
1402 break;
1403 }
1404 }
1405 }
1406
0a08e17d 1407 if (isCollapsible && !fCollapseFilterEnabled) {
3bccf3b1
MAL
1408 fTablePopupMenuManager.add(collapseAction);
1409 fTablePopupMenuManager.add(new Separator());
03142470
BH
1410 }
1411
3bccf3b1 1412 fTablePopupMenuManager.add(clearFiltersAction);
db4721fb
PT
1413 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
1414 if (savedFilters.length > 0) {
1415 final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
1416 for (final ITmfFilterTreeNode node : savedFilters) {
1417 if (node instanceof TmfFilterNode) {
1418 final TmfFilterNode filter = (TmfFilterNode) node;
1419 subMenu.add(new Action(filter.getFilterName()) {
1420 @Override
1421 public void run() {
1422 applyFilter(filter);
1423 }
1424 });
1425 }
1426 }
3bccf3b1 1427 fTablePopupMenuManager.add(subMenu);
db4721fb 1428 }
3bccf3b1 1429 appendToTablePopupMenu(fTablePopupMenuManager, item);
db4721fb
PT
1430 }
1431 });
1432
3bccf3b1
MAL
1433 fRawViewerPopupMenuManager = new MenuManager();
1434 fRawViewerPopupMenuManager.setRemoveAllWhenShown(true);
1435 fRawViewerPopupMenuManager.addMenuListener(new IMenuListener() {
db4721fb
PT
1436 @Override
1437 public void menuAboutToShow(final IMenuManager manager) {
1438 if (fTable.isVisible() && fRawViewer.isVisible()) {
3bccf3b1
MAL
1439 fRawViewerPopupMenuManager.add(hideTableAction);
1440 fRawViewerPopupMenuManager.add(hideRawAction);
db4721fb 1441 } else if (!fTable.isVisible()) {
3bccf3b1 1442 fRawViewerPopupMenuManager.add(showTableAction);
db4721fb 1443 } else if (!fRawViewer.isVisible()) {
3bccf3b1 1444 fRawViewerPopupMenuManager.add(showRawAction);
db4721fb 1445 }
18d4ca27 1446 appendToRawPopupMenu(fRawViewerPopupMenuManager);
db4721fb
PT
1447 }
1448 });
1449
3bccf3b1 1450 fHeaderMenu = fHeaderPopupMenuManager.createContextMenu(fTable);
be42703d 1451
3bccf3b1 1452 fTablePopup = fTablePopupMenuManager.createContextMenu(fTable);
346ffed7 1453 fTable.setMenu(fTablePopup);
db4721fb 1454
3bccf3b1 1455 fRawTablePopup = fRawViewerPopupMenuManager.createContextMenu(fRawViewer);
346ffed7 1456 fRawViewer.setMenu(fRawTablePopup);
db4721fb
PT
1457 }
1458
a0a88f65
AM
1459 /**
1460 * Append an item to the event table's pop-up menu.
1461 *
1462 * @param tablePopupMenu
1463 * The menu manager
1464 * @param selectedItem
1465 * The item to append
1466 */
db4721fb
PT
1467 protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
1468 // override to append more actions
1469 }
1470
a0a88f65
AM
1471 /**
1472 * Append an item to the raw viewer's pop-up menu.
1473 *
1474 * @param rawViewerPopupMenu
1475 * The menu manager
1476 */
db4721fb
PT
1477 protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
1478 // override to append more actions
1479 }
1480
1481 @Override
1482 public void dispose() {
25033fef
PT
1483 fComposite.dispose();
1484 }
1485
1486 private void internalDispose() {
db4721fb
PT
1487 stopSearchThread();
1488 stopFilterThread();
812e7197 1489 PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(this);
db4721fb 1490 ColorSettingsManager.removeColorSettingsListener(this);
25033fef 1491 fCache.clear();
db4721fb
PT
1492 if ((fTrace != null) && fDisposeOnClose) {
1493 fTrace.dispose();
1494 }
1495 fResourceManager.dispose();
3bccf3b1
MAL
1496 if (fRawViewerPopupMenuManager != null) {
1497 fRawViewerPopupMenuManager.dispose();
1498 }
1499 if (fHeaderPopupMenuManager != null) {
1500 fHeaderPopupMenuManager.dispose();
1501 }
1502 if (fTablePopupMenuManager != null) {
1503 fTablePopupMenuManager.dispose();
1504 }
1505
db4721fb
PT
1506 super.dispose();
1507 }
1508
1509 /**
1510 * Assign a layout data object to this view.
1511 *
1512 * @param layoutData
1513 * The layout data to assign
1514 */
1515 public void setLayoutData(final Object layoutData) {
1516 fComposite.setLayoutData(layoutData);
1517 }
1518
1519 /**
1520 * Get the virtual table contained in this event table.
1521 *
1522 * @return The TMF virtual table
1523 */
1524 public TmfVirtualTable getTable() {
1525 return fTable;
1526 }
1527
1528 /**
1529 * @param columnData
baafe54c
AM
1530 * columnData
1531 * @deprecated The column headers are now set at the constructor, this
1532 * shouldn't be called anymore.
db4721fb 1533 */
baafe54c 1534 @Deprecated
8b7eb3cd 1535 protected void setColumnHeaders(final org.eclipse.tracecompass.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
baafe54c 1536 /* No-op */
db4721fb
PT
1537 }
1538
a0a88f65
AM
1539 /**
1540 * Set a table item's data.
1541 *
1542 * @param item
1543 * The item to set
1544 * @param event
1545 * Which trace event to link with this entry
1546 * @param rank
1547 * Which rank this event has in the trace/experiment
1548 */
db4721fb 1549 protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
03142470 1550 String[] itemStrings = getItemStrings(fColumns, event);
32528869
BH
1551
1552 // Get the actual ITmfEvent from the CachedEvent
1553 ITmfEvent tmfEvent = event;
1554 if (event instanceof CachedEvent) {
1555 tmfEvent = ((CachedEvent) event).event;
1556 }
03142470 1557 item.setText(itemStrings);
32528869 1558 item.setData(tmfEvent);
b2c971ec 1559 item.setData(Key.TIMESTAMP, tmfEvent.getTimestamp());
db4721fb
PT
1560 item.setData(Key.RANK, rank);
1561
2db176a0
PT
1562 final Collection<Long> markerIds = fBookmarksMap.get(rank);
1563 if (!markerIds.isEmpty()) {
1564 Joiner joiner = Joiner.on("\n -").skipNulls(); //$NON-NLS-1$
1565 List<Object> parts = new ArrayList<>();
1566 if (markerIds.size() > 1) {
1567 parts.add(Messages.TmfEventsTable_MultipleBookmarksToolTip);
1568 }
db4721fb 1569 try {
2db176a0
PT
1570 for (long markerId : markerIds) {
1571 final IMarker marker = fBookmarksFile.findMarker(markerId);
7697e148
PT
1572 if (marker != null) {
1573 parts.add(marker.getAttribute(IMarker.MESSAGE));
1574 }
2db176a0
PT
1575 }
1576 } catch (CoreException e) {
db4721fb
PT
1577 displayException(e);
1578 }
2db176a0 1579 item.setData(Key.BOOKMARK, joiner.join(parts));
db4721fb
PT
1580 } else {
1581 item.setData(Key.BOOKMARK, null);
1582 }
1583
1584 boolean searchMatch = false;
1585 boolean searchNoMatch = false;
1586 final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
1587 if (searchFilter != null) {
32528869 1588 if (searchFilter.matches(tmfEvent)) {
db4721fb
PT
1589 searchMatch = true;
1590 } else {
1591 searchNoMatch = true;
1592 }
1593 }
1594
32528869 1595 final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(tmfEvent);
db4721fb
PT
1596 if (searchNoMatch) {
1597 item.setForeground(colorSetting.getDimmedForegroundColor());
1598 item.setBackground(colorSetting.getDimmedBackgroundColor());
1599 } else {
1600 item.setForeground(colorSetting.getForegroundColor());
1601 item.setBackground(colorSetting.getBackgroundColor());
1602 }
812e7197 1603 item.setFont(fFont);
db4721fb
PT
1604
1605 if (searchMatch) {
2db176a0 1606 if (!markerIds.isEmpty()) {
db4721fb
PT
1607 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
1608 } else {
1609 item.setImage(SEARCH_MATCH_IMAGE);
1610 }
2db176a0 1611 } else if (!markerIds.isEmpty()) {
db4721fb
PT
1612 item.setImage(BOOKMARK_IMAGE);
1613 } else {
1614 item.setImage((Image) null);
1615 }
03142470 1616
110d44a1
PT
1617 List<StyleRange> styleRanges = new ArrayList<>();
1618 for (int index = 0; index < fTable.getColumns().length; index++) {
1619 TableColumn column = fTable.getColumns()[index];
1620 String regex = null;
0a08e17d
PT
1621 if (fHeaderState == HeaderState.SEARCH) {
1622 if (searchMatch) {
1623 regex = (String) column.getData(Key.SEARCH_TXT);
1624 }
1625 } else {
110d44a1 1626 regex = (String) column.getData(Key.FILTER_TXT);
110d44a1
PT
1627 }
1628 if (regex != null) {
110d44a1
PT
1629 String text = item.getText(index);
1630 try {
1631 Pattern pattern = Pattern.compile(regex);
1632 Matcher matcher = pattern.matcher(text);
1633 while (matcher.find()) {
1634 int start = matcher.start();
1635 int length = matcher.end() - start;
1636 Color foreground = colorSetting.getForegroundColor();
bb7c657f 1637 Color background = fHighlightColor;
110d44a1
PT
1638 StyleRange styleRange = new StyleRange(start, length, foreground, background);
1639 styleRange.data = index;
1640 styleRanges.add(styleRange);
1641 }
1642 } catch (PatternSyntaxException e) {
1643 /* ignored */
1644 }
1645 }
1646 }
1647 if (styleRanges.isEmpty()) {
1648 item.setData(Key.STYLE_RANGES, null);
1649 } else {
1650 item.setData(Key.STYLE_RANGES, styleRanges);
1651 }
1652 item.getParent().redraw();
1653
03142470
BH
1654 if ((itemStrings[MARGIN_COLUMN_INDEX] != null) && !itemStrings[MARGIN_COLUMN_INDEX].isEmpty()) {
1655 packMarginColumn();
1656 }
db4721fb
PT
1657 }
1658
a0a88f65
AM
1659 /**
1660 * Set the item data of the header row.
1661 *
1662 * @param item
1663 * The item to use as table header
1664 */
db4721fb 1665 protected void setHeaderRowItemData(final TableItem item) {
0a08e17d 1666 if (fHeaderState == HeaderState.NO_SEARCH) {
db4721fb 1667 item.setImage(SEARCH_IMAGE);
0a08e17d
PT
1668 } else if (fHeaderState == HeaderState.SEARCH) {
1669 item.setImage(FILTER_ADD_IMAGE);
db4721fb
PT
1670 }
1671 item.setForeground(fGrayColor);
03142470
BH
1672 // Ignore collapse and image column
1673 for (int i = EVENT_COLUMNS_START_INDEX; i < fTable.getColumns().length; i++) {
db4721fb 1674 final TableColumn column = fTable.getColumns()[i];
0a08e17d 1675 final String filter = (String) column.getData(Key.SEARCH_TXT);
db4721fb 1676 if (filter == null) {
0a08e17d 1677 item.setText(i, SEARCH_HINT);
db4721fb 1678 item.setForeground(i, fGrayColor);
812e7197 1679 item.setFont(i, fFont);
db4721fb
PT
1680 } else {
1681 item.setText(i, filter);
1682 item.setForeground(i, fGreenColor);
1683 item.setFont(i, fBoldFont);
1684 }
1685 }
1686 }
1687
a0a88f65
AM
1688 /**
1689 * Set the item data of the "filter status" row.
1690 *
1691 * @param item
1692 * The item to use as filter status row
1693 */
db4721fb
PT
1694 protected void setFilterStatusRowItemData(final TableItem item) {
1695 for (int i = 0; i < fTable.getColumns().length; i++) {
03142470 1696 if (i == MARGIN_COLUMN_INDEX) {
db4721fb
PT
1697 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
1698 item.setImage(FILTER_IMAGE);
1699 } else {
1700 item.setImage(STOP_IMAGE);
1701 }
03142470
BH
1702 }
1703
1704 if (i == FILTER_SUMMARY_INDEX) {
1705 item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
db4721fb 1706 } else {
03142470 1707 item.setText(i, EMPTY_STRING);
db4721fb
PT
1708 }
1709 }
93bfd50a 1710 item.setData(null);
db4721fb
PT
1711 item.setData(Key.TIMESTAMP, null);
1712 item.setData(Key.RANK, null);
110d44a1 1713 item.setData(Key.STYLE_RANGES, null);
db4721fb
PT
1714 item.setForeground(null);
1715 item.setBackground(null);
812e7197 1716 item.setFont(fFont);
db4721fb
PT
1717 }
1718
a0a88f65
AM
1719 /**
1720 * Create an editor for the header.
1721 */
a890f499 1722 private void createHeaderEditor() {
db4721fb
PT
1723 final TableEditor tableEditor = fTable.createTableEditor();
1724 tableEditor.horizontalAlignment = SWT.LEFT;
1725 tableEditor.verticalAlignment = SWT.CENTER;
1726 tableEditor.grabHorizontal = true;
1727 tableEditor.minimumWidth = 50;
1728
1729 // Handle the header row selection
1730 fTable.addMouseListener(new MouseAdapter() {
1731 int columnIndex;
1732 TableColumn column;
1733 TableItem item;
1734
1735 @Override
1736 public void mouseDown(final MouseEvent event) {
1737 if (event.button != 1) {
1738 return;
1739 }
1740 // Identify the selected row
1741 final Point point = new Point(event.x, event.y);
1742 item = fTable.getItem(point);
1743
1744 // Header row selected
1745 if ((item != null) && (fTable.indexOf(item) == 0)) {
1746
8ec1247f
BH
1747 // Margin column selected
1748 if (item.getBounds(0).contains(point)) {
db4721fb 1749 if (fHeaderState == HeaderState.SEARCH) {
0a08e17d 1750 applySearchAsFilter();
db4721fb 1751 }
db4721fb
PT
1752 return;
1753 }
1754
1755 // Identify the selected column
1756 columnIndex = -1;
1757 for (int i = 0; i < fTable.getColumns().length; i++) {
1758 final Rectangle rect = item.getBounds(i);
1759 if (rect.contains(point)) {
1760 columnIndex = i;
1761 break;
1762 }
1763 }
1764
1765 if (columnIndex == -1) {
1766 return;
1767 }
1768
1769 column = fTable.getColumns()[columnIndex];
1770
8b7eb3cd
MK
1771 /*
1772 * The control that will be the editor must be a child of
1773 * the Table
1774 */
db4721fb 1775 final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
0a08e17d 1776 final String headerString = (String) column.getData(Key.SEARCH_TXT);
db4721fb
PT
1777 if (headerString != null) {
1778 newEditor.setText(headerString);
1779 }
1780 newEditor.addFocusListener(new FocusAdapter() {
1781 @Override
1782 public void focusLost(final FocusEvent e) {
1783 final boolean changed = updateHeader(newEditor.getText());
1784 if (changed) {
1785 applyHeader();
1786 }
1787 }
1788 });
1789 newEditor.addKeyListener(new KeyAdapter() {
1790 @Override
1791 public void keyPressed(final KeyEvent e) {
1792 if (e.character == SWT.CR) {
1793 updateHeader(newEditor.getText());
1794 applyHeader();
0a08e17d
PT
1795 if ((e.stateMask & SWT.CTRL) != 0) {
1796 applySearchAsFilter();
0a08e17d 1797 }
d6d59bfa
PT
1798 /*
1799 * Set focus on the table so that the next
1800 * carriage return goes to the next result
1801 */
1802 TmfEventsTable.this.getTable().setFocus();
db4721fb
PT
1803 } else if (e.character == SWT.ESC) {
1804 tableEditor.getEditor().dispose();
d6d59bfa 1805 TmfEventsTable.this.getTable().setFocus();
db4721fb
PT
1806 }
1807 }
1808 });
1809 newEditor.selectAll();
1810 newEditor.setFocus();
1811 tableEditor.setEditor(newEditor, item, columnIndex);
1812 }
1813 }
1814
1815 /*
1816 * returns true is value was changed
1817 */
17dd85d7 1818 private boolean updateHeader(final String regex) {
17dd85d7 1819 if (regex.length() > 0) {
db4721fb 1820 try {
db4721fb 1821 Pattern.compile(regex);
0a08e17d 1822 if (regex.equals(column.getData(Key.SEARCH_TXT))) {
db4721fb
PT
1823 tableEditor.getEditor().dispose();
1824 return false;
1825 }
ec34bf48 1826 final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
ec48d248 1827 ITmfEventAspect<?> aspect = (ITmfEventAspect<?>) column.getData(Key.ASPECT);
c409f16b 1828 filter.setEventAspect(aspect);
db4721fb 1829 filter.setRegex(regex);
0a08e17d
PT
1830 column.setData(Key.SEARCH_OBJ, filter);
1831 column.setData(Key.SEARCH_TXT, regex);
db4721fb
PT
1832 } catch (final PatternSyntaxException ex) {
1833 tableEditor.getEditor().dispose();
1834 MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1835 ex.getDescription(), ex.getMessage());
1836 return false;
1837 }
1838 } else {
0a08e17d 1839 if (column.getData(Key.SEARCH_TXT) == null) {
db4721fb
PT
1840 tableEditor.getEditor().dispose();
1841 return false;
1842 }
0a08e17d
PT
1843 column.setData(Key.SEARCH_OBJ, null);
1844 column.setData(Key.SEARCH_TXT, null);
db4721fb
PT
1845 }
1846 return true;
1847 }
1848
1849 private void applyHeader() {
0a08e17d
PT
1850 stopSearchThread();
1851 final TmfFilterRootNode filter = new TmfFilterRootNode();
1852 for (final TableColumn col : fTable.getColumns()) {
1853 final Object filterObj = col.getData(Key.SEARCH_OBJ);
1854 if (filterObj instanceof ITmfFilterTreeNode) {
1855 filter.addChild((ITmfFilterTreeNode) filterObj);
db4721fb
PT
1856 }
1857 }
0a08e17d
PT
1858 if (filter.getChildrenCount() > 0) {
1859 fHeaderState = HeaderState.SEARCH;
1860 fTable.setData(Key.SEARCH_OBJ, filter);
1861 fTable.refresh();
1862 searchNext();
1863 fireSearchApplied(filter);
1864 } else {
1865 fHeaderState = HeaderState.NO_SEARCH;
1866 fTable.setData(Key.SEARCH_OBJ, null);
1867 fTable.refresh();
1868 fireSearchApplied(null);
1869 }
db4721fb
PT
1870
1871 tableEditor.getEditor().dispose();
1872 }
1873 });
1874
1875 fTable.addKeyListener(new KeyAdapter() {
1876 @Override
1877 public void keyPressed(final KeyEvent e) {
1878 e.doit = false;
1879 if (e.character == SWT.ESC) {
1880 stopFilterThread();
1881 stopSearchThread();
1882 fTable.refresh();
1883 } else if (e.character == SWT.DEL) {
1884 if (fHeaderState == HeaderState.SEARCH) {
0a08e17d 1885 fHeaderState = HeaderState.NO_SEARCH;
db4721fb
PT
1886 stopSearchThread();
1887 for (final TableColumn column : fTable.getColumns()) {
1888 column.setData(Key.SEARCH_OBJ, null);
1889 column.setData(Key.SEARCH_TXT, null);
0a08e17d 1890 column.setData(Key.FILTER_TXT, null);
db4721fb
PT
1891 }
1892 fTable.setData(Key.SEARCH_OBJ, null);
1893 fTable.refresh();
1894 fireSearchApplied(null);
0a08e17d
PT
1895 } else {
1896 for (final TableColumn column : fTable.getColumns()) {
1897 column.setData(Key.FILTER_TXT, null);
1898 }
1899 fTable.refresh();
db4721fb
PT
1900 }
1901 } else if (e.character == SWT.CR) {
0a08e17d
PT
1902 if ((e.stateMask & SWT.CTRL) != 0) {
1903 if (fHeaderState == HeaderState.SEARCH) {
1904 applySearchAsFilter();
1905 }
1906 } else if ((e.stateMask & SWT.SHIFT) == 0) {
db4721fb
PT
1907 searchNext();
1908 } else {
1909 searchPrevious();
1910 }
1911 }
1912 }
1913 });
1914 }
1915
0a08e17d
PT
1916 /**
1917 * Apply the current search condition as a new filter.
1918 *
1919 * @since 2.0
1920 */
1921 protected void applySearchAsFilter() {
1922 Object searchObj = fTable.getData(Key.SEARCH_OBJ);
1923 if (searchObj instanceof ITmfFilter) {
1924 ITmfFilter filter = (ITmfFilter) searchObj;
1925 fTable.setData(Key.SEARCH_OBJ, null);
1926 fireSearchApplied(null);
1927 fHeaderState = HeaderState.NO_SEARCH;
1928 for (final TableColumn col : fTable.getColumns()) {
1929 col.setData(Key.FILTER_TXT, col.getData(Key.SEARCH_TXT));
1930 col.setData(Key.SEARCH_TXT, null);
1931 col.setData(Key.SEARCH_OBJ, null);
1932 }
1933 applyFilter(filter);
1934 }
1935 }
1936
a0a88f65
AM
1937 /**
1938 * Send an event indicating a filter has been applied.
1939 *
1940 * @param filter
1941 * The filter that was just applied
1942 */
db4721fb 1943 protected void fireFilterApplied(final ITmfFilter filter) {
faa38350 1944 broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
db4721fb
PT
1945 }
1946
a0a88f65
AM
1947 /**
1948 * Send an event indicating that a search has been applied.
1949 *
1950 * @param filter
1951 * The search filter that was just applied
1952 */
db4721fb 1953 protected void fireSearchApplied(final ITmfFilter filter) {
faa38350 1954 broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
db4721fb
PT
1955 }
1956
a0a88f65
AM
1957 /**
1958 * Start the filtering thread.
1959 */
db4721fb
PT
1960 protected void startFilterThread() {
1961 synchronized (fFilterSyncObj) {
1962 final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1963 if (fFilterThread == null || fFilterThread.filter != filter) {
1964 if (fFilterThread != null) {
1965 fFilterThread.cancel();
1966 fFilterThreadResume = false;
1967 }
1968 fFilterThread = new FilterThread(filter);
1969 fFilterThread.start();
1970 } else {
1971 fFilterThreadResume = true;
1972 }
1973 }
1974 }
1975
a0a88f65
AM
1976 /**
1977 * Stop the filtering thread.
1978 */
db4721fb
PT
1979 protected void stopFilterThread() {
1980 synchronized (fFilterSyncObj) {
1981 if (fFilterThread != null) {
1982 fFilterThread.cancel();
1983 fFilterThread = null;
1984 fFilterThreadResume = false;
1985 }
1986 }
1987 }
1988
1989 /**
0a08e17d 1990 * Apply a filter. It is added to the existing filters.
a0a88f65
AM
1991 *
1992 * @param filter
1993 * The filter to apply
db4721fb
PT
1994 */
1995 protected void applyFilter(ITmfFilter filter) {
f29f8868
BH
1996 stopFilterThread();
1997 stopSearchThread();
db4721fb
PT
1998 fFilterMatchCount = 0;
1999 fFilterCheckCount = 0;
0a08e17d
PT
2000 ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
2001 if (rootFilter == null) {
2002 rootFilter = new TmfFilterRootNode();
2003 }
2004 if (filter instanceof TmfFilterRootNode) {
2005 TmfFilterRootNode parentFilter = (TmfFilterRootNode) filter;
2006 for (ITmfFilterTreeNode child : parentFilter.getChildren()) {
2007 rootFilter.addChild(child);
2008 }
2009 } else if (filter instanceof TmfCollapseFilter) {
2010 fCollapseFilterEnabled = true;
2011 } else if (filter instanceof ITmfFilterTreeNode) {
2012 rootFilter.addChild((ITmfFilterTreeNode) filter);
2013 } else {
2014 rootFilter.addChild(new TmfFilterObjectNode(filter));
2015 }
2016 fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
2017 fHeaderBar.addFilter(filter);
2018 fTable.clearAll();
2019 fTable.setData(Key.FILTER_OBJ, rootFilter);
2020 /* +1 for header row, +2 for top and bottom filter status rows */
2021 fTable.setItemCount(3);
2022 startFilterThread();
2023 fireFilterApplied(rootFilter);
2024 }
2025
2026 /**
2027 * Remove a filter. Any other existing filters remain applied.
2028 *
2029 * @param filter
2030 * The filter to remove
2031 * @since 2.0
2032 */
2033 protected void removeFilter(ITmfFilter filter) {
2034 ITmfFilterTreeNode rootFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
2035 if (rootFilter == null) {
2036 return;
2037 }
2038 stopFilterThread();
2039 stopSearchThread();
2040 fFilterMatchCount = 0;
2041 fFilterCheckCount = 0;
2042 if (filter instanceof TmfCollapseFilter) {
2043 fCollapseFilterEnabled = false;
2044 } else if (filter instanceof ITmfFilterTreeNode) {
2045 rootFilter.removeChild((ITmfFilterTreeNode) filter);
2046 } else {
2047 for (ITmfFilterTreeNode child : rootFilter.getChildren()) {
2048 if (child instanceof TmfFilterObjectNode) {
2049 if (((TmfFilterObjectNode) child).getFilter().equals(filter)) {
2050 rootFilter.removeChild(child);
2051 break;
2052 }
2053 }
2054 }
2055 }
2056 if (!rootFilter.hasChildren() && !fCollapseFilterEnabled) {
2057 clearFilters();
2058 return;
2059 }
2060 fCache.applyFilter(rootFilter, fCollapseFilterEnabled);
2061 fHeaderBar.removeFilter(filter);
db4721fb 2062 fTable.clearAll();
0a08e17d 2063 fTable.setData(Key.FILTER_OBJ, rootFilter);
8b7eb3cd
MK
2064 /* +1 for header row, +2 for top and bottom filter status rows */
2065 fTable.setItemCount(3);
db4721fb 2066 startFilterThread();
0a08e17d
PT
2067 fireFilterApplied(rootFilter);
2068
2069 // Set original width
2070 fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
2071 packMarginColumn();
db4721fb
PT
2072 }
2073
a0a88f65
AM
2074 /**
2075 * Clear all currently active filters.
2076 */
db4721fb
PT
2077 protected void clearFilters() {
2078 if (fTable.getData(Key.FILTER_OBJ) == null) {
2079 return;
2080 }
2081 stopFilterThread();
2082 stopSearchThread();
2083 fCache.clearFilter();
0a08e17d
PT
2084 fHeaderBar.clearFilters();
2085 fCollapseFilterEnabled = false;
db4721fb
PT
2086 fTable.clearAll();
2087 for (final TableColumn column : fTable.getColumns()) {
2088 column.setData(Key.FILTER_OBJ, null);
2089 column.setData(Key.FILTER_TXT, null);
2090 }
2091 fTable.setData(Key.FILTER_OBJ, null);
2092 if (fTrace != null) {
8b7eb3cd
MK
2093 /* +1 for header row */
2094 fTable.setItemCount((int) fTrace.getNbEvents() + 1);
db4721fb 2095 } else {
8b7eb3cd
MK
2096 /* +1 for header row */
2097 fTable.setItemCount(1);
db4721fb
PT
2098 }
2099 fFilterMatchCount = 0;
2100 fFilterCheckCount = 0;
2101 if (fSelectedRank >= 0) {
8b7eb3cd
MK
2102 /* +1 for header row */
2103 fTable.setSelection((int) fSelectedRank + 1);
db4721fb
PT
2104 } else {
2105 fTable.setSelection(0);
2106 }
2107 fireFilterApplied(null);
3f43dc48 2108 updateStatusLine(null);
03142470
BH
2109
2110 // Set original width
2111 fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
2112 packMarginColumn();
db4721fb
PT
2113 }
2114
a0a88f65
AM
2115 /**
2116 * Wrapper Thread object for the filtering thread.
2117 */
db4721fb
PT
2118 protected class FilterThread extends Thread {
2119 private final ITmfFilterTreeNode filter;
0a08e17d 2120 private TmfCollapseFilter collapseFilter = null;
fd3f1eff 2121 private TmfEventRequest request;
db4721fb
PT
2122 private boolean refreshBusy = false;
2123 private boolean refreshPending = false;
2124 private final Object syncObj = new Object();
2125
a0a88f65
AM
2126 /**
2127 * Constructor.
2128 *
2129 * @param filter
2130 * The filter this thread will be processing
2131 */
db4721fb
PT
2132 public FilterThread(final ITmfFilterTreeNode filter) {
2133 super("Filter Thread"); //$NON-NLS-1$
2134 this.filter = filter;
2135 }
2136
2137 @Override
2138 public void run() {
2139 if (fTrace == null) {
2140 return;
2141 }
0a08e17d
PT
2142 if (fCollapseFilterEnabled) {
2143 collapseFilter = new TmfCollapseFilter();
2144 }
db4721fb
PT
2145 final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
2146 if (nbRequested <= 0) {
2147 return;
2148 }
fd3f1eff
AM
2149 request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
2150 (int) fFilterCheckCount, nbRequested, ExecutionType.BACKGROUND) {
db4721fb
PT
2151 @Override
2152 public void handleData(final ITmfEvent event) {
2153 super.handleData(event);
2154 if (request.isCancelled()) {
2155 return;
2156 }
03142470 2157 boolean refresh = false;
db4721fb 2158 if (filter.matches(event)) {
0a08e17d
PT
2159 if (collapseFilter == null || collapseFilter.matches(event)) {
2160 final long rank = fFilterCheckCount;
2161 final int index = (int) fFilterMatchCount;
2162 fFilterMatchCount++;
2163 fCache.storeEvent(event, rank, index);
2164 } else if (collapseFilter != null) {
03142470
BH
2165 fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
2166 }
0a08e17d 2167 refresh = true;
03142470
BH
2168 }
2169
2170 if (refresh || (fFilterCheckCount % 100) == 0) {
db4721fb
PT
2171 refreshTable();
2172 }
2173 fFilterCheckCount++;
2174 }
2175 };
fd3f1eff 2176 ((ITmfEventProvider) fTrace).sendRequest(request);
db4721fb
PT
2177 try {
2178 request.waitForCompletion();
2179 } catch (final InterruptedException e) {
2180 }
2181 refreshTable();
2182 synchronized (fFilterSyncObj) {
2183 fFilterThread = null;
2184 if (fFilterThreadResume) {
2185 fFilterThreadResume = false;
2186 fFilterThread = new FilterThread(filter);
2187 fFilterThread.start();
2188 }
2189 }
2190 }
2191
a0a88f65
AM
2192 /**
2193 * Refresh the filter.
2194 */
db4721fb
PT
2195 public void refreshTable() {
2196 synchronized (syncObj) {
2197 if (refreshBusy) {
2198 refreshPending = true;
2199 return;
2200 }
2201 refreshBusy = true;
2202 }
2203 Display.getDefault().asyncExec(new Runnable() {
2204 @Override
2205 public void run() {
2206 if (request.isCancelled()) {
2207 return;
2208 }
2209 if (fTable.isDisposed()) {
2210 return;
2211 }
8b7eb3cd
MK
2212 /*
2213 * +1 for header row, +2 for top and bottom filter status
2214 * rows
2215 */
2216 fTable.setItemCount((int) fFilterMatchCount + 3);
db4721fb
PT
2217 fTable.refresh();
2218 synchronized (syncObj) {
2219 refreshBusy = false;
2220 if (refreshPending) {
2221 refreshPending = false;
2222 refreshTable();
2223 }
2224 }
2225 }
2226 });
2227 }
2228
a0a88f65
AM
2229 /**
2230 * Cancel this filtering thread.
2231 */
db4721fb
PT
2232 public void cancel() {
2233 if (request != null) {
2234 request.cancel();
2235 }
2236 }
2237 }
2238
a0a88f65
AM
2239 /**
2240 * Go to the next item of a search.
2241 */
db4721fb
PT
2242 protected void searchNext() {
2243 synchronized (fSearchSyncObj) {
2244 if (fSearchThread != null) {
2245 return;
2246 }
2247 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
2248 if (searchFilter == null) {
2249 return;
2250 }
2251 final int selectionIndex = fTable.getSelectionIndex();
2252 int startIndex;
2253 if (selectionIndex > 0) {
8b7eb3cd
MK
2254 /* -1 for header row, +1 for next event */
2255 startIndex = selectionIndex;
db4721fb 2256 } else {
8b7eb3cd
MK
2257 /*
2258 * header row is selected, start at top event
2259 */
2260 /* -1 for header row */
2261 startIndex = Math.max(0, fTable.getTopIndex() - 1);
db4721fb
PT
2262 }
2263 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 2264 if (eventFilter != null) {
8b7eb3cd
MK
2265 // -1 for top filter status row
2266 startIndex = Math.max(0, startIndex - 1);
db4721fb
PT
2267 }
2268 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
2269 fSearchThread.schedule();
2270 }
2271 }
2272
a0a88f65
AM
2273 /**
2274 * Go to the previous item of a search.
2275 */
db4721fb
PT
2276 protected void searchPrevious() {
2277 synchronized (fSearchSyncObj) {
2278 if (fSearchThread != null) {
2279 return;
2280 }
2281 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
2282 if (searchFilter == null) {
2283 return;
2284 }
2285 final int selectionIndex = fTable.getSelectionIndex();
2286 int startIndex;
2287 if (selectionIndex > 0) {
8b7eb3cd
MK
2288 /* -1 for header row, -1 for previous event */
2289 startIndex = selectionIndex - 2;
db4721fb 2290 } else {
8b7eb3cd
MK
2291 /*
2292 * Header row is selected, start at precedent of top event
2293 */
2294 /* -1 for header row, -1 for previous event */
2295 startIndex = fTable.getTopIndex() - 2;
db4721fb
PT
2296 }
2297 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 2298 if (eventFilter != null) {
8b7eb3cd
MK
2299 /* -1 for top filter status row */
2300 startIndex = startIndex - 1;
db4721fb
PT
2301 }
2302 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
2303 fSearchThread.schedule();
2304 }
2305 }
2306
a0a88f65
AM
2307 /**
2308 * Stop the search thread.
2309 */
db4721fb
PT
2310 protected void stopSearchThread() {
2311 fPendingGotoRank = -1;
2312 synchronized (fSearchSyncObj) {
2313 if (fSearchThread != null) {
2314 fSearchThread.cancel();
2315 fSearchThread = null;
2316 }
2317 }
2318 }
2319
a0a88f65
AM
2320 /**
2321 * Wrapper for the search thread.
2322 */
db4721fb 2323 protected class SearchThread extends Job {
a0a88f65
AM
2324
2325 private ITmfFilterTreeNode searchFilter;
2326 private ITmfFilterTreeNode eventFilter;
2327 private int startIndex;
2328 private int direction;
2329 private long rank;
2330 private long foundRank = -1;
fd3f1eff 2331 private TmfEventRequest request;
ae3ffd37 2332 private ITmfTimestamp foundTimestamp = null;
db4721fb 2333
a0a88f65
AM
2334 /**
2335 * Constructor.
2336 *
2337 * @param searchFilter
2338 * The search filter
2339 * @param eventFilter
2340 * The event filter
2341 * @param startIndex
2342 * The index at which we should start searching
2343 * @param currentRank
2344 * The current rank
2345 * @param direction
2346 * In which direction should we search, forward or backwards
2347 */
2348 public SearchThread(final ITmfFilterTreeNode searchFilter,
2349 final ITmfFilterTreeNode eventFilter, final int startIndex,
db4721fb
PT
2350 final long currentRank, final int direction) {
2351 super(Messages.TmfEventsTable_SearchingJobName);
2352 this.searchFilter = searchFilter;
2353 this.eventFilter = eventFilter;
2354 this.startIndex = startIndex;
2355 this.rank = currentRank;
2356 this.direction = direction;
2357 }
2358
2359 @Override
2360 protected IStatus run(final IProgressMonitor monitor) {
f4e06774
FLN
2361 final ITmfTrace trace = fTrace;
2362 if (trace == null) {
db4721fb
PT
2363 return Status.OK_STATUS;
2364 }
2365 final Display display = Display.getDefault();
2366 if (startIndex < 0) {
f4e06774 2367 rank = (int) trace.getNbEvents() - 1;
8b7eb3cd
MK
2368 /*
2369 * -1 for header row, -3 for header and top and bottom filter
2370 * status rows
2371 */
2372 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) {
db4721fb
PT
2373 rank = 0;
2374 } else {
2375 int idx = startIndex;
2376 while (foundRank == -1) {
2377 final CachedEvent event = fCache.peekEvent(idx);
2378 if (event == null) {
2379 break;
2380 }
2381 rank = event.rank;
2382 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
2383 foundRank = event.rank;
ae3ffd37 2384 foundTimestamp = event.event.getTimestamp();
db4721fb
PT
2385 break;
2386 }
2387 if (direction == Direction.FORWARD) {
2388 idx++;
2389 } else {
2390 idx--;
2391 }
2392 }
2393 if (foundRank == -1) {
2394 if (direction == Direction.FORWARD) {
2395 rank++;
f4e06774 2396 if (rank > (trace.getNbEvents() - 1)) {
db4721fb
PT
2397 rank = 0;
2398 }
2399 } else {
2400 rank--;
2401 if (rank < 0) {
f4e06774 2402 rank = (int) trace.getNbEvents() - 1;
db4721fb
PT
2403 }
2404 }
2405 }
2406 }
2407 final int startRank = (int) rank;
2408 boolean wrapped = false;
f4e06774
FLN
2409 while (!monitor.isCanceled() && (foundRank == -1)) {
2410 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, trace.getCacheSize()));
db4721fb 2411 if (direction == Direction.BACKWARD) {
f4e06774 2412 rank = Math.max(0, rank - trace.getCacheSize() + 1);
db4721fb 2413 }
fd3f1eff
AM
2414 request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY,
2415 (int) rank, nbRequested, ExecutionType.BACKGROUND) {
db4721fb
PT
2416 long currentRank = rank;
2417
2418 @Override
2419 public void handleData(final ITmfEvent event) {
2420 super.handleData(event);
2421 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
2422 foundRank = currentRank;
ae3ffd37 2423 foundTimestamp = event.getTimestamp();
db4721fb
PT
2424 if (direction == Direction.FORWARD) {
2425 done();
2426 return;
2427 }
2428 }
2429 currentRank++;
2430 }
2431 };
f4e06774 2432 ((ITmfEventProvider) trace).sendRequest(request);
db4721fb
PT
2433 try {
2434 request.waitForCompletion();
2435 if (request.isCancelled()) {
2436 return Status.OK_STATUS;
2437 }
2438 } catch (final InterruptedException e) {
2439 synchronized (fSearchSyncObj) {
2440 fSearchThread = null;
2441 }
2442 return Status.OK_STATUS;
2443 }
2444 if (foundRank == -1) {
2445 if (direction == Direction.FORWARD) {
2446 if (rank == 0) {
2447 synchronized (fSearchSyncObj) {
2448 fSearchThread = null;
2449 }
2450 return Status.OK_STATUS;
2451 }
2452 nbRequested = (int) rank;
2453 rank = 0;
2454 wrapped = true;
2455 } else {
2456 rank--;
2457 if (rank < 0) {
f4e06774 2458 rank = (int) trace.getNbEvents() - 1;
db4721fb
PT
2459 wrapped = true;
2460 }
2461 if ((rank <= startRank) && wrapped) {
2462 synchronized (fSearchSyncObj) {
2463 fSearchThread = null;
2464 }
2465 return Status.OK_STATUS;
2466 }
2467 }
2468 }
2469 }
2470 int index = (int) foundRank;
2471 if (eventFilter != null) {
2472 index = fCache.getFilteredEventIndex(foundRank);
2473 }
8b7eb3cd
MK
2474 /* +1 for header row, +1 for top filter status row */
2475 final int selection = index + 1 + (eventFilter != null ? +1 : 0);
db4721fb
PT
2476
2477 display.asyncExec(new Runnable() {
2478 @Override
2479 public void run() {
2480 if (monitor.isCanceled()) {
2481 return;
2482 }
2483 if (fTable.isDisposed()) {
2484 return;
2485 }
2486 fTable.setSelection(selection);
2487 fSelectedRank = foundRank;
9ba0a108 2488 fSelectedBeginRank = fSelectedRank;
ae3ffd37
PT
2489 fRawViewer.selectAndReveal(fSelectedRank);
2490 if (foundTimestamp != null) {
97c71024 2491 broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, foundTimestamp));
ae3ffd37 2492 }
a56ec2b8 2493 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
db4721fb
PT
2494 synchronized (fSearchSyncObj) {
2495 fSearchThread = null;
2496 }
3f43dc48 2497 updateStatusLine(null);
db4721fb
PT
2498 }
2499 });
2500 return Status.OK_STATUS;
2501 }
2502
2503 @Override
2504 protected void canceling() {
0a08e17d
PT
2505 if (request != null) {
2506 request.cancel();
2507 }
db4721fb
PT
2508 synchronized (fSearchSyncObj) {
2509 fSearchThread = null;
2510 }
2511 }
2512 }
2513
a0a88f65
AM
2514 /**
2515 * Create the resources.
2516 */
a890f499 2517 private void createResources() {
8b7eb3cd 2518 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable.getForeground().getRGB()));
78688b59 2519 fGreenColor = PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
812e7197
PT
2520 }
2521
2522 /**
2523 * Initialize the fonts.
812e7197 2524 */
a890f499 2525 private void initializeFonts() {
812e7197
PT
2526 FontRegistry fontRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry();
2527 fFont = fontRegistry.get(FONT_DEFINITION_ID);
2528 fBoldFont = fontRegistry.getBold(FONT_DEFINITION_ID);
2529 fTable.setFont(fFont);
2530 /* Column header font cannot be set. See Bug 63038 */
2531 }
2532
bb7c657f
PT
2533 /**
2534 * Initialize the colors.
bb7c657f 2535 */
a890f499 2536 private void initializeColors() {
bb7c657f
PT
2537 ColorRegistry colorRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry();
2538 fHighlightColor = colorRegistry.get(HIGHLIGHT_COLOR_DEFINITION_ID);
2539 }
2540
812e7197
PT
2541 /**
2542 * @since 1.0
2543 */
2544 @Override
2545 public void propertyChange(PropertyChangeEvent event) {
2546 if ((IThemeManager.CHANGE_CURRENT_THEME.equals(event.getProperty())) ||
2547 (FONT_DEFINITION_ID.equals(event.getProperty()))) {
2548 initializeFonts();
2549 fTable.refresh();
2550 }
bb7c657f
PT
2551 if ((IThemeManager.CHANGE_CURRENT_THEME.equals(event.getProperty())) ||
2552 (HIGHLIGHT_COLOR_DEFINITION_ID.equals(event.getProperty()))) {
2553 initializeColors();
2554 fTable.refresh();
2555 }
db4721fb
PT
2556 }
2557
a0a88f65
AM
2558 /**
2559 * Pack the columns.
19d1060f
MK
2560 *
2561 * @return
2562 * Whether or not a pack was done in this call. Otherwise, it was already done by a
2563 * previous call
2564 *
2565 * @since 2.0
a0a88f65 2566 */
19d1060f 2567 protected boolean packColumns() {
db4721fb 2568 if (fPackDone) {
19d1060f 2569 return false;
db4721fb 2570 }
d3bc98ee 2571 fTable.setRedraw(false);
03142470
BH
2572 try {
2573 TableColumn tableColumns[] = fTable.getColumns();
2574 for (int i = 0; i < tableColumns.length; i++) {
2575 final TableColumn column = tableColumns[i];
2576 packSingleColumn(i, column);
2577 }
346ffed7 2578
03142470
BH
2579 } finally {
2580 // Make sure that redraw is always enabled.
2581 fTable.setRedraw(true);
2582 }
2583 fPackDone = true;
19d1060f 2584 return true;
03142470 2585 }
d3bc98ee 2586
03142470
BH
2587 private void packMarginColumn() {
2588 TableColumn[] columns = fTable.getColumns();
2589 if (columns.length > 0) {
2590 packSingleColumn(0, columns[0]);
2591 }
2592 }
f29f8868 2593
03142470
BH
2594 private void packSingleColumn(int i, final TableColumn column) {
2595 final int headerWidth = column.getWidth();
2596 column.pack();
8b7eb3cd
MK
2597 /*
2598 * Workaround for Linux which doesn't consider the image width of
2599 * search/filter row in TableColumn.pack() after having executed
2600 * TableItem.setImage(null) for other rows than search/filter row.
2601 */
0a08e17d 2602 if (IS_LINUX && (i == 0) && fCollapseFilterEnabled) {
03142470 2603 column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
db4721fb 2604 }
d3bc98ee 2605
03142470
BH
2606 if (column.getWidth() < headerWidth) {
2607 column.setWidth(headerWidth);
2608 }
db4721fb
PT
2609 }
2610
7697e148
PT
2611 /**
2612 * Returns true if the column is a visible event column.
2613 *
b2c971ec
MK
2614 * @param column
2615 * the column
2616 * @return false if the column is the margin column or hidden, true
2617 * otherwise
7697e148
PT
2618 */
2619 private static boolean isVisibleEventColumn(TableColumn column) {
2620 if (column.getData(Key.ASPECT) == TmfMarginColumn.MARGIN_ASPECT) {
2621 return false;
2622 }
2623 if (!column.getResizable() && column.getWidth() == 0) {
2624 return false;
2625 }
2626 return true;
2627 }
2628
d3de0920 2629 /**
baafe54c
AM
2630 * Get the array of item strings (e.g., what to display in each cell of the
2631 * table row) corresponding to the columns and trace event passed in
2632 * parameter. The order of the Strings in the returned array will correspond
2633 * to the iteration order of 'columns'.
d3de0920 2634 *
baafe54c
AM
2635 * <p>
2636 * To ensure consistent results, make sure only call this within a scope
2637 * synchronized on 'columns'! If the order of 'columns' changes right after
2638 * this method is called, the returned value won't be ordered correctly
2639 * anymore.
2640 */
2641 private static String[] getItemStrings(List<TmfEventTableColumn> columns, ITmfEvent event) {
2642 if (event == null) {
2643 return EMPTY_STRING_ARRAY;
2644 }
2645 synchronized (columns) {
2646 List<String> itemStrings = new ArrayList<>(columns.size());
2647 for (TmfEventTableColumn column : columns) {
03142470
BH
2648 ITmfEvent passedEvent = event;
2649 if (!(column instanceof TmfMarginColumn) && (event instanceof CachedEvent)) {
8b7eb3cd
MK
2650 /*
2651 * Make sure that the event object from the trace is passed
2652 * to all columns but the TmfMarginColumn
2653 */
03142470
BH
2654 passedEvent = ((CachedEvent) event).event;
2655 }
2656 if (passedEvent == null) {
2657 itemStrings.add(EMPTY_STRING);
2658 } else {
2659 itemStrings.add(column.getItemString(passedEvent));
2660 }
2661
baafe54c
AM
2662 }
2663 return itemStrings.toArray(new String[0]);
2664 }
2665 }
2666
2667 /**
2668 * Get the contents of the row in the events table corresponding to an
2669 * event. The order of the elements corresponds to the current order of the
2670 * columns.
a0a88f65 2671 *
db4721fb 2672 * @param event
cf37ad9f
AM
2673 * The event printed in this row
2674 * @return The event row entries
db4721fb 2675 */
cf37ad9f 2676 public String[] getItemStrings(ITmfEvent event) {
6817308e
PT
2677 List<TmfEventTableColumn> columns = new ArrayList<>();
2678 for (int i : fTable.getColumnOrder()) {
2679 columns.add(fColumns.get(i));
2680 }
2681 return getItemStrings(columns, event);
db4721fb
PT
2682 }
2683
14665360 2684 /**
8b7eb3cd
MK
2685 * Returns an array of zero-relative integers that map the creation order of
2686 * the receiver's columns to the order in which they are currently being
2687 * displayed.
14665360 2688 * <p>
8b7eb3cd
MK
2689 * Specifically, the indices of the returned array represent the current
2690 * visual order of the columns, and the contents of the array represent the
2691 * creation order of the columns.
14665360
PT
2692 *
2693 * @return the current visual order of the receiver's columns
2694 * @since 1.0
2695 */
2696 public int[] getColumnOrder() {
2697 return fColumnOrder;
2698 }
2699
2700 /**
8b7eb3cd
MK
2701 * Sets the order that the columns in the receiver should be displayed in to
2702 * the given argument which is described in terms of the zero-relative
2703 * ordering of when the columns were added.
14665360 2704 * <p>
8b7eb3cd
MK
2705 * Specifically, the contents of the array represent the original position
2706 * of each column at the time its creation.
14665360 2707 *
8b7eb3cd
MK
2708 * @param order
2709 * the new order to display the columns
14665360
PT
2710 * @since 1.0
2711 */
2712 public void setColumnOrder(int[] order) {
2713 if (order == null || order.length != fTable.getColumns().length) {
2714 return;
2715 }
2716 fTable.setColumnOrder(order);
2717 fColumnOrder = fTable.getColumnOrder();
2718 }
2719
db4721fb
PT
2720 /**
2721 * Notify this table that is got the UI focus.
2722 */
2723 public void setFocus() {
2724 fTable.setFocus();
2725 }
2726
0bc16991
MAL
2727 /**
2728 * Registers context menus with a site for extension. This method can be
2729 * called for part sites so that context menu contributions can be added.
2730 *
2731 * @param site
2732 * the site that the context menus will be registered for
2733 *
066b02aa 2734 * @since 1.2
0bc16991
MAL
2735 */
2736 public void registerContextMenus(IWorkbenchPartSite site) {
2737 if (site instanceof IEditorSite) {
2738 IEditorSite editorSite = (IEditorSite) site;
2739 // Don't use the editor input when adding contributions, otherwise
2740 // we get too many unwanted things.
2741 editorSite.registerContextMenu(fTablePopupMenuManager, this, false);
2742 }
2743 }
2744
db4721fb
PT
2745 /**
2746 * Assign a new trace to this event table.
2747 *
2748 * @param trace
2749 * The trace to assign to this event table
2750 * @param disposeOnClose
2751 * true if the trace should be disposed when the table is
2752 * disposed
2753 */
2754 public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
2755 if ((fTrace != null) && fDisposeOnClose) {
2756 fTrace.dispose();
2757 }
2758 fTrace = trace;
2759 fPackDone = false;
db4721fb
PT
2760 fDisposeOnClose = disposeOnClose;
2761
2762 // Perform the updates on the UI thread
78688b59 2763 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
db4721fb
PT
2764 @Override
2765 public void run() {
9ba0a108
PT
2766 fSelectedRank = -1;
2767 fSelectedBeginRank = -1;
db4721fb 2768 fTable.removeAll();
f4e06774
FLN
2769 fCache.setTrace(trace); // Clear the cache
2770 if (trace != null) {
2771 if (!fTable.isDisposed()) {
db4721fb 2772 if (fTable.getData(Key.FILTER_OBJ) == null) {
8b7eb3cd 2773 // +1 for header row
f4e06774 2774 fTable.setItemCount((int) trace.getNbEvents() + 1);
db4721fb
PT
2775 } else {
2776 stopFilterThread();
2777 fFilterMatchCount = 0;
2778 fFilterCheckCount = 0;
8b7eb3cd
MK
2779 /*
2780 * +1 for header row, +2 for top and bottom filter
2781 * status rows
2782 */
2783 fTable.setItemCount(3);
db4721fb
PT
2784 startFilterThread();
2785 }
2786 }
db4721fb 2787 }
f4e06774 2788 fRawViewer.setTrace(trace);
db4721fb
PT
2789 }
2790 });
2791 }
2792
3f43dc48
PT
2793 /**
2794 * Assign the status line manager
2795 *
2796 * @param statusLineManager
8b7eb3cd
MK
2797 * The status line manager, or null to disable status line
2798 * messages
3f43dc48
PT
2799 */
2800 public void setStatusLineManager(IStatusLineManager statusLineManager) {
2801 if (fStatusLineManager != null && statusLineManager == null) {
03142470 2802 fStatusLineManager.setMessage(EMPTY_STRING);
3f43dc48
PT
2803 }
2804 fStatusLineManager = statusLineManager;
2805 }
2806
2807 private void updateStatusLine(ITmfTimestamp delta) {
2808 if (fStatusLineManager != null) {
2809 if (delta != null) {
2810 fStatusLineManager.setMessage("\u0394: " + delta); //$NON-NLS-1$
2811 } else {
2812 fStatusLineManager.setMessage(null);
2813 }
2814 }
2815 }
2816
db4721fb
PT
2817 // ------------------------------------------------------------------------
2818 // Event cache
2819 // ------------------------------------------------------------------------
2820
2821 /**
2822 * Notify that the event cache has been updated
2823 *
2824 * @param completed
2825 * Also notify if the populating of the cache is complete, or
2826 * not.
2827 */
2828 public void cacheUpdated(final boolean completed) {
2829 synchronized (fCacheUpdateSyncObj) {
2830 if (fCacheUpdateBusy) {
2831 fCacheUpdatePending = true;
2832 fCacheUpdateCompleted = completed;
2833 return;
2834 }
2835 fCacheUpdateBusy = true;
2836 }
2837 // Event cache is now updated. Perform update on the UI thread
78688b59
PT
2838 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
2839 @Override
2840 public void run() {
2841 if (!fTable.isDisposed()) {
2842 fTable.refresh();
2843 packColumns();
2844 }
2845 if (completed) {
2846 populateCompleted();
2847 }
2848 synchronized (fCacheUpdateSyncObj) {
2849 fCacheUpdateBusy = false;
2850 if (fCacheUpdatePending) {
2851 fCacheUpdatePending = false;
2852 cacheUpdated(fCacheUpdateCompleted);
db4721fb
PT
2853 }
2854 }
78688b59
PT
2855 }
2856 });
db4721fb
PT
2857 }
2858
a0a88f65
AM
2859 /**
2860 * Callback for when populating the table is complete.
2861 */
db4721fb
PT
2862 protected void populateCompleted() {
2863 // Nothing by default;
2864 }
2865
93bfd50a
PT
2866 // ------------------------------------------------------------------------
2867 // ISelectionProvider
2868 // ------------------------------------------------------------------------
2869
93bfd50a
PT
2870 @Override
2871 public void addSelectionChangedListener(ISelectionChangedListener listener) {
2872 selectionChangedListeners.add(listener);
2873 }
2874
93bfd50a
PT
2875 @Override
2876 public ISelection getSelection() {
2877 if (fTable == null || fTable.isDisposed()) {
2878 return StructuredSelection.EMPTY;
2879 }
507b1336 2880 List<Object> list = new ArrayList<>(fTable.getSelection().length);
93bfd50a
PT
2881 for (TableItem item : fTable.getSelection()) {
2882 if (item.getData() != null) {
2883 list.add(item.getData());
2884 }
2885 }
2886 return new StructuredSelection(list);
2887 }
2888
93bfd50a
PT
2889 @Override
2890 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
2891 selectionChangedListeners.remove(listener);
2892 }
2893
93bfd50a
PT
2894 @Override
2895 public void setSelection(ISelection selection) {
2896 // not implemented
2897 }
2898
2899 /**
8b7eb3cd
MK
2900 * Notifies any selection changed listeners that the viewer's selection has
2901 * changed. Only listeners registered at the time this method is called are
2902 * notified.
93bfd50a 2903 *
8b7eb3cd
MK
2904 * @param event
2905 * a selection changed event
93bfd50a
PT
2906 *
2907 * @see ISelectionChangedListener#selectionChanged
93bfd50a
PT
2908 */
2909 protected void fireSelectionChanged(final SelectionChangedEvent event) {
2910 Object[] listeners = selectionChangedListeners.getListeners();
2911 for (int i = 0; i < listeners.length; ++i) {
2912 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
2913 SafeRunnable.run(new SafeRunnable() {
faa38350 2914 @Override
93bfd50a
PT
2915 public void run() {
2916 l.selectionChanged(event);
2917 }
2918 });
2919 }
2920 }
2921
db4721fb
PT
2922 // ------------------------------------------------------------------------
2923 // Bookmark handling
2924 // ------------------------------------------------------------------------
2925
2926 /**
2927 * Add a bookmark to this event table.
2928 *
2929 * @param bookmarksFile
2930 * The file to use for the bookmarks
2931 */
2932 public void addBookmark(final IFile bookmarksFile) {
2933 fBookmarksFile = bookmarksFile;
2934 final TableItem[] selection = fTable.getSelection();
2935 if (selection.length > 0) {
2936 final TableItem tableItem = selection[0];
2937 if (tableItem.getData(Key.RANK) != null) {
2938 final StringBuffer defaultMessage = new StringBuffer();
7697e148
PT
2939 for (int i : fTable.getColumnOrder()) {
2940 TableColumn column = fTable.getColumns()[i];
2941 // Omit the margin column and hidden columns
2942 if (isVisibleEventColumn(column)) {
2943 if (defaultMessage.length() > 0) {
2944 defaultMessage.append(", "); //$NON-NLS-1$
2945 }
2946 defaultMessage.append(tableItem.getText(i));
db4721fb 2947 }
db4721fb 2948 }
7697e148
PT
2949 final AddBookmarkDialog dialog = new AddBookmarkDialog(
2950 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), defaultMessage.toString());
db4721fb
PT
2951 if (dialog.open() == Window.OK) {
2952 final String message = dialog.getValue();
2953 try {
7697e148
PT
2954 final Long rank = (Long) tableItem.getData(Key.RANK);
2955 final String location = NLS.bind(Messages.TmfMarker_LocationRank, rank.toString());
2956 final ITmfTimestamp timestamp = (ITmfTimestamp) tableItem.getData(Key.TIMESTAMP);
2957 final long[] id = new long[1];
2958 ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
2959 @Override
2960 public void run(IProgressMonitor monitor) throws CoreException {
2961 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
2962 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
2963 bookmark.setAttribute(IMarker.LOCATION, location);
2964 bookmark.setAttribute(ITmfMarker.MARKER_RANK, rank.toString());
b2c971ec 2965 bookmark.setAttribute(ITmfMarker.MARKER_TIME, Long.toString(timestamp.toNanos()));
7697e148
PT
2966 bookmark.setAttribute(ITmfMarker.MARKER_COLOR, dialog.getColorValue().toString());
2967 id[0] = bookmark.getId();
2968 }
2969 }, null);
2970 fBookmarksMap.put(rank, id[0]);
2971 fTable.refresh();
db4721fb
PT
2972 } catch (final CoreException e) {
2973 displayException(e);
2974 }
2975 }
2976 }
2977 }
2978
2979 }
2980
2981 /**
7697e148 2982 * Add one or more bookmarks to this event table.
db4721fb 2983 *
7697e148
PT
2984 * @param bookmarks
2985 * The bookmarks to add
2986 * @since 2.0
db4721fb 2987 */
7697e148
PT
2988 public void addBookmark(final IMarker... bookmarks) {
2989 for (IMarker bookmark : bookmarks) {
2990 /* try location as an integer for backward compatibility */
2991 long rank = bookmark.getAttribute(IMarker.LOCATION, -1);
2992 if (rank == -1) {
2993 String rankString = bookmark.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
2994 if (rankString != null) {
2995 try {
2996 rank = Long.parseLong(rankString);
2997 } catch (NumberFormatException e) {
2998 Activator.getDefault().logError("Invalid marker rank", e); //$NON-NLS-1$
2999 }
3000 }
3001 }
3002 if (rank != -1) {
3003 fBookmarksMap.put(rank, bookmark.getId());
db4721fb
PT
3004 }
3005 }
7697e148
PT
3006 fTable.refresh();
3007 }
3008
3009 /**
3010 * Remove one or more bookmarks from this event table.
3011 *
3012 * @param bookmarks
3013 * The bookmarks to remove
3014 * @since 2.0
3015 */
3016 public void removeBookmark(final IMarker... bookmarks) {
3017 for (IMarker bookmark : bookmarks) {
3018 for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
3019 if (entry.getValue().equals(bookmark.getId())) {
3020 fBookmarksMap.remove(entry.getKey(), entry.getValue());
3021 break;
3022 }
3023 }
3024 }
3025 fTable.refresh();
db4721fb
PT
3026 }
3027
0126a8ca 3028 private void toggleBookmark(final Long rank) {
db4721fb
PT
3029 if (fBookmarksFile == null) {
3030 return;
3031 }
3032 if (fBookmarksMap.containsKey(rank)) {
2db176a0 3033 final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
db4721fb
PT
3034 fTable.refresh();
3035 try {
2db176a0
PT
3036 for (long markerId : markerIds) {
3037 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
3038 if (bookmark != null) {
3039 bookmark.delete();
3040 }
db4721fb
PT
3041 }
3042 } catch (final CoreException e) {
3043 displayException(e);
3044 }
3045 } else {
3046 addBookmark(fBookmarksFile);
3047 }
3048 }
3049
3050 /**
3051 * Refresh the bookmarks assigned to this trace, from the contents of a
3052 * bookmark file.
3053 *
3054 * @param bookmarksFile
3055 * The bookmark file to use
3056 */
3057 public void refreshBookmarks(final IFile bookmarksFile) {
3058 fBookmarksFile = bookmarksFile;
3059 if (bookmarksFile == null) {
3060 fBookmarksMap.clear();
3061 fTable.refresh();
3062 return;
3063 }
3064 try {
3065 fBookmarksMap.clear();
7697e148
PT
3066 IMarker[] bookmarks = bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
3067 addBookmark(bookmarks);
db4721fb
PT
3068 } catch (final CoreException e) {
3069 displayException(e);
3070 }
3071 }
3072
3073 @Override
3074 public void gotoMarker(final IMarker marker) {
7697e148
PT
3075 ITmfTimestamp tsBegin = null;
3076 ITmfTimestamp tsEnd = null;
3077 /* try location as an integer for backward compatibility */
3078 long rank = marker.getAttribute(IMarker.LOCATION, -1);
3079 if (rank == -1) {
3080 String rankString = marker.getAttribute(ITmfMarker.MARKER_RANK, (String) null);
3081 try {
3082 rank = Long.parseLong(rankString);
3083 } catch (NumberFormatException e) {
3084 /* ignored */
3085 }
3086 }
3087 try {
3088 String timeString = marker.getAttribute(ITmfMarker.MARKER_TIME, (String) null);
3089 long time = Long.parseLong(timeString);
b2c971ec 3090 tsBegin = TmfTimestamp.fromNanos(time);
7697e148
PT
3091 String durationString = marker.getAttribute(ITmfMarker.MARKER_DURATION, (String) null);
3092 long duration = Long.parseLong(durationString);
b2c971ec 3093 tsEnd = TmfTimestamp.fromNanos(time + duration);
7697e148
PT
3094 } catch (NumberFormatException e) {
3095 /* ignored */
3096 }
3097 if (rank == -1 && tsBegin != null) {
3098 final ITmfContext context = fTrace.seekEvent(tsBegin);
3099 rank = context.getRank();
3100 context.dispose();
3101 }
db4721fb 3102 if (rank != -1) {
7697e148 3103 int index = (int) rank;
db4721fb 3104 if (fTable.getData(Key.FILTER_OBJ) != null) {
8b7eb3cd
MK
3105 // +1 for top filter status row
3106 index = fCache.getFilteredEventIndex(rank) + 1;
db4721fb
PT
3107 } else if (rank >= fTable.getItemCount()) {
3108 fPendingGotoRank = rank;
3109 }
a56ec2b8 3110 fSelectedRank = rank;
9ba0a108 3111 fSelectedBeginRank = fSelectedRank;
db4721fb 3112 fTable.setSelection(index + 1); // +1 for header row
3f43dc48 3113 updateStatusLine(null);
7697e148
PT
3114 if (tsBegin != null) {
3115 if (tsEnd != null) {
3116 broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin, tsEnd));
3117 } else {
3118 broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, tsBegin));
3119 }
3120 }
db4721fb
PT
3121 }
3122 }
3123
3124 // ------------------------------------------------------------------------
3125 // Listeners
3126 // ------------------------------------------------------------------------
3127
db4721fb
PT
3128 @Override
3129 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
3130 fTable.refresh();
3131 }
3132
db4721fb
PT
3133 // ------------------------------------------------------------------------
3134 // Signal handlers
3135 // ------------------------------------------------------------------------
3136
db4721fb
PT
3137 /**
3138 * Handler for the trace updated signal
3139 *
3140 * @param signal
3141 * The incoming signal
3142 */
3143 @TmfSignalHandler
3144 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
3145 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
3146 return;
3147 }
3148 // Perform the refresh on the UI thread
3149 Display.getDefault().asyncExec(new Runnable() {
3150 @Override
3151 public void run() {
3152 if (!fTable.isDisposed() && (fTrace != null)) {
3153 if (fTable.getData(Key.FILTER_OBJ) == null) {
8b7eb3cd
MK
3154 /* +1 for header row */
3155 fTable.setItemCount((int) fTrace.getNbEvents() + 1);
3156 /* +1 for header row */
3157 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) {
3158 /* +1 for header row */
3159 fTable.setSelection((int) fPendingGotoRank + 1);
db4721fb 3160 fPendingGotoRank = -1;
3f43dc48 3161 updateStatusLine(null);
db4721fb
PT
3162 }
3163 } else {
3164 startFilterThread();
3165 }
3166 }
3167 if (!fRawViewer.isDisposed() && (fTrace != null)) {
3168 fRawViewer.refreshEventCount();
3169 }
3170 }
3171 });
3172 }
3173
3174 /**
97c71024 3175 * Handler for the selection range signal.
db4721fb
PT
3176 *
3177 * @param signal
3178 * The incoming signal
97c71024 3179 * @since 1.0
db4721fb
PT
3180 */
3181 @TmfSignalHandler
97c71024 3182 public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
db4721fb
PT
3183 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
3184
8b7eb3cd
MK
3185 /*
3186 * Create a request for one event that will be queued after other
3187 * ongoing requests. When this request is completed do the work to
3188 * select the actual event with the timestamp specified in the
3189 * signal. This procedure prevents the method fTrace.getRank() from
3190 * interfering and delaying ongoing requests.
3191 */
fd3f1eff
AM
3192 final TmfEventRequest subRequest = new TmfEventRequest(ITmfEvent.class,
3193 TmfTimeRange.ETERNITY, 0, 1, ExecutionType.FOREGROUND) {
db4721fb 3194
b2c971ec
MK
3195 ITmfTimestamp ts = signal.getBeginTime();
3196 ITmfTimestamp tf = signal.getEndTime();
db4721fb
PT
3197
3198 @Override
73fce654
AM
3199 public void handleSuccess() {
3200 super.handleSuccess();
db4721fb
PT
3201 if (fTrace == null) {
3202 return;
3203 }
3204
8b6aedef
JCK
3205 final Pair<Long, Long> selection = getSelectedRanks();
3206 updateDisplayWithSelection(selection.getFirst().longValue(), selection.getSecond().longValue());
3207 }
3208
3209 /**
3210 * Verify if the event is within the trace range and adjust if
3211 * necessary.
3212 *
3213 * @return A pair of rank representing the selected area
3214 **/
3215 private Pair<Long, Long> getSelectedRanks() {
3216
3217 /* Clamp the timestamp value to fit inside of the trace */
3218 ITmfTimestamp timestampBegin = ts;
3219 if (timestampBegin.compareTo(fTrace.getStartTime()) < 0) {
3220 timestampBegin = fTrace.getStartTime();
db4721fb 3221 }
8b6aedef
JCK
3222 if (timestampBegin.compareTo(fTrace.getEndTime()) > 0) {
3223 timestampBegin = fTrace.getEndTime();
db4721fb
PT
3224 }
3225
8b6aedef
JCK
3226 ITmfTimestamp timestampEnd = tf;
3227 if (timestampEnd.compareTo(fTrace.getStartTime()) < 0) {
3228 timestampEnd = fTrace.getStartTime();
3229 }
3230 if (timestampEnd.compareTo(fTrace.getEndTime()) > 0) {
3231 timestampEnd = fTrace.getEndTime();
3232 }
db4721fb 3233
8b6aedef
JCK
3234 ITmfTimestamp tb;
3235 ITmfTimestamp te;
3236 long rankBegin;
3237 long rankEnd;
3238 ITmfContext contextBegin;
3239 ITmfContext contextEnd;
3240
3241 /* Adjust the rank of the selection to the right range */
3242 if (timestampBegin.compareTo(timestampEnd) > 0) {
3243 te = timestampEnd;
3244 contextEnd = fTrace.seekEvent(te);
3245 rankEnd = contextEnd.getRank();
3246 contextEnd.dispose();
b2c971ec
MK
3247 /*
3248 * To include all events at the begin time, seek at the
3249 * next nanosecond and then use the previous rank
3250 */
8b6aedef
JCK
3251 tb = timestampBegin.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
3252 if (tb.compareTo(fTrace.getEndTime()) <= 0) {
3253 contextBegin = fTrace.seekEvent(tb);
3254 rankBegin = contextBegin.getRank();
3255 contextBegin.dispose();
3256 } else {
3257 rankBegin = ITmfContext.UNKNOWN_RANK;
3258 }
3259 rankBegin = (rankBegin == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankBegin) - 1;
b2c971ec
MK
3260 /*
3261 * If no events in selection range, select only the next
3262 * event
3263 */
8b6aedef
JCK
3264 rankBegin = rankBegin >= rankEnd ? rankBegin : rankEnd;
3265 } else {
3266 tb = timestampBegin;
3267 contextBegin = fTrace.seekEvent(tb);
3268 rankBegin = contextBegin.getRank();
3269 contextBegin.dispose();
b2c971ec
MK
3270 /*
3271 * To include all events at the end time, seek at the
3272 * next nanosecond and then use the previous rank
3273 */
8b6aedef
JCK
3274 te = timestampEnd.normalize(1, ITmfTimestamp.NANOSECOND_SCALE);
3275 if (te.compareTo(fTrace.getEndTime()) <= 0) {
3276 contextEnd = fTrace.seekEvent(te);
3277 rankEnd = contextEnd.getRank();
3278 contextEnd.dispose();
3279 } else {
3280 rankEnd = ITmfContext.UNKNOWN_RANK;
3281 }
3282 rankEnd = (rankEnd == ITmfContext.UNKNOWN_RANK ? fTrace.getNbEvents() : rankEnd) - 1;
b2c971ec
MK
3283 /*
3284 * If no events in selection range, select only the next
3285 * event
3286 */
8b6aedef
JCK
3287 rankEnd = rankEnd >= rankBegin ? rankEnd : rankBegin;
3288 }
3289 return new Pair<>(Long.valueOf(rankBegin), Long.valueOf(rankEnd));
3290 }
3291
3292 private void updateDisplayWithSelection(final long rankBegin, final long rankEnd) {
78688b59 3293 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
db4721fb
PT
3294 @Override
3295 public void run() {
3296 // Return if table is disposed
3297 if (fTable.isDisposed()) {
3298 return;
3299 }
3300
8b6aedef
JCK
3301 fSelectedRank = rankEnd;
3302 long toReveal = fSelectedBeginRank != rankBegin ? rankBegin : rankEnd;
3303 fSelectedBeginRank = rankBegin;
3304 int indexBegin = (int) rankBegin;
3305 int indexEnd = (int) rankEnd;
3306
60fb38b8 3307 if (fTable.getData(Key.FILTER_OBJ) != null) {
8b7eb3cd 3308 /* +1 for top filter status row */
8b6aedef
JCK
3309 indexBegin = fCache.getFilteredEventIndex(rankBegin) + 1;
3310 indexEnd = rankEnd == rankBegin ? indexBegin : fCache.getFilteredEventIndex(rankEnd) + 1;
db4721fb 3311 }
8b7eb3cd 3312 /* +1 for header row */
8b6aedef
JCK
3313 fTable.setSelectionRange(indexBegin + 1, indexEnd + 1);
3314 fRawViewer.selectAndReveal(toReveal);
3f43dc48 3315 updateStatusLine(null);
db4721fb
PT
3316 }
3317 });
3318 }
3319 };
3320
fd3f1eff 3321 ((ITmfEventProvider) fTrace).sendRequest(subRequest);
db4721fb
PT
3322 }
3323 }
3324
3325 // ------------------------------------------------------------------------
3326 // Error handling
3327 // ------------------------------------------------------------------------
3328
3329 /**
3330 * Display an exception in a message box
3331 *
8b7eb3cd
MK
3332 * @param e
3333 * the exception
db4721fb
PT
3334 */
3335 private static void displayException(final Exception e) {
3336 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
60fb38b8 3337 mb.setText(e.getClass().getSimpleName());
db4721fb
PT
3338 mb.setMessage(e.getMessage());
3339 mb.open();
3340 }
3341
f8177ba2 3342 /**
ae09c4ad 3343 * Refresh the table
f8177ba2
FC
3344 */
3345 public void refresh() {
3346 fCache.clear();
3347 fTable.refresh();
3348 fTable.redraw();
3349 }
03142470
BH
3350
3351 /**
9447c7ee
AM
3352 * Margin column for images and special text (e.g. collapse count)
3353 */
3354 private static final class TmfMarginColumn extends TmfEventTableColumn {
3355
ec48d248
AM
3356 private static final @NonNull ITmfEventAspect<String> MARGIN_ASPECT =
3357 new ITmfEventAspect<String>() {
9447c7ee
AM
3358
3359 @Override
3360 public String getName() {
3361 return EMPTY_STRING;
3362 }
3363
3364 @Override
3365 public String resolve(ITmfEvent event) {
3366 if (!(event instanceof CachedEvent) || ((CachedEvent) event).repeatCount == 0) {
3367 return EMPTY_STRING;
3368 }
3369 return "+" + ((CachedEvent) event).repeatCount; //$NON-NLS-1$
3370 }
3371
3372 @Override
3373 public String getHelpText() {
3374 return EMPTY_STRING;
3375 }
9447c7ee
AM
3376 };
3377
3378 /**
3379 * Constructor
3380 */
3381 public TmfMarginColumn() {
3382 super(MARGIN_ASPECT);
3383 }
3384 }
03142470 3385
db4721fb 3386}
This page took 0.321617 seconds and 5 git commands to generate.