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