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