tmf: Update Javadoc throughout tmf.ui
[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.Dialog;
42 import org.eclipse.jface.dialogs.InputDialog;
43 import org.eclipse.jface.dialogs.MessageDialog;
44 import org.eclipse.jface.resource.FontDescriptor;
45 import org.eclipse.jface.resource.JFaceResources;
46 import org.eclipse.jface.resource.LocalResourceManager;
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 protected void applyFilter(ITmfFilter filter) {
1076 stopFilterThread();
1077 stopSearchThread();
1078 fFilterMatchCount = 0;
1079 fFilterCheckCount = 0;
1080 fCache.applyFilter(filter);
1081 fTable.clearAll();
1082 fTable.setData(Key.FILTER_OBJ, filter);
1083 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1084 startFilterThread();
1085 fireFilterApplied(filter);
1086 }
1087
1088 protected void clearFilters() {
1089 if (fTable.getData(Key.FILTER_OBJ) == null) {
1090 return;
1091 }
1092 stopFilterThread();
1093 stopSearchThread();
1094 fCache.clearFilter();
1095 fTable.clearAll();
1096 for (final TableColumn column : fTable.getColumns()) {
1097 column.setData(Key.FILTER_OBJ, null);
1098 column.setData(Key.FILTER_TXT, null);
1099 }
1100 fTable.setData(Key.FILTER_OBJ, null);
1101 if (fTrace != null) {
1102 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1103 } else {
1104 fTable.setItemCount(1); // +1 for header row
1105 }
1106 fFilterMatchCount = 0;
1107 fFilterCheckCount = 0;
1108 if (fSelectedRank >= 0) {
1109 fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
1110 } else {
1111 fTable.setSelection(0);
1112 }
1113 fireFilterApplied(null);
1114 }
1115
1116 protected class FilterThread extends Thread {
1117 private final ITmfFilterTreeNode filter;
1118 private TmfDataRequest<ITmfEvent> request;
1119 private boolean refreshBusy = false;
1120 private boolean refreshPending = false;
1121 private final Object syncObj = new Object();
1122
1123 public FilterThread(final ITmfFilterTreeNode filter) {
1124 super("Filter Thread"); //$NON-NLS-1$
1125 this.filter = filter;
1126 }
1127
1128 @SuppressWarnings("unchecked")
1129 @Override
1130 public void run() {
1131 if (fTrace == null) {
1132 return;
1133 }
1134 final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
1135 if (nbRequested <= 0) {
1136 return;
1137 }
1138 request = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, (int) fFilterCheckCount,
1139 nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1140 @Override
1141 public void handleData(final ITmfEvent event) {
1142 super.handleData(event);
1143 if (request.isCancelled()) {
1144 return;
1145 }
1146 if (filter.matches(event)) {
1147 final long rank = fFilterCheckCount;
1148 final int index = (int) fFilterMatchCount;
1149 fFilterMatchCount++;
1150 fCache.storeEvent(event.clone(), rank, index);
1151 refreshTable();
1152 } else if ((fFilterCheckCount % 100) == 0) {
1153 refreshTable();
1154 }
1155 fFilterCheckCount++;
1156 }
1157 };
1158 ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(request);
1159 try {
1160 request.waitForCompletion();
1161 } catch (final InterruptedException e) {
1162 }
1163 refreshTable();
1164 synchronized (fFilterSyncObj) {
1165 fFilterThread = null;
1166 if (fFilterThreadResume) {
1167 fFilterThreadResume = false;
1168 fFilterThread = new FilterThread(filter);
1169 fFilterThread.start();
1170 }
1171 }
1172 }
1173
1174 public void refreshTable() {
1175 synchronized (syncObj) {
1176 if (refreshBusy) {
1177 refreshPending = true;
1178 return;
1179 }
1180 refreshBusy = true;
1181 }
1182 Display.getDefault().asyncExec(new Runnable() {
1183 @Override
1184 public void run() {
1185 if (request.isCancelled()) {
1186 return;
1187 }
1188 if (fTable.isDisposed()) {
1189 return;
1190 }
1191 fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
1192 fTable.refresh();
1193 synchronized (syncObj) {
1194 refreshBusy = false;
1195 if (refreshPending) {
1196 refreshPending = false;
1197 refreshTable();
1198 }
1199 }
1200 }
1201 });
1202 }
1203
1204 public void cancel() {
1205 if (request != null) {
1206 request.cancel();
1207 }
1208 }
1209 }
1210
1211 protected void searchNext() {
1212 synchronized (fSearchSyncObj) {
1213 if (fSearchThread != null) {
1214 return;
1215 }
1216 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1217 if (searchFilter == null) {
1218 return;
1219 }
1220 final int selectionIndex = fTable.getSelectionIndex();
1221 int startIndex;
1222 if (selectionIndex > 0) {
1223 startIndex = selectionIndex; // -1 for header row, +1 for next event
1224 } else {
1225 // header row is selected, start at top event
1226 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
1227 }
1228 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1229 if (eventFilter != null)
1230 {
1231 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
1232 }
1233 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
1234 fSearchThread.schedule();
1235 }
1236 }
1237
1238 protected void searchPrevious() {
1239 synchronized (fSearchSyncObj) {
1240 if (fSearchThread != null) {
1241 return;
1242 }
1243 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1244 if (searchFilter == null) {
1245 return;
1246 }
1247 final int selectionIndex = fTable.getSelectionIndex();
1248 int startIndex;
1249 if (selectionIndex > 0) {
1250 startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
1251 } else {
1252 // header row is selected, start at precedent of top event
1253 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
1254 }
1255 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1256 if (eventFilter != null)
1257 {
1258 startIndex = startIndex - 1; // -1 for top filter status row
1259 }
1260 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
1261 fSearchThread.schedule();
1262 }
1263 }
1264
1265 protected void stopSearchThread() {
1266 fPendingGotoRank = -1;
1267 synchronized (fSearchSyncObj) {
1268 if (fSearchThread != null) {
1269 fSearchThread.cancel();
1270 fSearchThread = null;
1271 }
1272 }
1273 }
1274
1275 protected class SearchThread extends Job {
1276 protected ITmfFilterTreeNode searchFilter;
1277 protected ITmfFilterTreeNode eventFilter;
1278 protected int startIndex;
1279 protected int direction;
1280 protected long rank;
1281 protected long foundRank = -1;
1282 protected TmfDataRequest<ITmfEvent> request;
1283
1284 public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,
1285 final long currentRank, final int direction) {
1286 super(Messages.TmfEventsTable_SearchingJobName);
1287 this.searchFilter = searchFilter;
1288 this.eventFilter = eventFilter;
1289 this.startIndex = startIndex;
1290 this.rank = currentRank;
1291 this.direction = direction;
1292 }
1293
1294 @SuppressWarnings("unchecked")
1295 @Override
1296 protected IStatus run(final IProgressMonitor monitor) {
1297 if (fTrace == null) {
1298 return Status.OK_STATUS;
1299 }
1300 final Display display = Display.getDefault();
1301 if (startIndex < 0) {
1302 rank = (int) fTrace.getNbEvents() - 1;
1303 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
1304 rank = 0;
1305 } else {
1306 int idx = startIndex;
1307 while (foundRank == -1) {
1308 final CachedEvent event = fCache.peekEvent(idx);
1309 if (event == null) {
1310 break;
1311 }
1312 rank = event.rank;
1313 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
1314 foundRank = event.rank;
1315 break;
1316 }
1317 if (direction == Direction.FORWARD) {
1318 idx++;
1319 } else {
1320 idx--;
1321 }
1322 }
1323 if (foundRank == -1) {
1324 if (direction == Direction.FORWARD) {
1325 rank++;
1326 if (rank > (fTrace.getNbEvents() - 1)) {
1327 rank = 0;
1328 }
1329 } else {
1330 rank--;
1331 if (rank < 0) {
1332 rank = (int) fTrace.getNbEvents() - 1;
1333 }
1334 }
1335 }
1336 }
1337 final int startRank = (int) rank;
1338 boolean wrapped = false;
1339 while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
1340 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1341 if (direction == Direction.BACKWARD) {
1342 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1343 }
1344 request = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1345 long currentRank = rank;
1346
1347 @Override
1348 public void handleData(final ITmfEvent event) {
1349 super.handleData(event);
1350 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
1351 foundRank = currentRank;
1352 if (direction == Direction.FORWARD) {
1353 done();
1354 return;
1355 }
1356 }
1357 currentRank++;
1358 }
1359 };
1360 ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(request);
1361 try {
1362 request.waitForCompletion();
1363 if (request.isCancelled()) {
1364 return Status.OK_STATUS;
1365 }
1366 } catch (final InterruptedException e) {
1367 synchronized (fSearchSyncObj) {
1368 fSearchThread = null;
1369 }
1370 return Status.OK_STATUS;
1371 }
1372 if (foundRank == -1) {
1373 if (direction == Direction.FORWARD) {
1374 if (rank == 0) {
1375 synchronized (fSearchSyncObj) {
1376 fSearchThread = null;
1377 }
1378 return Status.OK_STATUS;
1379 }
1380 nbRequested = (int) rank;
1381 rank = 0;
1382 wrapped = true;
1383 } else {
1384 rank--;
1385 if (rank < 0) {
1386 rank = (int) fTrace.getNbEvents() - 1;
1387 wrapped = true;
1388 }
1389 if ((rank <= startRank) && wrapped) {
1390 synchronized (fSearchSyncObj) {
1391 fSearchThread = null;
1392 }
1393 return Status.OK_STATUS;
1394 }
1395 }
1396 }
1397 }
1398 int index = (int) foundRank;
1399 if (eventFilter != null) {
1400 index = fCache.getFilteredEventIndex(foundRank);
1401 }
1402 final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1403
1404 display.asyncExec(new Runnable() {
1405 @Override
1406 public void run() {
1407 if (monitor.isCanceled()) {
1408 return;
1409 }
1410 if (fTable.isDisposed()) {
1411 return;
1412 }
1413 fTable.setSelection(selection);
1414 fSelectedRank = foundRank;
1415 synchronized (fSearchSyncObj) {
1416 fSearchThread = null;
1417 }
1418 }
1419 });
1420 return Status.OK_STATUS;
1421 }
1422
1423 @Override
1424 protected void canceling() {
1425 request.cancel();
1426 synchronized (fSearchSyncObj) {
1427 fSearchThread = null;
1428 }
1429 }
1430 }
1431
1432 protected void createResources() {
1433 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
1434 .getForeground().getRGB()));
1435 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1436 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1437 }
1438
1439 protected void packColumns() {
1440 if (fPackDone) {
1441 return;
1442 }
1443 for (final TableColumn column : fTable.getColumns()) {
1444 final int headerWidth = column.getWidth();
1445 column.pack();
1446 if (column.getWidth() < headerWidth) {
1447 column.setWidth(headerWidth);
1448 }
1449 }
1450 fPackDone = true;
1451 }
1452
1453 /**
1454 * @param event
1455 * @return
1456 *
1457 * FIXME: Add support for column selection
1458 */
1459 protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
1460 ITmfEventField[] fields = new TmfEventField[0];
1461 if (event != null) {
1462 final String timestamp = event.getTimestamp().toString();
1463 final String source = event.getSource();
1464 final String type = event.getType().getName();
1465 final String reference = event.getReference();
1466 final ITmfEventField content = event.getContent();
1467 final String value = (content.getValue() != null) ? content.getValue().toString() : content.toString();
1468 fields = new TmfEventField[] {
1469 new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),
1470 new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),
1471 new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),
1472 new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),
1473 new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, value)
1474 };
1475 }
1476 return fields;
1477 }
1478
1479 /**
1480 * Notify this table that is got the UI focus.
1481 */
1482 public void setFocus() {
1483 fTable.setFocus();
1484 }
1485
1486 /**
1487 * Assign a new trace to this event table.
1488 *
1489 * @param trace
1490 * The trace to assign to this event table
1491 * @param disposeOnClose
1492 * true if the trace should be disposed when the table is
1493 * disposed
1494 */
1495 public void setTrace(final ITmfTrace<?> trace, final boolean disposeOnClose) {
1496 if ((fTrace != null) && fDisposeOnClose) {
1497 fTrace.dispose();
1498 }
1499 fTrace = trace;
1500 fPackDone = false;
1501 fSelectedRank = 0;
1502 fDisposeOnClose = disposeOnClose;
1503
1504 // Perform the updates on the UI thread
1505 fTable.getDisplay().syncExec(new Runnable() {
1506 @Override
1507 public void run() {
1508 fTable.removeAll();
1509 fCache.setTrace(fTrace); // Clear the cache
1510 if (fTrace != null) {
1511 if (!fTable.isDisposed() && (fTrace != null)) {
1512 if (fTable.getData(Key.FILTER_OBJ) == null) {
1513 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1514 } else {
1515 stopFilterThread();
1516 fFilterMatchCount = 0;
1517 fFilterCheckCount = 0;
1518 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1519 startFilterThread();
1520 }
1521 }
1522 fRawViewer.setTrace(fTrace);
1523 }
1524 }
1525 });
1526 }
1527
1528 // ------------------------------------------------------------------------
1529 // Event cache
1530 // ------------------------------------------------------------------------
1531
1532 /**
1533 * Notify that the event cache has been updated
1534 *
1535 * @param completed
1536 * Also notify if the populating of the cache is complete, or
1537 * not.
1538 */
1539 public void cacheUpdated(final boolean completed) {
1540 synchronized (fCacheUpdateSyncObj) {
1541 if (fCacheUpdateBusy) {
1542 fCacheUpdatePending = true;
1543 fCacheUpdateCompleted = completed;
1544 return;
1545 }
1546 fCacheUpdateBusy = true;
1547 }
1548 // Event cache is now updated. Perform update on the UI thread
1549 if (!fTable.isDisposed()) {
1550 fTable.getDisplay().asyncExec(new Runnable() {
1551 @Override
1552 public void run() {
1553 if (!fTable.isDisposed()) {
1554 fTable.refresh();
1555 packColumns();
1556 }
1557 if (completed) {
1558 populateCompleted();
1559 }
1560 synchronized (fCacheUpdateSyncObj) {
1561 fCacheUpdateBusy = false;
1562 if (fCacheUpdatePending) {
1563 fCacheUpdatePending = false;
1564 cacheUpdated(fCacheUpdateCompleted);
1565 }
1566 }
1567 }
1568 });
1569 }
1570 }
1571
1572 protected void populateCompleted() {
1573 // Nothing by default;
1574 }
1575
1576 // ------------------------------------------------------------------------
1577 // Bookmark handling
1578 // ------------------------------------------------------------------------
1579
1580 /**
1581 * Add a bookmark to this event table.
1582 *
1583 * @param bookmarksFile
1584 * The file to use for the bookmarks
1585 */
1586 public void addBookmark(final IFile bookmarksFile) {
1587 fBookmarksFile = bookmarksFile;
1588 final TableItem[] selection = fTable.getSelection();
1589 if (selection.length > 0) {
1590 final TableItem tableItem = selection[0];
1591 if (tableItem.getData(Key.RANK) != null) {
1592 final StringBuffer defaultMessage = new StringBuffer();
1593 for (int i = 0; i < fTable.getColumns().length; i++) {
1594 if (i > 0)
1595 {
1596 defaultMessage.append(", "); //$NON-NLS-1$
1597 }
1598 defaultMessage.append(tableItem.getText(i));
1599 }
1600 final InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1601 Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText,
1602 defaultMessage.toString(), null);
1603 if (dialog.open() == Dialog.OK) {
1604 final String message = dialog.getValue();
1605 try {
1606 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
1607 if (bookmark.exists()) {
1608 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
1609 final long rank = (Long) tableItem.getData(Key.RANK);
1610 final int location = (int) rank;
1611 bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
1612 fBookmarksMap.put(rank, bookmark.getId());
1613 fTable.refresh();
1614 }
1615 } catch (final CoreException e) {
1616 displayException(e);
1617 }
1618 }
1619 }
1620 }
1621
1622 }
1623
1624 /**
1625 * Remove a bookmark from this event table.
1626 *
1627 * @param bookmark
1628 * The bookmark to remove
1629 */
1630 public void removeBookmark(final IMarker bookmark) {
1631 for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
1632 if (entry.getValue().equals(bookmark.getId())) {
1633 fBookmarksMap.remove(entry.getKey());
1634 fTable.refresh();
1635 return;
1636 }
1637 }
1638 }
1639
1640 private void toggleBookmark(final long rank) {
1641 if (fBookmarksFile == null) {
1642 return;
1643 }
1644 if (fBookmarksMap.containsKey(rank)) {
1645 final Long markerId = fBookmarksMap.remove(rank);
1646 fTable.refresh();
1647 try {
1648 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
1649 if (bookmark != null) {
1650 bookmark.delete();
1651 }
1652 } catch (final CoreException e) {
1653 displayException(e);
1654 }
1655 } else {
1656 addBookmark(fBookmarksFile);
1657 }
1658 }
1659
1660 /**
1661 * Refresh the bookmarks assigned to this trace, from the contents of a
1662 * bookmark file.
1663 *
1664 * @param bookmarksFile
1665 * The bookmark file to use
1666 */
1667 public void refreshBookmarks(final IFile bookmarksFile) {
1668 fBookmarksFile = bookmarksFile;
1669 if (bookmarksFile == null) {
1670 fBookmarksMap.clear();
1671 fTable.refresh();
1672 return;
1673 }
1674 try {
1675 fBookmarksMap.clear();
1676 for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
1677 final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
1678 if (location != -1) {
1679 final long rank = location;
1680 fBookmarksMap.put(rank, bookmark.getId());
1681 }
1682 }
1683 fTable.refresh();
1684 } catch (final CoreException e) {
1685 displayException(e);
1686 }
1687 }
1688
1689 @Override
1690 public void gotoMarker(final IMarker marker) {
1691 final int rank = marker.getAttribute(IMarker.LOCATION, -1);
1692 if (rank != -1) {
1693 int index = rank;
1694 if (fTable.getData(Key.FILTER_OBJ) != null) {
1695 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1696 } else if (rank >= fTable.getItemCount()) {
1697 fPendingGotoRank = rank;
1698 }
1699 fTable.setSelection(index + 1); // +1 for header row
1700 }
1701 }
1702
1703 // ------------------------------------------------------------------------
1704 // Listeners
1705 // ------------------------------------------------------------------------
1706
1707 /*
1708 * (non-Javadoc)
1709 *
1710 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
1711 */
1712 @Override
1713 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
1714 fTable.refresh();
1715 }
1716
1717 @Override
1718 public void addEventsFilterListener(final ITmfEventsFilterListener listener) {
1719 if (!fEventsFilterListeners.contains(listener)) {
1720 fEventsFilterListeners.add(listener);
1721 }
1722 }
1723
1724 @Override
1725 public void removeEventsFilterListener(final ITmfEventsFilterListener listener) {
1726 fEventsFilterListeners.remove(listener);
1727 }
1728
1729 // ------------------------------------------------------------------------
1730 // Signal handlers
1731 // ------------------------------------------------------------------------
1732
1733 /**
1734 * Handler for the experiment updated signal.
1735 *
1736 * @param signal
1737 * The incoming signal
1738 */
1739 @TmfSignalHandler
1740 public void experimentUpdated(final TmfExperimentUpdatedSignal signal) {
1741 if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) {
1742 return;
1743 }
1744 // Perform the refresh on the UI thread
1745 Display.getDefault().asyncExec(new Runnable() {
1746 @Override
1747 public void run() {
1748 if (!fTable.isDisposed() && (fTrace != null)) {
1749 if (fTable.getData(Key.FILTER_OBJ) == null) {
1750 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1751 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
1752 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1753 fPendingGotoRank = -1;
1754 }
1755 } else {
1756 startFilterThread();
1757 }
1758 }
1759 if (!fRawViewer.isDisposed() && (fTrace != null)) {
1760 fRawViewer.refreshEventCount();
1761 }
1762 }
1763 });
1764 }
1765
1766 /**
1767 * Handler for the trace updated signal
1768 *
1769 * @param signal
1770 * The incoming signal
1771 */
1772 @TmfSignalHandler
1773 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
1774 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
1775 return;
1776 }
1777 // Perform the refresh on the UI thread
1778 Display.getDefault().asyncExec(new Runnable() {
1779 @Override
1780 public void run() {
1781 if (!fTable.isDisposed() && (fTrace != null)) {
1782 if (fTable.getData(Key.FILTER_OBJ) == null) {
1783 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1784 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
1785 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1786 fPendingGotoRank = -1;
1787 }
1788 } else {
1789 startFilterThread();
1790 }
1791 }
1792 if (!fRawViewer.isDisposed() && (fTrace != null)) {
1793 fRawViewer.refreshEventCount();
1794 }
1795 }
1796 });
1797 }
1798
1799 /**
1800 * Handler for the time synch signal.
1801 *
1802 * @param signal
1803 * The incoming signal
1804 */
1805 @SuppressWarnings("unchecked")
1806 @TmfSignalHandler
1807 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
1808 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
1809
1810 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
1811 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
1812 // the method fTrace.getRank() from interfering and delaying ongoing requests.
1813 final TmfDataRequest<ITmfEvent> subRequest = new TmfDataRequest<ITmfEvent>(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
1814
1815 TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
1816
1817 @Override
1818 public void handleData(final ITmfEvent event) {
1819 super.handleData(event);
1820 }
1821
1822 @Override
1823 public void handleCompleted() {
1824 super.handleCompleted();
1825 if (fTrace == null) {
1826 return;
1827 }
1828
1829 // Verify if the event is within the trace range and adjust if necessary
1830 ITmfTimestamp timestamp = ts;
1831 if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
1832 timestamp = fTrace.getStartTime();
1833 }
1834 if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
1835 timestamp = fTrace.getEndTime();
1836 }
1837
1838 // Get the rank of the selected event in the table
1839 final ITmfContext context = fTrace.seekEvent(timestamp);
1840 final long rank = context.getRank();
1841 fSelectedRank = rank;
1842
1843 fTable.getDisplay().asyncExec(new Runnable() {
1844 @Override
1845 public void run() {
1846 // Return if table is disposed
1847 if (fTable.isDisposed()) {
1848 return;
1849 }
1850
1851 int index = (int) rank;
1852 if (fTable.isDisposed()) {
1853 return;
1854 }
1855 if (fTable.getData(Key.FILTER_OBJ) != null)
1856 {
1857 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1858 }
1859 fTable.setSelection(index + 1); // +1 for header row
1860 fRawViewer.selectAndReveal(rank);
1861 }
1862 });
1863 }
1864 };
1865
1866 ((ITmfDataProvider<ITmfEvent>) fTrace).sendRequest(subRequest);
1867 }
1868 }
1869
1870 // ------------------------------------------------------------------------
1871 // Error handling
1872 // ------------------------------------------------------------------------
1873
1874 /**
1875 * Display an exception in a message box
1876 *
1877 * @param e the exception
1878 */
1879 private void displayException(final Exception e) {
1880 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
1881 mb.setText(e.getClass().getName());
1882 mb.setMessage(e.getMessage());
1883 mb.open();
1884 }
1885
1886 }
This page took 0.073654 seconds and 5 git commands to generate.