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