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