tmf: Update copyright headers in tmf.ui
[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)
db4721fb
PT
15 *******************************************************************************/
16
17package org.eclipse.linuxtools.tmf.ui.viewers.events;
18
60fb38b8 19import java.io.FileNotFoundException;
db4721fb
PT
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.HashMap;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.regex.Pattern;
27import java.util.regex.PatternSyntaxException;
28
29import org.eclipse.core.resources.IFile;
30import org.eclipse.core.resources.IMarker;
31import org.eclipse.core.resources.IResource;
60fb38b8 32import org.eclipse.core.resources.IResourceVisitor;
ac44ef71 33import org.eclipse.core.resources.ResourcesPlugin;
db4721fb 34import org.eclipse.core.runtime.CoreException;
ac44ef71 35import org.eclipse.core.runtime.IPath;
db4721fb
PT
36import org.eclipse.core.runtime.IProgressMonitor;
37import org.eclipse.core.runtime.IStatus;
93bfd50a 38import org.eclipse.core.runtime.ListenerList;
ac44ef71 39import org.eclipse.core.runtime.Path;
db4721fb
PT
40import org.eclipse.core.runtime.Status;
41import org.eclipse.core.runtime.jobs.Job;
ac44ef71
AR
42import org.eclipse.emf.common.util.URI;
43import org.eclipse.emf.ecore.EValidator;
db4721fb
PT
44import org.eclipse.jface.action.Action;
45import org.eclipse.jface.action.IAction;
46import org.eclipse.jface.action.IMenuListener;
47import org.eclipse.jface.action.IMenuManager;
48import org.eclipse.jface.action.MenuManager;
49import org.eclipse.jface.action.Separator;
50import org.eclipse.jface.dialogs.InputDialog;
51import org.eclipse.jface.dialogs.MessageDialog;
52import org.eclipse.jface.resource.FontDescriptor;
53import org.eclipse.jface.resource.JFaceResources;
54import org.eclipse.jface.resource.LocalResourceManager;
ac44ef71 55import org.eclipse.jface.util.OpenStrategy;
93bfd50a 56import org.eclipse.jface.util.SafeRunnable;
60fb38b8 57import org.eclipse.jface.viewers.ArrayContentProvider;
93bfd50a
PT
58import org.eclipse.jface.viewers.ISelection;
59import org.eclipse.jface.viewers.ISelectionChangedListener;
60import org.eclipse.jface.viewers.ISelectionProvider;
60fb38b8 61import org.eclipse.jface.viewers.LabelProvider;
93bfd50a
PT
62import org.eclipse.jface.viewers.SelectionChangedEvent;
63import org.eclipse.jface.viewers.StructuredSelection;
db4721fb
PT
64import org.eclipse.jface.window.Window;
65import org.eclipse.linuxtools.internal.tmf.ui.Activator;
66import org.eclipse.linuxtools.internal.tmf.ui.Messages;
3be2946f 67import org.eclipse.linuxtools.internal.tmf.ui.dialogs.MultiLineInputDialog;
db4721fb
PT
68import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;
69import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
60fb38b8
PT
70import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfConstants;
71import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfCallsite;
ac44ef71 72import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfEvent;
db4721fb
PT
73import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
74import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
db4721fb 75import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
db4721fb
PT
76import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
77import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;
78import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;
79import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;
80import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;
81import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;
82import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
faa38350
PT
83import org.eclipse.linuxtools.tmf.core.signal.TmfEventFilterAppliedSignal;
84import org.eclipse.linuxtools.tmf.core.signal.TmfEventSearchAppliedSignal;
db4721fb
PT
85import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
86import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
87import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;
3bd46eef
AM
88import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
89import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
db4721fb
PT
90import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
91import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;
92import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
93import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
94import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
95import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
96import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
97import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
98import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
99import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;
100import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
101import org.eclipse.swt.SWT;
102import org.eclipse.swt.custom.SashForm;
103import org.eclipse.swt.custom.TableEditor;
104import org.eclipse.swt.events.FocusAdapter;
105import org.eclipse.swt.events.FocusEvent;
106import org.eclipse.swt.events.KeyAdapter;
107import org.eclipse.swt.events.KeyEvent;
108import org.eclipse.swt.events.MouseAdapter;
109import org.eclipse.swt.events.MouseEvent;
110import org.eclipse.swt.events.SelectionAdapter;
111import org.eclipse.swt.events.SelectionEvent;
112import org.eclipse.swt.graphics.Color;
113import org.eclipse.swt.graphics.Font;
114import org.eclipse.swt.graphics.Image;
115import org.eclipse.swt.graphics.Point;
116import org.eclipse.swt.graphics.Rectangle;
117import org.eclipse.swt.layout.FillLayout;
118import org.eclipse.swt.layout.GridData;
119import org.eclipse.swt.layout.GridLayout;
120import org.eclipse.swt.widgets.Composite;
121import org.eclipse.swt.widgets.Display;
122import org.eclipse.swt.widgets.Event;
123import org.eclipse.swt.widgets.Label;
124import org.eclipse.swt.widgets.Listener;
125import org.eclipse.swt.widgets.Menu;
126import org.eclipse.swt.widgets.MessageBox;
127import org.eclipse.swt.widgets.Shell;
128import org.eclipse.swt.widgets.TableColumn;
129import org.eclipse.swt.widgets.TableItem;
130import org.eclipse.swt.widgets.Text;
ac44ef71 131import org.eclipse.ui.IWorkbenchPage;
60fb38b8 132import org.eclipse.ui.PartInitException;
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();
597 if (data instanceof CtfTmfEvent) {
598 CtfTmfEvent event = (CtfTmfEvent) data;
599 CtfTmfCallsite cs = event.getCallsite();
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 }
646 } catch (PartInitException e) {
647 // TODO Auto-generated catch block
648 e.printStackTrace();
649 } catch (CoreException e) {
650 // TODO Auto-generated catch block
651 e.printStackTrace();
652 }
653 }
654 }
655 };
656
657 final IAction openModelAction = new Action(Messages.TmfEventsTable_OpenModelActionText) {
ac44ef71
AR
658 @Override
659 public void run() {
660
661 final TableItem items[] = fTable.getSelection();
662 if (items.length != 1) {
663 return;
664 }
665 final TableItem item = items[0];
666
667 final Object eventData = item.getData();
668 if (eventData instanceof CtfTmfEvent) {
60fb38b8 669 String modelURI = ((CtfTmfEvent) eventData).getCustomAttribute(CtfConstants.MODEL_URI_KEY);
ac44ef71
AR
670
671 if (modelURI != null) {
672 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
673
674 IFile file = null;
675 final URI uri = URI.createURI(modelURI);
676 if (uri.isPlatformResource()) {
677 IPath path = new Path(uri.toPlatformString(true));
678 file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
679 } else if (uri.isFile() && !uri.isRelative()) {
680 file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
681 new Path(uri.toFileString()));
682 }
683
684 if (file != null) {
685 try {
686 /*
687 * create a temporary validation marker on the
688 * model file, remove it afterwards thus,
689 * navigation works with all model editors
690 * supporting the navigation to a marker
691 */
692 IMarker marker = file.createMarker(EValidator.MARKER);
693 marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
694 marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
695
696 IDE.openEditor(activePage, marker, OpenStrategy.activateOnOpen());
697 marker.delete();
698 }
699 catch (CoreException e) {
700 displayException(e);
701 }
60fb38b8
PT
702 } else {
703 displayException(new FileNotFoundException('\'' + modelURI + '\'' + '\n' + Messages.TmfEventsTable_OpenModelUnsupportedURI));
ac44ef71
AR
704 }
705 }
706 }
707 }
708 };
60fb38b8 709
db4721fb
PT
710 final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
711 @Override
712 public void run() {
713 fHeaderState = HeaderState.SEARCH;
714 fTable.refresh();
715 }
716 };
717
718 final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
719 @Override
720 public void run() {
721 fHeaderState = HeaderState.FILTER;
722 fTable.refresh();
723 }
724 };
725
726 final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
727 @Override
728 public void run() {
729 clearFilters();
730 }
731 };
732
733 class ToggleBookmarkAction extends Action {
734 long fRank;
735
736 public ToggleBookmarkAction(final String text, final long rank) {
737 super(text);
738 fRank = rank;
739 }
740
741 @Override
742 public void run() {
743 toggleBookmark(fRank);
744 }
745 }
746
747 final MenuManager tablePopupMenu = new MenuManager();
748 tablePopupMenu.setRemoveAllWhenShown(true);
749 tablePopupMenu.addMenuListener(new IMenuListener() {
750 @Override
751 public void menuAboutToShow(final IMenuManager manager) {
752 if (fTable.getSelectionIndex() == 0) {
753 // Right-click on header row
754 if (fHeaderState == HeaderState.FILTER) {
755 tablePopupMenu.add(showSearchBarAction);
756 } else {
757 tablePopupMenu.add(showFilterBarAction);
758 }
759 return;
760 }
761 final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
762 final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
763 if (item != null) {
764 final Rectangle imageBounds = item.getImageBounds(0);
765 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
766 if (point.x <= (imageBounds.x + imageBounds.width)) {
767 // Right-click on left margin
768 final Long rank = (Long) item.getData(Key.RANK);
769 if ((rank != null) && (fBookmarksFile != null)) {
770 if (fBookmarksMap.containsKey(rank)) {
771 tablePopupMenu.add(new ToggleBookmarkAction(
772 Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
773 } else {
774 tablePopupMenu.add(new ToggleBookmarkAction(
775 Messages.TmfEventsTable_AddBookmarkActionText, rank));
776 }
777 }
778 return;
779 }
780 }
60fb38b8 781
db4721fb
PT
782 // Right-click on table
783 if (fTable.isVisible() && fRawViewer.isVisible()) {
784 tablePopupMenu.add(hideTableAction);
785 tablePopupMenu.add(hideRawAction);
786 } else if (!fTable.isVisible()) {
787 tablePopupMenu.add(showTableAction);
788 } else if (!fRawViewer.isVisible()) {
789 tablePopupMenu.add(showRawAction);
790 }
791 tablePopupMenu.add(new Separator());
60fb38b8 792
ac44ef71 793 if (item != null) {
60fb38b8
PT
794 final Object data = item.getData();
795 if (data instanceof CtfTmfEvent) {
796 Separator separator = null;
797 CtfTmfEvent event = (CtfTmfEvent) data;
798 if (event.getCallsite() != null) {
799 tablePopupMenu.add(openCallsiteAction);
800 separator = new Separator();
801 }
802 if (event.listCustomAttributes().contains(CtfConstants.MODEL_URI_KEY)) {
803 tablePopupMenu.add(openModelAction);
804 separator = new Separator();
805 }
806 if (separator != null) {
807 tablePopupMenu.add(separator);
ac44ef71
AR
808 }
809 }
810 }
60fb38b8 811
db4721fb
PT
812 tablePopupMenu.add(clearFiltersAction);
813 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
814 if (savedFilters.length > 0) {
815 final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
816 for (final ITmfFilterTreeNode node : savedFilters) {
817 if (node instanceof TmfFilterNode) {
818 final TmfFilterNode filter = (TmfFilterNode) node;
819 subMenu.add(new Action(filter.getFilterName()) {
820 @Override
821 public void run() {
822 applyFilter(filter);
823 }
824 });
825 }
826 }
827 tablePopupMenu.add(subMenu);
828 }
829 appendToTablePopupMenu(tablePopupMenu, item);
830 }
831 });
832
833 final MenuManager rawViewerPopupMenu = new MenuManager();
834 rawViewerPopupMenu.setRemoveAllWhenShown(true);
835 rawViewerPopupMenu.addMenuListener(new IMenuListener() {
836 @Override
837 public void menuAboutToShow(final IMenuManager manager) {
838 if (fTable.isVisible() && fRawViewer.isVisible()) {
839 rawViewerPopupMenu.add(hideTableAction);
840 rawViewerPopupMenu.add(hideRawAction);
841 } else if (!fTable.isVisible()) {
842 rawViewerPopupMenu.add(showTableAction);
843 } else if (!fRawViewer.isVisible()) {
844 rawViewerPopupMenu.add(showRawAction);
845 }
846 appendToRawPopupMenu(tablePopupMenu);
847 }
848 });
849
850 Menu menu = tablePopupMenu.createContextMenu(fTable);
851 fTable.setMenu(menu);
852
853 menu = rawViewerPopupMenu.createContextMenu(fRawViewer);
854 fRawViewer.setMenu(menu);
855 }
856
f8177ba2 857
a0a88f65
AM
858 /**
859 * Append an item to the event table's pop-up menu.
860 *
861 * @param tablePopupMenu
862 * The menu manager
863 * @param selectedItem
864 * The item to append
865 */
db4721fb
PT
866 protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
867 // override to append more actions
868 }
869
a0a88f65
AM
870 /**
871 * Append an item to the raw viewer's pop-up menu.
872 *
873 * @param rawViewerPopupMenu
874 * The menu manager
875 */
db4721fb
PT
876 protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
877 // override to append more actions
878 }
879
880 @Override
881 public void dispose() {
882 stopSearchThread();
883 stopFilterThread();
884 ColorSettingsManager.removeColorSettingsListener(this);
885 fComposite.dispose();
886 if ((fTrace != null) && fDisposeOnClose) {
887 fTrace.dispose();
888 }
889 fResourceManager.dispose();
890 super.dispose();
891 }
892
893 /**
894 * Assign a layout data object to this view.
895 *
896 * @param layoutData
897 * The layout data to assign
898 */
899 public void setLayoutData(final Object layoutData) {
900 fComposite.setLayoutData(layoutData);
901 }
902
903 /**
904 * Get the virtual table contained in this event table.
905 *
906 * @return The TMF virtual table
907 */
908 public TmfVirtualTable getTable() {
909 return fTable;
910 }
911
912 /**
913 * @param columnData
914 *
915 * FIXME: Add support for column selection
916 */
917 protected void setColumnHeaders(final ColumnData[] columnData) {
918 fTable.setColumnHeaders(columnData);
919 }
920
a0a88f65
AM
921 /**
922 * Set a table item's data.
923 *
924 * @param item
925 * The item to set
926 * @param event
927 * Which trace event to link with this entry
928 * @param rank
929 * Which rank this event has in the trace/experiment
930 */
db4721fb
PT
931 protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
932 final ITmfEventField[] fields = extractItemFields(event);
933 final String[] content = new String[fields.length];
934 for (int i = 0; i < fields.length; i++) {
935 content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$
936 }
937 item.setText(content);
93bfd50a 938 item.setData(event);
db4721fb
PT
939 item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
940 item.setData(Key.RANK, rank);
941
942 boolean bookmark = false;
943 final Long markerId = fBookmarksMap.get(rank);
944 if (markerId != null) {
945 bookmark = true;
946 try {
947 final IMarker marker = fBookmarksFile.findMarker(markerId);
948 item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));
949 } catch (final CoreException e) {
950 displayException(e);
951 }
952 } else {
953 item.setData(Key.BOOKMARK, null);
954 }
955
956 boolean searchMatch = false;
957 boolean searchNoMatch = false;
958 final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
959 if (searchFilter != null) {
960 if (searchFilter.matches(event)) {
961 searchMatch = true;
962 } else {
963 searchNoMatch = true;
964 }
965 }
966
967 final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);
968 if (searchNoMatch) {
969 item.setForeground(colorSetting.getDimmedForegroundColor());
970 item.setBackground(colorSetting.getDimmedBackgroundColor());
971 } else {
972 item.setForeground(colorSetting.getForegroundColor());
973 item.setBackground(colorSetting.getBackgroundColor());
974 }
975
976 if (searchMatch) {
977 if (bookmark) {
978 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
979 } else {
980 item.setImage(SEARCH_MATCH_IMAGE);
981 }
982 } else if (bookmark) {
983 item.setImage(BOOKMARK_IMAGE);
984 } else {
985 item.setImage((Image) null);
986 }
987 }
988
a0a88f65
AM
989 /**
990 * Set the item data of the header row.
991 *
992 * @param item
993 * The item to use as table header
994 */
db4721fb
PT
995 protected void setHeaderRowItemData(final TableItem item) {
996 String txtKey = null;
997 if (fHeaderState == HeaderState.SEARCH) {
998 item.setImage(SEARCH_IMAGE);
999 txtKey = Key.SEARCH_TXT;
1000 } else if (fHeaderState == HeaderState.FILTER) {
1001 item.setImage(FILTER_IMAGE);
1002 txtKey = Key.FILTER_TXT;
1003 }
1004 item.setForeground(fGrayColor);
1005 for (int i = 0; i < fTable.getColumns().length; i++) {
1006 final TableColumn column = fTable.getColumns()[i];
1007 final String filter = (String) column.getData(txtKey);
1008 if (filter == null) {
1009 if (fHeaderState == HeaderState.SEARCH) {
1010 item.setText(i, SEARCH_HINT);
1011 } else if (fHeaderState == HeaderState.FILTER) {
1012 item.setText(i, FILTER_HINT);
1013 }
1014 item.setForeground(i, fGrayColor);
1015 item.setFont(i, fTable.getFont());
1016 } else {
1017 item.setText(i, filter);
1018 item.setForeground(i, fGreenColor);
1019 item.setFont(i, fBoldFont);
1020 }
1021 }
1022 }
1023
a0a88f65
AM
1024 /**
1025 * Set the item data of the "filter status" row.
1026 *
1027 * @param item
1028 * The item to use as filter status row
1029 */
db4721fb
PT
1030 protected void setFilterStatusRowItemData(final TableItem item) {
1031 for (int i = 0; i < fTable.getColumns().length; i++) {
1032 if (i == 0) {
1033 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
1034 item.setImage(FILTER_IMAGE);
1035 } else {
1036 item.setImage(STOP_IMAGE);
1037 }
1038 item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
1039 } else {
1040 item.setText(i, ""); //$NON-NLS-1$
1041 }
1042 }
93bfd50a 1043 item.setData(null);
db4721fb
PT
1044 item.setData(Key.TIMESTAMP, null);
1045 item.setData(Key.RANK, null);
1046 item.setForeground(null);
1047 item.setBackground(null);
1048 }
1049
a0a88f65
AM
1050 /**
1051 * Create an editor for the header.
1052 */
db4721fb
PT
1053 protected void createHeaderEditor() {
1054 final TableEditor tableEditor = fTable.createTableEditor();
1055 tableEditor.horizontalAlignment = SWT.LEFT;
1056 tableEditor.verticalAlignment = SWT.CENTER;
1057 tableEditor.grabHorizontal = true;
1058 tableEditor.minimumWidth = 50;
1059
1060 // Handle the header row selection
1061 fTable.addMouseListener(new MouseAdapter() {
1062 int columnIndex;
1063 TableColumn column;
1064 TableItem item;
1065
1066 @Override
1067 public void mouseDown(final MouseEvent event) {
1068 if (event.button != 1) {
1069 return;
1070 }
1071 // Identify the selected row
1072 final Point point = new Point(event.x, event.y);
1073 item = fTable.getItem(point);
1074
1075 // Header row selected
1076 if ((item != null) && (fTable.indexOf(item) == 0)) {
1077
1078 // Icon selected
1079 if (item.getImageBounds(0).contains(point)) {
1080 if (fHeaderState == HeaderState.SEARCH) {
1081 fHeaderState = HeaderState.FILTER;
1082 } else if (fHeaderState == HeaderState.FILTER) {
1083 fHeaderState = HeaderState.SEARCH;
1084 }
1085 fTable.refresh();
1086 return;
1087 }
1088
1089 // Identify the selected column
1090 columnIndex = -1;
1091 for (int i = 0; i < fTable.getColumns().length; i++) {
1092 final Rectangle rect = item.getBounds(i);
1093 if (rect.contains(point)) {
1094 columnIndex = i;
1095 break;
1096 }
1097 }
1098
1099 if (columnIndex == -1) {
1100 return;
1101 }
1102
1103 column = fTable.getColumns()[columnIndex];
1104
1105 String txtKey = null;
1106 if (fHeaderState == HeaderState.SEARCH) {
1107 txtKey = Key.SEARCH_TXT;
1108 } else if (fHeaderState == HeaderState.FILTER) {
1109 txtKey = Key.FILTER_TXT;
1110 }
1111
1112 // The control that will be the editor must be a child of the Table
1113 final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
1114 final String headerString = (String) column.getData(txtKey);
1115 if (headerString != null) {
1116 newEditor.setText(headerString);
1117 }
1118 newEditor.addFocusListener(new FocusAdapter() {
1119 @Override
1120 public void focusLost(final FocusEvent e) {
1121 final boolean changed = updateHeader(newEditor.getText());
1122 if (changed) {
1123 applyHeader();
1124 }
1125 }
1126 });
1127 newEditor.addKeyListener(new KeyAdapter() {
1128 @Override
1129 public void keyPressed(final KeyEvent e) {
1130 if (e.character == SWT.CR) {
1131 updateHeader(newEditor.getText());
1132 applyHeader();
1133 } else if (e.character == SWT.ESC) {
1134 tableEditor.getEditor().dispose();
1135 }
1136 }
1137 });
1138 newEditor.selectAll();
1139 newEditor.setFocus();
1140 tableEditor.setEditor(newEditor, item, columnIndex);
1141 }
1142 }
1143
1144 /*
1145 * returns true is value was changed
1146 */
1147 private boolean updateHeader(final String text) {
1148 String objKey = null;
1149 String txtKey = null;
1150 if (fHeaderState == HeaderState.SEARCH) {
1151 objKey = Key.SEARCH_OBJ;
1152 txtKey = Key.SEARCH_TXT;
1153 } else if (fHeaderState == HeaderState.FILTER) {
1154 objKey = Key.FILTER_OBJ;
1155 txtKey = Key.FILTER_TXT;
1156 }
1157 if (text.trim().length() > 0) {
1158 try {
1159 final String regex = TmfFilterMatchesNode.regexFix(text);
1160 Pattern.compile(regex);
1161 if (regex.equals(column.getData(txtKey))) {
1162 tableEditor.getEditor().dispose();
1163 return false;
1164 }
1165 final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
1166 String fieldId = (String) column.getData(Key.FIELD_ID);
1167 if (fieldId == null) {
1168 fieldId = column.getText();
1169 }
1170 filter.setField(fieldId);
1171 filter.setRegex(regex);
1172 column.setData(objKey, filter);
1173 column.setData(txtKey, regex);
1174 } catch (final PatternSyntaxException ex) {
1175 tableEditor.getEditor().dispose();
1176 MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1177 ex.getDescription(), ex.getMessage());
1178 return false;
1179 }
1180 } else {
1181 if (column.getData(txtKey) == null) {
1182 tableEditor.getEditor().dispose();
1183 return false;
1184 }
1185 column.setData(objKey, null);
1186 column.setData(txtKey, null);
1187 }
1188 return true;
1189 }
1190
1191 private void applyHeader() {
1192 if (fHeaderState == HeaderState.SEARCH) {
1193 stopSearchThread();
1194 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
3dca7aa5
AM
1195 for (final TableColumn col : fTable.getColumns()) {
1196 final Object filterObj = col.getData(Key.SEARCH_OBJ);
db4721fb
PT
1197 if (filterObj instanceof ITmfFilterTreeNode) {
1198 filter.addChild((ITmfFilterTreeNode) filterObj);
1199 }
1200 }
1201 if (filter.getChildrenCount() > 0) {
1202 fTable.setData(Key.SEARCH_OBJ, filter);
1203 fTable.refresh();
1204 searchNext();
1205 fireSearchApplied(filter);
1206 } else {
1207 fTable.setData(Key.SEARCH_OBJ, null);
1208 fTable.refresh();
1209 fireSearchApplied(null);
1210 }
1211 } else if (fHeaderState == HeaderState.FILTER) {
1212 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
3dca7aa5
AM
1213 for (final TableColumn col : fTable.getColumns()) {
1214 final Object filterObj = col.getData(Key.FILTER_OBJ);
db4721fb
PT
1215 if (filterObj instanceof ITmfFilterTreeNode) {
1216 filter.addChild((ITmfFilterTreeNode) filterObj);
1217 }
1218 }
1219 if (filter.getChildrenCount() > 0) {
1220 applyFilter(filter);
1221 } else {
1222 clearFilters();
1223 }
1224 }
1225
1226 tableEditor.getEditor().dispose();
1227 }
1228 });
1229
1230 fTable.addKeyListener(new KeyAdapter() {
1231 @Override
1232 public void keyPressed(final KeyEvent e) {
1233 e.doit = false;
1234 if (e.character == SWT.ESC) {
1235 stopFilterThread();
1236 stopSearchThread();
1237 fTable.refresh();
1238 } else if (e.character == SWT.DEL) {
1239 if (fHeaderState == HeaderState.SEARCH) {
1240 stopSearchThread();
1241 for (final TableColumn column : fTable.getColumns()) {
1242 column.setData(Key.SEARCH_OBJ, null);
1243 column.setData(Key.SEARCH_TXT, null);
1244 }
1245 fTable.setData(Key.SEARCH_OBJ, null);
1246 fTable.refresh();
1247 fireSearchApplied(null);
1248 } else if (fHeaderState == HeaderState.FILTER) {
1249 clearFilters();
1250 }
1251 } else if (e.character == SWT.CR) {
1252 if ((e.stateMask & SWT.SHIFT) == 0) {
1253 searchNext();
1254 } else {
1255 searchPrevious();
1256 }
1257 }
1258 }
1259 });
1260 }
1261
a0a88f65
AM
1262 /**
1263 * Send an event indicating a filter has been applied.
1264 *
1265 * @param filter
1266 * The filter that was just applied
1267 */
db4721fb 1268 protected void fireFilterApplied(final ITmfFilter filter) {
faa38350 1269 broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
db4721fb
PT
1270 }
1271
a0a88f65
AM
1272 /**
1273 * Send an event indicating that a search has been applied.
1274 *
1275 * @param filter
1276 * The search filter that was just applied
1277 */
db4721fb 1278 protected void fireSearchApplied(final ITmfFilter filter) {
faa38350 1279 broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
db4721fb
PT
1280 }
1281
a0a88f65
AM
1282 /**
1283 * Start the filtering thread.
1284 */
db4721fb
PT
1285 protected void startFilterThread() {
1286 synchronized (fFilterSyncObj) {
1287 final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1288 if (fFilterThread == null || fFilterThread.filter != filter) {
1289 if (fFilterThread != null) {
1290 fFilterThread.cancel();
1291 fFilterThreadResume = false;
1292 }
1293 fFilterThread = new FilterThread(filter);
1294 fFilterThread.start();
1295 } else {
1296 fFilterThreadResume = true;
1297 }
1298 }
1299 }
1300
a0a88f65
AM
1301 /**
1302 * Stop the filtering thread.
1303 */
db4721fb
PT
1304 protected void stopFilterThread() {
1305 synchronized (fFilterSyncObj) {
1306 if (fFilterThread != null) {
1307 fFilterThread.cancel();
1308 fFilterThread = null;
1309 fFilterThreadResume = false;
1310 }
1311 }
1312 }
1313
1314 /**
a0a88f65
AM
1315 * Apply a filter.
1316 *
1317 * @param filter
1318 * The filter to apply
db4721fb
PT
1319 * @since 1.1
1320 */
1321 protected void applyFilter(ITmfFilter filter) {
f29f8868
BH
1322 stopFilterThread();
1323 stopSearchThread();
db4721fb
PT
1324 fFilterMatchCount = 0;
1325 fFilterCheckCount = 0;
1326 fCache.applyFilter(filter);
1327 fTable.clearAll();
1328 fTable.setData(Key.FILTER_OBJ, filter);
1329 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1330 startFilterThread();
1331 fireFilterApplied(filter);
1332 }
1333
a0a88f65
AM
1334 /**
1335 * Clear all currently active filters.
1336 */
db4721fb
PT
1337 protected void clearFilters() {
1338 if (fTable.getData(Key.FILTER_OBJ) == null) {
1339 return;
1340 }
1341 stopFilterThread();
1342 stopSearchThread();
1343 fCache.clearFilter();
1344 fTable.clearAll();
1345 for (final TableColumn column : fTable.getColumns()) {
1346 column.setData(Key.FILTER_OBJ, null);
1347 column.setData(Key.FILTER_TXT, null);
1348 }
1349 fTable.setData(Key.FILTER_OBJ, null);
1350 if (fTrace != null) {
1351 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1352 } else {
1353 fTable.setItemCount(1); // +1 for header row
1354 }
1355 fFilterMatchCount = 0;
1356 fFilterCheckCount = 0;
1357 if (fSelectedRank >= 0) {
1358 fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
1359 } else {
1360 fTable.setSelection(0);
1361 }
1362 fireFilterApplied(null);
1363 }
1364
a0a88f65
AM
1365 /**
1366 * Wrapper Thread object for the filtering thread.
1367 */
db4721fb
PT
1368 protected class FilterThread extends Thread {
1369 private final ITmfFilterTreeNode filter;
1370 private TmfDataRequest request;
1371 private boolean refreshBusy = false;
1372 private boolean refreshPending = false;
1373 private final Object syncObj = new Object();
1374
a0a88f65
AM
1375 /**
1376 * Constructor.
1377 *
1378 * @param filter
1379 * The filter this thread will be processing
1380 */
db4721fb
PT
1381 public FilterThread(final ITmfFilterTreeNode filter) {
1382 super("Filter Thread"); //$NON-NLS-1$
1383 this.filter = filter;
1384 }
1385
1386 @Override
1387 public void run() {
1388 if (fTrace == null) {
1389 return;
1390 }
1391 final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
1392 if (nbRequested <= 0) {
1393 return;
1394 }
1395 request = new TmfDataRequest(ITmfEvent.class, (int) fFilterCheckCount,
1396 nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1397 @Override
1398 public void handleData(final ITmfEvent event) {
1399 super.handleData(event);
1400 if (request.isCancelled()) {
1401 return;
1402 }
1403 if (filter.matches(event)) {
1404 final long rank = fFilterCheckCount;
1405 final int index = (int) fFilterMatchCount;
1406 fFilterMatchCount++;
bd54d363 1407 fCache.storeEvent(event, rank, index);
db4721fb
PT
1408 refreshTable();
1409 } else if ((fFilterCheckCount % 100) == 0) {
1410 refreshTable();
1411 }
1412 fFilterCheckCount++;
1413 }
1414 };
1415 ((ITmfDataProvider) fTrace).sendRequest(request);
1416 try {
1417 request.waitForCompletion();
1418 } catch (final InterruptedException e) {
1419 }
1420 refreshTable();
1421 synchronized (fFilterSyncObj) {
1422 fFilterThread = null;
1423 if (fFilterThreadResume) {
1424 fFilterThreadResume = false;
1425 fFilterThread = new FilterThread(filter);
1426 fFilterThread.start();
1427 }
1428 }
1429 }
1430
a0a88f65
AM
1431 /**
1432 * Refresh the filter.
1433 */
db4721fb
PT
1434 public void refreshTable() {
1435 synchronized (syncObj) {
1436 if (refreshBusy) {
1437 refreshPending = true;
1438 return;
1439 }
1440 refreshBusy = true;
1441 }
1442 Display.getDefault().asyncExec(new Runnable() {
1443 @Override
1444 public void run() {
1445 if (request.isCancelled()) {
1446 return;
1447 }
1448 if (fTable.isDisposed()) {
1449 return;
1450 }
1451 fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
1452 fTable.refresh();
1453 synchronized (syncObj) {
1454 refreshBusy = false;
1455 if (refreshPending) {
1456 refreshPending = false;
1457 refreshTable();
1458 }
1459 }
1460 }
1461 });
1462 }
1463
a0a88f65
AM
1464 /**
1465 * Cancel this filtering thread.
1466 */
db4721fb
PT
1467 public void cancel() {
1468 if (request != null) {
1469 request.cancel();
1470 }
1471 }
1472 }
1473
a0a88f65
AM
1474 /**
1475 * Go to the next item of a search.
1476 */
db4721fb
PT
1477 protected void searchNext() {
1478 synchronized (fSearchSyncObj) {
1479 if (fSearchThread != null) {
1480 return;
1481 }
1482 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1483 if (searchFilter == null) {
1484 return;
1485 }
1486 final int selectionIndex = fTable.getSelectionIndex();
1487 int startIndex;
1488 if (selectionIndex > 0) {
1489 startIndex = selectionIndex; // -1 for header row, +1 for next event
1490 } else {
1491 // header row is selected, start at top event
1492 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
1493 }
1494 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 1495 if (eventFilter != null) {
db4721fb
PT
1496 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
1497 }
1498 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
1499 fSearchThread.schedule();
1500 }
1501 }
1502
a0a88f65
AM
1503 /**
1504 * Go to the previous item of a search.
1505 */
db4721fb
PT
1506 protected void searchPrevious() {
1507 synchronized (fSearchSyncObj) {
1508 if (fSearchThread != null) {
1509 return;
1510 }
1511 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1512 if (searchFilter == null) {
1513 return;
1514 }
1515 final int selectionIndex = fTable.getSelectionIndex();
1516 int startIndex;
1517 if (selectionIndex > 0) {
1518 startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
1519 } else {
1520 // header row is selected, start at precedent of top event
1521 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
1522 }
1523 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
60fb38b8 1524 if (eventFilter != null) {
db4721fb
PT
1525 startIndex = startIndex - 1; // -1 for top filter status row
1526 }
1527 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
1528 fSearchThread.schedule();
1529 }
1530 }
1531
a0a88f65
AM
1532 /**
1533 * Stop the search thread.
1534 */
db4721fb
PT
1535 protected void stopSearchThread() {
1536 fPendingGotoRank = -1;
1537 synchronized (fSearchSyncObj) {
1538 if (fSearchThread != null) {
1539 fSearchThread.cancel();
1540 fSearchThread = null;
1541 }
1542 }
1543 }
1544
a0a88f65
AM
1545 /**
1546 * Wrapper for the search thread.
1547 */
db4721fb 1548 protected class SearchThread extends Job {
a0a88f65
AM
1549
1550 private ITmfFilterTreeNode searchFilter;
1551 private ITmfFilterTreeNode eventFilter;
1552 private int startIndex;
1553 private int direction;
1554 private long rank;
1555 private long foundRank = -1;
1556 private TmfDataRequest request;
ae3ffd37 1557 private ITmfTimestamp foundTimestamp = null;
db4721fb 1558
a0a88f65
AM
1559 /**
1560 * Constructor.
1561 *
1562 * @param searchFilter
1563 * The search filter
1564 * @param eventFilter
1565 * The event filter
1566 * @param startIndex
1567 * The index at which we should start searching
1568 * @param currentRank
1569 * The current rank
1570 * @param direction
1571 * In which direction should we search, forward or backwards
1572 */
1573 public SearchThread(final ITmfFilterTreeNode searchFilter,
1574 final ITmfFilterTreeNode eventFilter, final int startIndex,
db4721fb
PT
1575 final long currentRank, final int direction) {
1576 super(Messages.TmfEventsTable_SearchingJobName);
1577 this.searchFilter = searchFilter;
1578 this.eventFilter = eventFilter;
1579 this.startIndex = startIndex;
1580 this.rank = currentRank;
1581 this.direction = direction;
1582 }
1583
1584 @Override
1585 protected IStatus run(final IProgressMonitor monitor) {
1586 if (fTrace == null) {
1587 return Status.OK_STATUS;
1588 }
1589 final Display display = Display.getDefault();
1590 if (startIndex < 0) {
1591 rank = (int) fTrace.getNbEvents() - 1;
1592 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
1593 rank = 0;
1594 } else {
1595 int idx = startIndex;
1596 while (foundRank == -1) {
1597 final CachedEvent event = fCache.peekEvent(idx);
1598 if (event == null) {
1599 break;
1600 }
1601 rank = event.rank;
1602 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
1603 foundRank = event.rank;
ae3ffd37 1604 foundTimestamp = event.event.getTimestamp();
db4721fb
PT
1605 break;
1606 }
1607 if (direction == Direction.FORWARD) {
1608 idx++;
1609 } else {
1610 idx--;
1611 }
1612 }
1613 if (foundRank == -1) {
1614 if (direction == Direction.FORWARD) {
1615 rank++;
1616 if (rank > (fTrace.getNbEvents() - 1)) {
1617 rank = 0;
1618 }
1619 } else {
1620 rank--;
1621 if (rank < 0) {
1622 rank = (int) fTrace.getNbEvents() - 1;
1623 }
1624 }
1625 }
1626 }
1627 final int startRank = (int) rank;
1628 boolean wrapped = false;
1629 while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
1630 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1631 if (direction == Direction.BACKWARD) {
1632 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1633 }
1634 request = new TmfDataRequest(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1635 long currentRank = rank;
1636
1637 @Override
1638 public void handleData(final ITmfEvent event) {
1639 super.handleData(event);
1640 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
1641 foundRank = currentRank;
ae3ffd37 1642 foundTimestamp = event.getTimestamp();
db4721fb
PT
1643 if (direction == Direction.FORWARD) {
1644 done();
1645 return;
1646 }
1647 }
1648 currentRank++;
1649 }
1650 };
1651 ((ITmfDataProvider) fTrace).sendRequest(request);
1652 try {
1653 request.waitForCompletion();
1654 if (request.isCancelled()) {
1655 return Status.OK_STATUS;
1656 }
1657 } catch (final InterruptedException e) {
1658 synchronized (fSearchSyncObj) {
1659 fSearchThread = null;
1660 }
1661 return Status.OK_STATUS;
1662 }
1663 if (foundRank == -1) {
1664 if (direction == Direction.FORWARD) {
1665 if (rank == 0) {
1666 synchronized (fSearchSyncObj) {
1667 fSearchThread = null;
1668 }
1669 return Status.OK_STATUS;
1670 }
1671 nbRequested = (int) rank;
1672 rank = 0;
1673 wrapped = true;
1674 } else {
1675 rank--;
1676 if (rank < 0) {
1677 rank = (int) fTrace.getNbEvents() - 1;
1678 wrapped = true;
1679 }
1680 if ((rank <= startRank) && wrapped) {
1681 synchronized (fSearchSyncObj) {
1682 fSearchThread = null;
1683 }
1684 return Status.OK_STATUS;
1685 }
1686 }
1687 }
1688 }
1689 int index = (int) foundRank;
1690 if (eventFilter != null) {
1691 index = fCache.getFilteredEventIndex(foundRank);
1692 }
1693 final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1694
1695 display.asyncExec(new Runnable() {
1696 @Override
1697 public void run() {
1698 if (monitor.isCanceled()) {
1699 return;
1700 }
1701 if (fTable.isDisposed()) {
1702 return;
1703 }
1704 fTable.setSelection(selection);
1705 fSelectedRank = foundRank;
ae3ffd37
PT
1706 fRawViewer.selectAndReveal(fSelectedRank);
1707 if (foundTimestamp != null) {
1708 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, foundTimestamp));
1709 }
a56ec2b8 1710 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
db4721fb
PT
1711 synchronized (fSearchSyncObj) {
1712 fSearchThread = null;
1713 }
1714 }
1715 });
1716 return Status.OK_STATUS;
1717 }
1718
1719 @Override
1720 protected void canceling() {
1721 request.cancel();
1722 synchronized (fSearchSyncObj) {
1723 fSearchThread = null;
1724 }
1725 }
1726 }
1727
a0a88f65
AM
1728 /**
1729 * Create the resources.
1730 */
db4721fb
PT
1731 protected void createResources() {
1732 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
1733 .getForeground().getRGB()));
1734 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1735 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1736 }
1737
a0a88f65
AM
1738 /**
1739 * Pack the columns.
1740 */
db4721fb
PT
1741 protected void packColumns() {
1742 if (fPackDone) {
1743 return;
1744 }
f29f8868
BH
1745 boolean isLinux = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
1746
1747 TableColumn tableColumns[] = fTable.getColumns();
1748 for (int i = 0; i < tableColumns.length; i++) {
1749 final TableColumn column = tableColumns[i];
db4721fb
PT
1750 final int headerWidth = column.getWidth();
1751 column.pack();
f29f8868
BH
1752 // Workaround for Linux which doesn't consider the image width of
1753 // search/filter row in TableColumn.pack() after having executed
1754 // TableItem.setImage((Image)null) for other rows than search/filter row.
1755 if (isLinux && (i == 0)) {
1756 column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
1757 }
1758
db4721fb
PT
1759 if (column.getWidth() < headerWidth) {
1760 column.setWidth(headerWidth);
1761 }
1762 }
1763 fPackDone = true;
1764 }
1765
1766 /**
a0a88f65
AM
1767 * Extract the fields of an event (item in the table).
1768 *
db4721fb 1769 * @param event
a0a88f65
AM
1770 * The event to extract from
1771 * @return The array of fields
db4721fb
PT
1772 *
1773 * FIXME: Add support for column selection
1774 */
1775 protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
1776 ITmfEventField[] fields = new TmfEventField[0];
1777 if (event != null) {
1778 final String timestamp = event.getTimestamp().toString();
1779 final String source = event.getSource();
1780 final String type = event.getType().getName();
1781 final String reference = event.getReference();
a4a6acf4 1782 final String content = event.getContent().toString();
db4721fb
PT
1783 fields = new TmfEventField[] {
1784 new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),
1785 new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),
1786 new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),
1787 new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),
a4a6acf4 1788 new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, content)
db4721fb
PT
1789 };
1790 }
1791 return fields;
1792 }
1793
1794 /**
1795 * Notify this table that is got the UI focus.
1796 */
1797 public void setFocus() {
1798 fTable.setFocus();
1799 }
1800
1801 /**
1802 * Assign a new trace to this event table.
1803 *
1804 * @param trace
1805 * The trace to assign to this event table
1806 * @param disposeOnClose
1807 * true if the trace should be disposed when the table is
1808 * disposed
1809 */
1810 public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
1811 if ((fTrace != null) && fDisposeOnClose) {
1812 fTrace.dispose();
1813 }
1814 fTrace = trace;
1815 fPackDone = false;
1816 fSelectedRank = 0;
1817 fDisposeOnClose = disposeOnClose;
1818
1819 // Perform the updates on the UI thread
1820 fTable.getDisplay().syncExec(new Runnable() {
1821 @Override
1822 public void run() {
1823 fTable.removeAll();
1824 fCache.setTrace(fTrace); // Clear the cache
1825 if (fTrace != null) {
1826 if (!fTable.isDisposed() && (fTrace != null)) {
1827 if (fTable.getData(Key.FILTER_OBJ) == null) {
1828 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1829 } else {
1830 stopFilterThread();
1831 fFilterMatchCount = 0;
1832 fFilterCheckCount = 0;
1833 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1834 startFilterThread();
1835 }
1836 }
db4721fb 1837 }
ea279a69 1838 fRawViewer.setTrace(fTrace);
db4721fb
PT
1839 }
1840 });
1841 }
1842
1843 // ------------------------------------------------------------------------
1844 // Event cache
1845 // ------------------------------------------------------------------------
1846
1847 /**
1848 * Notify that the event cache has been updated
1849 *
1850 * @param completed
1851 * Also notify if the populating of the cache is complete, or
1852 * not.
1853 */
1854 public void cacheUpdated(final boolean completed) {
1855 synchronized (fCacheUpdateSyncObj) {
1856 if (fCacheUpdateBusy) {
1857 fCacheUpdatePending = true;
1858 fCacheUpdateCompleted = completed;
1859 return;
1860 }
1861 fCacheUpdateBusy = true;
1862 }
1863 // Event cache is now updated. Perform update on the UI thread
1864 if (!fTable.isDisposed()) {
1865 fTable.getDisplay().asyncExec(new Runnable() {
1866 @Override
1867 public void run() {
1868 if (!fTable.isDisposed()) {
1869 fTable.refresh();
1870 packColumns();
1871 }
1872 if (completed) {
1873 populateCompleted();
1874 }
1875 synchronized (fCacheUpdateSyncObj) {
1876 fCacheUpdateBusy = false;
1877 if (fCacheUpdatePending) {
1878 fCacheUpdatePending = false;
1879 cacheUpdated(fCacheUpdateCompleted);
1880 }
1881 }
1882 }
1883 });
1884 }
1885 }
1886
a0a88f65
AM
1887 /**
1888 * Callback for when populating the table is complete.
1889 */
db4721fb
PT
1890 protected void populateCompleted() {
1891 // Nothing by default;
1892 }
1893
93bfd50a
PT
1894 // ------------------------------------------------------------------------
1895 // ISelectionProvider
1896 // ------------------------------------------------------------------------
1897
1898 /* (non-Javadoc)
1899 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1900 */
1901 /**
1902 * @since 2.0
1903 */
1904 @Override
1905 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1906 selectionChangedListeners.add(listener);
1907 }
1908
1909 /* (non-Javadoc)
1910 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
1911 */
1912 /**
1913 * @since 2.0
1914 */
1915 @Override
1916 public ISelection getSelection() {
1917 if (fTable == null || fTable.isDisposed()) {
1918 return StructuredSelection.EMPTY;
1919 }
1920 List<Object> list = new ArrayList<Object>(fTable.getSelection().length);
1921 for (TableItem item : fTable.getSelection()) {
1922 if (item.getData() != null) {
1923 list.add(item.getData());
1924 }
1925 }
1926 return new StructuredSelection(list);
1927 }
1928
1929 /* (non-Javadoc)
1930 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1931 */
1932 /**
1933 * @since 2.0
1934 */
1935 @Override
1936 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
1937 selectionChangedListeners.remove(listener);
1938 }
1939
1940 /* (non-Javadoc)
1941 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
1942 */
1943 /**
1944 * @since 2.0
1945 */
1946 @Override
1947 public void setSelection(ISelection selection) {
1948 // not implemented
1949 }
1950
1951 /**
1952 * Notifies any selection changed listeners that the viewer's selection has changed.
1953 * Only listeners registered at the time this method is called are notified.
1954 *
1955 * @param event a selection changed event
1956 *
1957 * @see ISelectionChangedListener#selectionChanged
1958 * @since 2.0
1959 */
1960 protected void fireSelectionChanged(final SelectionChangedEvent event) {
1961 Object[] listeners = selectionChangedListeners.getListeners();
1962 for (int i = 0; i < listeners.length; ++i) {
1963 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
1964 SafeRunnable.run(new SafeRunnable() {
faa38350 1965 @Override
93bfd50a
PT
1966 public void run() {
1967 l.selectionChanged(event);
1968 }
1969 });
1970 }
1971 }
1972
db4721fb
PT
1973 // ------------------------------------------------------------------------
1974 // Bookmark handling
1975 // ------------------------------------------------------------------------
1976
1977 /**
1978 * Add a bookmark to this event table.
1979 *
1980 * @param bookmarksFile
1981 * The file to use for the bookmarks
1982 */
1983 public void addBookmark(final IFile bookmarksFile) {
1984 fBookmarksFile = bookmarksFile;
1985 final TableItem[] selection = fTable.getSelection();
1986 if (selection.length > 0) {
1987 final TableItem tableItem = selection[0];
1988 if (tableItem.getData(Key.RANK) != null) {
1989 final StringBuffer defaultMessage = new StringBuffer();
1990 for (int i = 0; i < fTable.getColumns().length; i++) {
60fb38b8 1991 if (i > 0) {
db4721fb
PT
1992 defaultMessage.append(", "); //$NON-NLS-1$
1993 }
1994 defaultMessage.append(tableItem.getText(i));
1995 }
3be2946f
PT
1996 final InputDialog dialog = new MultiLineInputDialog(
1997 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1998 Messages.TmfEventsTable_AddBookmarkDialogTitle,
1999 Messages.TmfEventsTable_AddBookmarkDialogMessage,
2000 defaultMessage.toString());
db4721fb
PT
2001 if (dialog.open() == Window.OK) {
2002 final String message = dialog.getValue();
2003 try {
2004 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
2005 if (bookmark.exists()) {
2006 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
2007 final long rank = (Long) tableItem.getData(Key.RANK);
2008 final int location = (int) rank;
2009 bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
2010 fBookmarksMap.put(rank, bookmark.getId());
2011 fTable.refresh();
2012 }
2013 } catch (final CoreException e) {
2014 displayException(e);
2015 }
2016 }
2017 }
2018 }
2019
2020 }
2021
2022 /**
2023 * Remove a bookmark from this event table.
2024 *
2025 * @param bookmark
2026 * The bookmark to remove
2027 */
2028 public void removeBookmark(final IMarker bookmark) {
2029 for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
2030 if (entry.getValue().equals(bookmark.getId())) {
2031 fBookmarksMap.remove(entry.getKey());
2032 fTable.refresh();
2033 return;
2034 }
2035 }
2036 }
2037
2038 private void toggleBookmark(final long rank) {
2039 if (fBookmarksFile == null) {
2040 return;
2041 }
2042 if (fBookmarksMap.containsKey(rank)) {
2043 final Long markerId = fBookmarksMap.remove(rank);
2044 fTable.refresh();
2045 try {
2046 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
2047 if (bookmark != null) {
2048 bookmark.delete();
2049 }
2050 } catch (final CoreException e) {
2051 displayException(e);
2052 }
2053 } else {
2054 addBookmark(fBookmarksFile);
2055 }
2056 }
2057
2058 /**
2059 * Refresh the bookmarks assigned to this trace, from the contents of a
2060 * bookmark file.
2061 *
2062 * @param bookmarksFile
2063 * The bookmark file to use
2064 */
2065 public void refreshBookmarks(final IFile bookmarksFile) {
2066 fBookmarksFile = bookmarksFile;
2067 if (bookmarksFile == null) {
2068 fBookmarksMap.clear();
2069 fTable.refresh();
2070 return;
2071 }
2072 try {
2073 fBookmarksMap.clear();
2074 for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
2075 final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
2076 if (location != -1) {
2077 final long rank = location;
2078 fBookmarksMap.put(rank, bookmark.getId());
2079 }
2080 }
2081 fTable.refresh();
2082 } catch (final CoreException e) {
2083 displayException(e);
2084 }
2085 }
2086
2087 @Override
2088 public void gotoMarker(final IMarker marker) {
2089 final int rank = marker.getAttribute(IMarker.LOCATION, -1);
2090 if (rank != -1) {
2091 int index = rank;
2092 if (fTable.getData(Key.FILTER_OBJ) != null) {
2093 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
2094 } else if (rank >= fTable.getItemCount()) {
2095 fPendingGotoRank = rank;
2096 }
a56ec2b8 2097 fSelectedRank = rank;
db4721fb
PT
2098 fTable.setSelection(index + 1); // +1 for header row
2099 }
2100 }
2101
2102 // ------------------------------------------------------------------------
2103 // Listeners
2104 // ------------------------------------------------------------------------
2105
2106 /*
2107 * (non-Javadoc)
2108 *
2109 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
2110 */
2111 @Override
2112 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
2113 fTable.refresh();
2114 }
2115
db4721fb
PT
2116 // ------------------------------------------------------------------------
2117 // Signal handlers
2118 // ------------------------------------------------------------------------
2119
db4721fb
PT
2120 /**
2121 * Handler for the trace updated signal
2122 *
2123 * @param signal
2124 * The incoming signal
2125 */
2126 @TmfSignalHandler
2127 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
2128 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
2129 return;
2130 }
2131 // Perform the refresh on the UI thread
2132 Display.getDefault().asyncExec(new Runnable() {
2133 @Override
2134 public void run() {
2135 if (!fTable.isDisposed() && (fTrace != null)) {
2136 if (fTable.getData(Key.FILTER_OBJ) == null) {
2137 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
2138 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
2139 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
2140 fPendingGotoRank = -1;
2141 }
2142 } else {
2143 startFilterThread();
2144 }
2145 }
2146 if (!fRawViewer.isDisposed() && (fTrace != null)) {
2147 fRawViewer.refreshEventCount();
2148 }
2149 }
2150 });
2151 }
2152
2153 /**
2154 * Handler for the time synch signal.
2155 *
2156 * @param signal
2157 * The incoming signal
2158 */
2159 @TmfSignalHandler
2160 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
2161 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
2162
2163 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
2164 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
2165 // the method fTrace.getRank() from interfering and delaying ongoing requests.
2166 final TmfDataRequest subRequest = new TmfDataRequest(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
2167
2168 TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
2169
2170 @Override
2171 public void handleData(final ITmfEvent event) {
2172 super.handleData(event);
2173 }
2174
2175 @Override
5419a136 2176 public void handleCompleted() {
db4721fb
PT
2177 super.handleCompleted();
2178 if (fTrace == null) {
2179 return;
2180 }
2181
2182 // Verify if the event is within the trace range and adjust if necessary
2183 ITmfTimestamp timestamp = ts;
2184 if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
2185 timestamp = fTrace.getStartTime();
2186 }
2187 if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
2188 timestamp = fTrace.getEndTime();
2189 }
2190
2191 // Get the rank of the selected event in the table
2192 final ITmfContext context = fTrace.seekEvent(timestamp);
2193 final long rank = context.getRank();
4c9f2944 2194 context.dispose();
db4721fb
PT
2195 fSelectedRank = rank;
2196
2197 fTable.getDisplay().asyncExec(new Runnable() {
2198 @Override
2199 public void run() {
2200 // Return if table is disposed
2201 if (fTable.isDisposed()) {
2202 return;
2203 }
2204
2205 int index = (int) rank;
2206 if (fTable.isDisposed()) {
2207 return;
2208 }
60fb38b8 2209 if (fTable.getData(Key.FILTER_OBJ) != null) {
db4721fb
PT
2210 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
2211 }
2212 fTable.setSelection(index + 1); // +1 for header row
2213 fRawViewer.selectAndReveal(rank);
2214 }
2215 });
2216 }
2217 };
2218
2219 ((ITmfDataProvider) fTrace).sendRequest(subRequest);
2220 }
2221 }
2222
2223 // ------------------------------------------------------------------------
2224 // Error handling
2225 // ------------------------------------------------------------------------
2226
2227 /**
2228 * Display an exception in a message box
2229 *
2230 * @param e the exception
2231 */
2232 private static void displayException(final Exception e) {
2233 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
60fb38b8 2234 mb.setText(e.getClass().getSimpleName());
db4721fb
PT
2235 mb.setMessage(e.getMessage());
2236 mb.open();
2237 }
2238
f8177ba2
FC
2239 /**
2240 * @since 2.0
2241 */
2242 public void refresh() {
2243 fCache.clear();
2244 fTable.refresh();
2245 fTable.redraw();
2246 }
db4721fb 2247}
This page took 0.126486 seconds and 5 git commands to generate.