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