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