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