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