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