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