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