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