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