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