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