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