tmf: Simple warning fixes in tmf.ui and tests
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
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
16 package org.eclipse.linuxtools.tmf.ui.viewers.events;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.regex.Pattern;
25 import java.util.regex.PatternSyntaxException;
26
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IMarker;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IProgressMonitor;
32 import org.eclipse.core.runtime.IStatus;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.core.runtime.jobs.Job;
35 import org.eclipse.jface.action.Action;
36 import org.eclipse.jface.action.IAction;
37 import org.eclipse.jface.action.IMenuListener;
38 import org.eclipse.jface.action.IMenuManager;
39 import org.eclipse.jface.action.MenuManager;
40 import org.eclipse.jface.action.Separator;
41 import org.eclipse.jface.dialogs.InputDialog;
42 import org.eclipse.jface.dialogs.MessageDialog;
43 import org.eclipse.jface.resource.FontDescriptor;
44 import org.eclipse.jface.resource.JFaceResources;
45 import org.eclipse.jface.resource.LocalResourceManager;
46 import org.eclipse.jface.window.Window;
47 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
48 import org.eclipse.linuxtools.internal.tmf.ui.Messages;
49 import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;
50 import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
51 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
52 import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
53 import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
54 import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
55 import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;
56 import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
57 import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;
58 import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;
59 import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;
60 import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;
61 import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;
62 import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
63 import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentUpdatedSignal;
64 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
65 import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
66 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;
67 import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
68 import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;
69 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
70 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
71 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
72 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
73 import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
74 import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
75 import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
76 import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;
77 import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
78 import org.eclipse.swt.SWT;
79 import org.eclipse.swt.custom.SashForm;
80 import org.eclipse.swt.custom.TableEditor;
81 import org.eclipse.swt.events.FocusAdapter;
82 import org.eclipse.swt.events.FocusEvent;
83 import org.eclipse.swt.events.KeyAdapter;
84 import org.eclipse.swt.events.KeyEvent;
85 import org.eclipse.swt.events.MouseAdapter;
86 import org.eclipse.swt.events.MouseEvent;
87 import org.eclipse.swt.events.SelectionAdapter;
88 import org.eclipse.swt.events.SelectionEvent;
89 import org.eclipse.swt.graphics.Color;
90 import org.eclipse.swt.graphics.Font;
91 import org.eclipse.swt.graphics.Image;
92 import org.eclipse.swt.graphics.Point;
93 import org.eclipse.swt.graphics.Rectangle;
94 import org.eclipse.swt.layout.FillLayout;
95 import org.eclipse.swt.layout.GridData;
96 import org.eclipse.swt.layout.GridLayout;
97 import org.eclipse.swt.widgets.Composite;
98 import org.eclipse.swt.widgets.Display;
99 import org.eclipse.swt.widgets.Event;
100 import org.eclipse.swt.widgets.Label;
101 import org.eclipse.swt.widgets.Listener;
102 import org.eclipse.swt.widgets.Menu;
103 import org.eclipse.swt.widgets.MessageBox;
104 import org.eclipse.swt.widgets.Shell;
105 import org.eclipse.swt.widgets.TableColumn;
106 import org.eclipse.swt.widgets.TableItem;
107 import org.eclipse.swt.widgets.Text;
108 import org.eclipse.ui.PlatformUI;
109 import org.eclipse.ui.ide.IGotoMarker;
110 import 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 */
121 public 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
1287 public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,
1288 final long currentRank, final int direction) {
1289 super(Messages.TmfEventsTable_SearchingJobName);
1290 this.searchFilter = searchFilter;
1291 this.eventFilter = eventFilter;
1292 this.startIndex = startIndex;
1293 this.rank = currentRank;
1294 this.direction = direction;
1295 }
1296
1297 @SuppressWarnings("unchecked")
1298 @Override
1299 protected IStatus run(final IProgressMonitor monitor) {
1300 if (fTrace == null) {
1301 return Status.OK_STATUS;
1302 }
1303 final Display display = Display.getDefault();
1304 if (startIndex < 0) {
1305 rank = (int) fTrace.getNbEvents() - 1;
1306 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
1307 rank = 0;
1308 } else {
1309 int idx = startIndex;
1310 while (foundRank == -1) {
1311 final CachedEvent event = fCache.peekEvent(idx);
1312 if (event == null) {
1313 break;
1314 }
1315 rank = event.rank;
1316 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
1317 foundRank = event.rank;
1318 break;
1319 }
1320 if (direction == Direction.FORWARD) {
1321 idx++;
1322 } else {
1323 idx--;
1324 }
1325 }
1326 if (foundRank == -1) {
1327 if (direction == Direction.FORWARD) {
1328 rank++;
1329 if (rank > (fTrace.getNbEvents() - 1)) {
1330 rank = 0;
1331 }
1332 } else {
1333 rank--;
1334 if (rank < 0) {
1335 rank = (int) fTrace.getNbEvents() - 1;
1336 }
1337 }
1338 }
1339 }
1340 final int startRank = (int) rank;
1341 boolean wrapped = false;
1342 while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
1343 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1344 if (direction == Direction.BACKWARD) {
1345 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1346 }
1347 request = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1348 long currentRank = rank;
1349
1350 @Override
1351 public void handleData(final ITmfEvent event) {
1352 super.handleData(event);
1353 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
1354 foundRank = currentRank;
1355 if (direction == Direction.FORWARD) {
1356 done();
1357 return;
1358 }
1359 }
1360 currentRank++;
1361 }
1362 };
1363 ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(request);
1364 try {
1365 request.waitForCompletion();
1366 if (request.isCancelled()) {
1367 return Status.OK_STATUS;
1368 }
1369 } catch (final InterruptedException e) {
1370 synchronized (fSearchSyncObj) {
1371 fSearchThread = null;
1372 }
1373 return Status.OK_STATUS;
1374 }
1375 if (foundRank == -1) {
1376 if (direction == Direction.FORWARD) {
1377 if (rank == 0) {
1378 synchronized (fSearchSyncObj) {
1379 fSearchThread = null;
1380 }
1381 return Status.OK_STATUS;
1382 }
1383 nbRequested = (int) rank;
1384 rank = 0;
1385 wrapped = true;
1386 } else {
1387 rank--;
1388 if (rank < 0) {
1389 rank = (int) fTrace.getNbEvents() - 1;
1390 wrapped = true;
1391 }
1392 if ((rank <= startRank) && wrapped) {
1393 synchronized (fSearchSyncObj) {
1394 fSearchThread = null;
1395 }
1396 return Status.OK_STATUS;
1397 }
1398 }
1399 }
1400 }
1401 int index = (int) foundRank;
1402 if (eventFilter != null) {
1403 index = fCache.getFilteredEventIndex(foundRank);
1404 }
1405 final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1406
1407 display.asyncExec(new Runnable() {
1408 @Override
1409 public void run() {
1410 if (monitor.isCanceled()) {
1411 return;
1412 }
1413 if (fTable.isDisposed()) {
1414 return;
1415 }
1416 fTable.setSelection(selection);
1417 fSelectedRank = foundRank;
1418 synchronized (fSearchSyncObj) {
1419 fSearchThread = null;
1420 }
1421 }
1422 });
1423 return Status.OK_STATUS;
1424 }
1425
1426 @Override
1427 protected void canceling() {
1428 request.cancel();
1429 synchronized (fSearchSyncObj) {
1430 fSearchThread = null;
1431 }
1432 }
1433 }
1434
1435 protected void createResources() {
1436 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
1437 .getForeground().getRGB()));
1438 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1439 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1440 }
1441
1442 protected void packColumns() {
1443 if (fPackDone) {
1444 return;
1445 }
1446 for (final TableColumn column : fTable.getColumns()) {
1447 final int headerWidth = column.getWidth();
1448 column.pack();
1449 if (column.getWidth() < headerWidth) {
1450 column.setWidth(headerWidth);
1451 }
1452 }
1453 fPackDone = true;
1454 }
1455
1456 /**
1457 * @param event
1458 * @return
1459 *
1460 * FIXME: Add support for column selection
1461 */
1462 protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
1463 ITmfEventField[] fields = new TmfEventField[0];
1464 if (event != null) {
1465 final String timestamp = event.getTimestamp().toString();
1466 final String source = event.getSource();
1467 final String type = event.getType().getName();
1468 final String reference = event.getReference();
1469 final ITmfEventField content = event.getContent();
1470 final String value = (content.getValue() != null) ? content.getValue().toString() : content.toString();
1471 fields = new TmfEventField[] {
1472 new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),
1473 new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),
1474 new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),
1475 new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),
1476 new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, value)
1477 };
1478 }
1479 return fields;
1480 }
1481
1482 /**
1483 * Notify this table that is got the UI focus.
1484 */
1485 public void setFocus() {
1486 fTable.setFocus();
1487 }
1488
1489 /**
1490 * Assign a new trace to this event table.
1491 *
1492 * @param trace
1493 * The trace to assign to this event table
1494 * @param disposeOnClose
1495 * true if the trace should be disposed when the table is
1496 * disposed
1497 */
1498 public void setTrace(final ITmfTrace<?> trace, final boolean disposeOnClose) {
1499 if ((fTrace != null) && fDisposeOnClose) {
1500 fTrace.dispose();
1501 }
1502 fTrace = trace;
1503 fPackDone = false;
1504 fSelectedRank = 0;
1505 fDisposeOnClose = disposeOnClose;
1506
1507 // Perform the updates on the UI thread
1508 fTable.getDisplay().syncExec(new Runnable() {
1509 @Override
1510 public void run() {
1511 fTable.removeAll();
1512 fCache.setTrace(fTrace); // Clear the cache
1513 if (fTrace != null) {
1514 if (!fTable.isDisposed() && (fTrace != null)) {
1515 if (fTable.getData(Key.FILTER_OBJ) == null) {
1516 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1517 } else {
1518 stopFilterThread();
1519 fFilterMatchCount = 0;
1520 fFilterCheckCount = 0;
1521 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1522 startFilterThread();
1523 }
1524 }
1525 fRawViewer.setTrace(fTrace);
1526 }
1527 }
1528 });
1529 }
1530
1531 // ------------------------------------------------------------------------
1532 // Event cache
1533 // ------------------------------------------------------------------------
1534
1535 /**
1536 * Notify that the event cache has been updated
1537 *
1538 * @param completed
1539 * Also notify if the populating of the cache is complete, or
1540 * not.
1541 */
1542 public void cacheUpdated(final boolean completed) {
1543 synchronized (fCacheUpdateSyncObj) {
1544 if (fCacheUpdateBusy) {
1545 fCacheUpdatePending = true;
1546 fCacheUpdateCompleted = completed;
1547 return;
1548 }
1549 fCacheUpdateBusy = true;
1550 }
1551 // Event cache is now updated. Perform update on the UI thread
1552 if (!fTable.isDisposed()) {
1553 fTable.getDisplay().asyncExec(new Runnable() {
1554 @Override
1555 public void run() {
1556 if (!fTable.isDisposed()) {
1557 fTable.refresh();
1558 packColumns();
1559 }
1560 if (completed) {
1561 populateCompleted();
1562 }
1563 synchronized (fCacheUpdateSyncObj) {
1564 fCacheUpdateBusy = false;
1565 if (fCacheUpdatePending) {
1566 fCacheUpdatePending = false;
1567 cacheUpdated(fCacheUpdateCompleted);
1568 }
1569 }
1570 }
1571 });
1572 }
1573 }
1574
1575 protected void populateCompleted() {
1576 // Nothing by default;
1577 }
1578
1579 // ------------------------------------------------------------------------
1580 // Bookmark handling
1581 // ------------------------------------------------------------------------
1582
1583 /**
1584 * Add a bookmark to this event table.
1585 *
1586 * @param bookmarksFile
1587 * The file to use for the bookmarks
1588 */
1589 public void addBookmark(final IFile bookmarksFile) {
1590 fBookmarksFile = bookmarksFile;
1591 final TableItem[] selection = fTable.getSelection();
1592 if (selection.length > 0) {
1593 final TableItem tableItem = selection[0];
1594 if (tableItem.getData(Key.RANK) != null) {
1595 final StringBuffer defaultMessage = new StringBuffer();
1596 for (int i = 0; i < fTable.getColumns().length; i++) {
1597 if (i > 0)
1598 {
1599 defaultMessage.append(", "); //$NON-NLS-1$
1600 }
1601 defaultMessage.append(tableItem.getText(i));
1602 }
1603 final InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1604 Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText,
1605 defaultMessage.toString(), null);
1606 if (dialog.open() == Window.OK) {
1607 final String message = dialog.getValue();
1608 try {
1609 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
1610 if (bookmark.exists()) {
1611 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
1612 final long rank = (Long) tableItem.getData(Key.RANK);
1613 final int location = (int) rank;
1614 bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
1615 fBookmarksMap.put(rank, bookmark.getId());
1616 fTable.refresh();
1617 }
1618 } catch (final CoreException e) {
1619 displayException(e);
1620 }
1621 }
1622 }
1623 }
1624
1625 }
1626
1627 /**
1628 * Remove a bookmark from this event table.
1629 *
1630 * @param bookmark
1631 * The bookmark to remove
1632 */
1633 public void removeBookmark(final IMarker bookmark) {
1634 for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
1635 if (entry.getValue().equals(bookmark.getId())) {
1636 fBookmarksMap.remove(entry.getKey());
1637 fTable.refresh();
1638 return;
1639 }
1640 }
1641 }
1642
1643 private void toggleBookmark(final long rank) {
1644 if (fBookmarksFile == null) {
1645 return;
1646 }
1647 if (fBookmarksMap.containsKey(rank)) {
1648 final Long markerId = fBookmarksMap.remove(rank);
1649 fTable.refresh();
1650 try {
1651 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
1652 if (bookmark != null) {
1653 bookmark.delete();
1654 }
1655 } catch (final CoreException e) {
1656 displayException(e);
1657 }
1658 } else {
1659 addBookmark(fBookmarksFile);
1660 }
1661 }
1662
1663 /**
1664 * Refresh the bookmarks assigned to this trace, from the contents of a
1665 * bookmark file.
1666 *
1667 * @param bookmarksFile
1668 * The bookmark file to use
1669 */
1670 public void refreshBookmarks(final IFile bookmarksFile) {
1671 fBookmarksFile = bookmarksFile;
1672 if (bookmarksFile == null) {
1673 fBookmarksMap.clear();
1674 fTable.refresh();
1675 return;
1676 }
1677 try {
1678 fBookmarksMap.clear();
1679 for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
1680 final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
1681 if (location != -1) {
1682 final long rank = location;
1683 fBookmarksMap.put(rank, bookmark.getId());
1684 }
1685 }
1686 fTable.refresh();
1687 } catch (final CoreException e) {
1688 displayException(e);
1689 }
1690 }
1691
1692 @Override
1693 public void gotoMarker(final IMarker marker) {
1694 final int rank = marker.getAttribute(IMarker.LOCATION, -1);
1695 if (rank != -1) {
1696 int index = rank;
1697 if (fTable.getData(Key.FILTER_OBJ) != null) {
1698 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1699 } else if (rank >= fTable.getItemCount()) {
1700 fPendingGotoRank = rank;
1701 }
1702 fTable.setSelection(index + 1); // +1 for header row
1703 }
1704 }
1705
1706 // ------------------------------------------------------------------------
1707 // Listeners
1708 // ------------------------------------------------------------------------
1709
1710 /*
1711 * (non-Javadoc)
1712 *
1713 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
1714 */
1715 @Override
1716 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
1717 fTable.refresh();
1718 }
1719
1720 @Override
1721 public void addEventsFilterListener(final ITmfEventsFilterListener listener) {
1722 if (!fEventsFilterListeners.contains(listener)) {
1723 fEventsFilterListeners.add(listener);
1724 }
1725 }
1726
1727 @Override
1728 public void removeEventsFilterListener(final ITmfEventsFilterListener listener) {
1729 fEventsFilterListeners.remove(listener);
1730 }
1731
1732 // ------------------------------------------------------------------------
1733 // Signal handlers
1734 // ------------------------------------------------------------------------
1735
1736 /**
1737 * Handler for the experiment updated signal.
1738 *
1739 * @param signal
1740 * The incoming signal
1741 */
1742 @TmfSignalHandler
1743 public void experimentUpdated(final TmfExperimentUpdatedSignal signal) {
1744 if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) {
1745 return;
1746 }
1747 // Perform the refresh on the UI thread
1748 Display.getDefault().asyncExec(new Runnable() {
1749 @Override
1750 public void run() {
1751 if (!fTable.isDisposed() && (fTrace != null)) {
1752 if (fTable.getData(Key.FILTER_OBJ) == null) {
1753 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1754 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
1755 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1756 fPendingGotoRank = -1;
1757 }
1758 } else {
1759 startFilterThread();
1760 }
1761 }
1762 if (!fRawViewer.isDisposed() && (fTrace != null)) {
1763 fRawViewer.refreshEventCount();
1764 }
1765 }
1766 });
1767 }
1768
1769 /**
1770 * Handler for the trace updated signal
1771 *
1772 * @param signal
1773 * The incoming signal
1774 */
1775 @TmfSignalHandler
1776 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
1777 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
1778 return;
1779 }
1780 // Perform the refresh on the UI thread
1781 Display.getDefault().asyncExec(new Runnable() {
1782 @Override
1783 public void run() {
1784 if (!fTable.isDisposed() && (fTrace != null)) {
1785 if (fTable.getData(Key.FILTER_OBJ) == null) {
1786 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1787 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
1788 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1789 fPendingGotoRank = -1;
1790 }
1791 } else {
1792 startFilterThread();
1793 }
1794 }
1795 if (!fRawViewer.isDisposed() && (fTrace != null)) {
1796 fRawViewer.refreshEventCount();
1797 }
1798 }
1799 });
1800 }
1801
1802 /**
1803 * Handler for the time synch signal.
1804 *
1805 * @param signal
1806 * The incoming signal
1807 */
1808 @SuppressWarnings("unchecked")
1809 @TmfSignalHandler
1810 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
1811 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
1812
1813 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
1814 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
1815 // the method fTrace.getRank() from interfering and delaying ongoing requests.
1816 final TmfDataRequest<ITmfEvent> subRequest = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
1817
1818 TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
1819
1820 @Override
1821 public void handleData(final ITmfEvent event) {
1822 super.handleData(event);
1823 }
1824
1825 @Override
1826 public void handleCompleted() {
1827 super.handleCompleted();
1828 if (fTrace == null) {
1829 return;
1830 }
1831
1832 // Verify if the event is within the trace range and adjust if necessary
1833 ITmfTimestamp timestamp = ts;
1834 if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
1835 timestamp = fTrace.getStartTime();
1836 }
1837 if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
1838 timestamp = fTrace.getEndTime();
1839 }
1840
1841 // Get the rank of the selected event in the table
1842 final ITmfContext context = fTrace.seekEvent(timestamp);
1843 final long rank = context.getRank();
1844 fSelectedRank = rank;
1845
1846 fTable.getDisplay().asyncExec(new Runnable() {
1847 @Override
1848 public void run() {
1849 // Return if table is disposed
1850 if (fTable.isDisposed()) {
1851 return;
1852 }
1853
1854 int index = (int) rank;
1855 if (fTable.isDisposed()) {
1856 return;
1857 }
1858 if (fTable.getData(Key.FILTER_OBJ) != null)
1859 {
1860 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1861 }
1862 fTable.setSelection(index + 1); // +1 for header row
1863 fRawViewer.selectAndReveal(rank);
1864 }
1865 });
1866 }
1867 };
1868
1869 ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(subRequest);
1870 }
1871 }
1872
1873 // ------------------------------------------------------------------------
1874 // Error handling
1875 // ------------------------------------------------------------------------
1876
1877 /**
1878 * Display an exception in a message box
1879 *
1880 * @param e the exception
1881 */
1882 private static void displayException(final Exception e) {
1883 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
1884 mb.setText(e.getClass().getName());
1885 mb.setMessage(e.getMessage());
1886 mb.open();
1887 }
1888
1889 }
This page took 0.107269 seconds and 6 git commands to generate.