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