Merge branch 'HistogramView'
[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 (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 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
853 fireFilterApplied(null);
854 }
855 }
856
857 tableEditor.getEditor().dispose();
858 }
859 });
860
861 fTable.addKeyListener(new KeyAdapter() {
862 @Override
863 public void keyPressed(KeyEvent e) {
864 e.doit = false;
865 if (e.character == SWT.ESC) {
866 stopFilterThread();
867 stopSearchThread();
868 fTable.refresh();
869 } else if (e.character == SWT.DEL) {
870 stopFilterThread();
871 stopSearchThread();
872 if (fHeaderState == HeaderState.SEARCH) {
873 for (TableColumn column : fTable.getColumns()) {
874 column.setData(Key.SEARCH_OBJ, null);
875 column.setData(Key.SEARCH_TXT, null);
876 }
877 fTable.setData(Key.SEARCH_OBJ, null);
878 fTable.refresh();
879 fireSearchApplied(null);
880 } else if (fHeaderState == HeaderState.FILTER) {
881 clearFilters();
882 }
883 } else if (e.character == SWT.CR) {
884 if ((e.stateMask & SWT.SHIFT) == 0) {
885 searchNext();
886 } else {
887 searchPrevious();
888 }
889 }
890 }
891 });
892 }
893
894 protected void fireFilterApplied(ITmfFilter filter) {
895 for (ITmfEventsFilterListener listener : fEventsFilterListeners) {
896 listener.filterApplied(filter, fTrace);
897 }
898 }
899
900 protected void fireSearchApplied(ITmfFilter filter) {
901 for (ITmfEventsFilterListener listener : fEventsFilterListeners) {
902 listener.searchApplied(filter, fTrace);
903 }
904 }
905
906 protected void startFilterThread() {
907 synchronized (fFilterSyncObj) {
908 if (fFilterThread != null) {
909 fFilterThread.cancel();
910 }
911 final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
912 fFilterThread = new FilterThread(filter);
913 fFilterThread.start();
914 }
915 }
916
917 protected void stopFilterThread() {
918 synchronized (fFilterSyncObj) {
919 if (fFilterThread != null) {
920 fFilterThread.cancel();
921 }
922 }
923 }
924
925 protected void clearFilters() {
926 if (fTable.getData(Key.FILTER_OBJ) == null) {
927 return;
928 }
929 fCache.clearFilter();
930 fTable.clearAll();
931 for (TableColumn column : fTable.getColumns()) {
932 column.setData(Key.FILTER_OBJ, null);
933 column.setData(Key.FILTER_TXT, null);
934 }
935 fTable.setData(Key.FILTER_OBJ, null);
936 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
937 fFilterMatchCount = 0;
938 fFilterCheckCount = 0;
939 if (fSelectedRank >= 0) {
940 fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
941 } else {
942 fTable.setSelection(0);
943 }
944 fireFilterApplied(null);
945 }
946
947 protected class FilterThread extends Thread {
948 private final ITmfFilterTreeNode filter;
949 private TmfEventRequest<TmfEvent> request;
950 private boolean refreshBusy = false;
951 private boolean refreshPending = false;
952 private Object syncObj = new Object();
953
954 public FilterThread(ITmfFilterTreeNode filter) {
955 super("Filter Thread"); //$NON-NLS-1$
956 this.filter = filter;
957 }
958
959 @SuppressWarnings("unchecked")
960 @Override
961 public void run() {
962 request = new TmfEventRequest<TmfEvent>(TmfEvent.class, TmfTimeRange.Eternity, TmfDataRequest.ALL_DATA, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
963 @Override
964 public void handleData(TmfEvent event) {
965 super.handleData(event);
966 if (request.isCancelled()) return;
967 if (filter.matches(event)) {
968 long rank = fFilterCheckCount;
969 int index = (int) fFilterMatchCount;
970 fFilterMatchCount++;
971 fCache.storeEvent(event.clone(), rank, index);
972 refreshTable();
973 } else if (fFilterCheckCount % 100 == 0) {
974 refreshTable();
975 }
976 fFilterCheckCount++;
977 }
978 };
979 ((ITmfDataProvider<TmfEvent>) fTrace).sendRequest(request);
980 try {
981 request.waitForCompletion();
982 } catch (InterruptedException e) {
983 }
984 refreshTable();
985 }
986
987 public void refreshTable() {
988 synchronized (syncObj) {
989 if (refreshBusy) {
990 refreshPending = true;
991 return;
992 } else {
993 refreshBusy = true;
994 }
995 }
996 Display.getDefault().asyncExec(new Runnable() {
997 @Override
998 public void run() {
999 if (request.isCancelled()) return;
1000 if (fTable.isDisposed()) return;
1001 fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
1002 fTable.refresh();
1003 synchronized (syncObj) {
1004 refreshBusy = false;
1005 if (refreshPending) {
1006 refreshPending = false;
1007 refreshTable();
1008 }
1009 }
1010 }
1011 });
1012 }
1013
1014 public void cancel() {
1015 request.cancel();
1016 }
1017 }
1018
1019 protected void searchNext() {
1020 synchronized (fSearchSyncObj) {
1021 if (fSearchThread != null) {
1022 return;
1023 }
1024 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1025 if (searchFilter == null) {
1026 return;
1027 }
1028 int selectionIndex = fTable.getSelectionIndex();
1029 int startIndex;
1030 if (selectionIndex > 0) {
1031 startIndex = selectionIndex; // -1 for header row, +1 for next event
1032 } else {
1033 // header row is selected, start at top event
1034 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
1035 }
1036 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1037 if (eventFilter != null) {
1038 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
1039 }
1040 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
1041 fSearchThread.schedule();
1042 }
1043 }
1044
1045 protected void searchPrevious() {
1046 synchronized (fSearchSyncObj) {
1047 if (fSearchThread != null) {
1048 return;
1049 }
1050 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1051 if (searchFilter == null) {
1052 return;
1053 }
1054 int selectionIndex = fTable.getSelectionIndex();
1055 int startIndex;
1056 if (selectionIndex > 0) {
1057 startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
1058 } else {
1059 // header row is selected, start at precedent of top event
1060 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
1061 }
1062 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1063 if (eventFilter != null) {
1064 startIndex = startIndex - 1; // -1 for top filter status row
1065 }
1066 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
1067 fSearchThread.schedule();
1068 }
1069 }
1070
1071 protected void stopSearchThread() {
1072 fPendingGotoRank = -1;
1073 synchronized (fSearchSyncObj) {
1074 if (fSearchThread != null) {
1075 fSearchThread.cancel();
1076 fSearchThread = null;
1077 }
1078 }
1079 }
1080
1081 protected class SearchThread extends Job {
1082 protected ITmfFilterTreeNode searchFilter;
1083 protected ITmfFilterTreeNode eventFilter;
1084 protected int startIndex;
1085 protected int direction;
1086 protected long rank;
1087 protected long foundRank = -1;
1088 protected TmfDataRequest<TmfEvent> request;
1089
1090 public SearchThread(ITmfFilterTreeNode searchFilter, ITmfFilterTreeNode eventFilter, int startIndex, long currentRank, int direction) {
1091 super(Messages.TmfEventsTable_SearchingJobName);
1092 this.searchFilter = searchFilter;
1093 this.eventFilter = eventFilter;
1094 this.startIndex = startIndex;
1095 this.rank = currentRank;
1096 this.direction = direction;
1097 }
1098
1099 @SuppressWarnings("unchecked")
1100 @Override
1101 protected IStatus run(final IProgressMonitor monitor) {
1102 final Display display = Display.getDefault();
1103 if (startIndex < 0) {
1104 rank = (int) fTrace.getNbEvents() - 1;
1105 } else if (startIndex >= fTable.getItemCount() - (eventFilter == null ? 1 : 3)) { // -1 for header row, -2 for top and bottom filter status rows
1106 rank = 0;
1107 } else {
1108 int idx = startIndex;
1109 while (foundRank == -1) {
1110 CachedEvent event = fCache.peekEvent(idx);
1111 if (event == null) {
1112 break;
1113 }
1114 rank = event.rank;
1115 if (searchFilter.matches(event.event) && (eventFilter == null || eventFilter.matches(event.event))) {
1116 foundRank = event.rank;
1117 break;
1118 }
1119 if (direction == Direction.FORWARD) {
1120 idx++;
1121 } else {
1122 idx--;
1123 }
1124 }
1125 if (foundRank == -1) {
1126 if (direction == Direction.FORWARD) {
1127 rank++;
1128 if (rank > fTrace.getNbEvents() - 1) {
1129 rank = 0;
1130 }
1131 } else {
1132 rank--;
1133 if (rank < 0) {
1134 rank = (int) fTrace.getNbEvents() - 1;
1135 }
1136 }
1137 }
1138 }
1139 int startRank = (int) rank;
1140 boolean wrapped = false;
1141 while (!monitor.isCanceled() && foundRank == -1) {
1142 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1143 if (direction == Direction.BACKWARD) {
1144 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1145 }
1146 request = new TmfDataRequest<TmfEvent>(TmfEvent.class, (int) rank, nbRequested) {
1147 long currentRank = rank;
1148 @Override
1149 public void handleData(TmfEvent event) {
1150 super.handleData(event);
1151 if (searchFilter.matches(event) && (eventFilter == null || eventFilter.matches(event))) {
1152 foundRank = currentRank;
1153 if (direction == Direction.FORWARD) {
1154 done();
1155 return;
1156 }
1157 }
1158 currentRank++;
1159 }
1160 };
1161 ((ITmfDataProvider<TmfEvent>) fTrace).sendRequest(request);
1162 try {
1163 request.waitForCompletion();
1164 if (request.isCancelled()) {
1165 return Status.OK_STATUS;
1166 }
1167 } catch (InterruptedException e) {
1168 synchronized (fSearchSyncObj) {
1169 fSearchThread = null;
1170 }
1171 return Status.OK_STATUS;
1172 }
1173 if (foundRank == -1) {
1174 if (direction == Direction.FORWARD) {
1175 if (rank == 0) {
1176 synchronized (fSearchSyncObj) {
1177 fSearchThread = null;
1178 }
1179 return Status.OK_STATUS;
1180 } else {
1181 nbRequested = (int) rank;
1182 rank = 0;
1183 wrapped = true;
1184 }
1185 } else {
1186 rank--;
1187 if (rank < 0) {
1188 rank = (int) fTrace.getNbEvents() - 1;
1189 wrapped = true;
1190 }
1191 if (rank <= startRank && wrapped) {
1192 synchronized (fSearchSyncObj) {
1193 fSearchThread = null;
1194 }
1195 return Status.OK_STATUS;
1196 }
1197 }
1198 }
1199 }
1200 int index = (int) foundRank;
1201 if (eventFilter != null) {
1202 index = fCache.getFilteredEventIndex(foundRank);
1203 }
1204 final int selection = (int) index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1205
1206 display.asyncExec(new Runnable() {
1207 @Override
1208 public void run() {
1209 if (monitor.isCanceled()) return;
1210 if (fTable.isDisposed()) return;
1211 fTable.setSelection(selection);
1212 fSelectedRank = foundRank;
1213 synchronized (fSearchSyncObj) {
1214 fSearchThread = null;
1215 }
1216 }
1217 });
1218 return Status.OK_STATUS;
1219 }
1220
1221 @Override
1222 protected void canceling() {
1223 request.cancel();
1224 synchronized (fSearchSyncObj) {
1225 fSearchThread = null;
1226 }
1227 }
1228 }
1229
1230 protected void createResources() {
1231 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable.getForeground().getRGB()));
1232 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1233 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1234 }
1235
1236 protected void packColumns() {
1237 if (fPackDone) return;
1238 for (TableColumn column : fTable.getColumns()) {
1239 int headerWidth = column.getWidth();
1240 column.pack();
1241 if (column.getWidth() < headerWidth) {
1242 column.setWidth(headerWidth);
1243 }
1244 }
1245 fPackDone = true;
1246 }
1247
1248 /**
1249 * @param event
1250 * @return
1251 *
1252 * FIXME: Add support for column selection
1253 */
1254 protected String[] extractItemFields(TmfEvent event) {
1255 String[] fields = new String[0];
1256 if (event != null) {
1257 fields = new String[] {
1258 new Long(event.getTimestamp().getValue()).toString(),
1259 event.getSource().getSourceId().toString(),
1260 event.getType().getTypeId().toString(),
1261 event.getReference().getReference().toString(),
1262 event.getContent().toString()
1263 };
1264 }
1265 return fields;
1266 }
1267
1268 public void setFocus() {
1269 fTable.setFocus();
1270 }
1271
1272 /**
1273 * @param trace
1274 * @param disposeOnClose true if the trace should be disposed when the table is disposed
1275 */
1276 public void setTrace(ITmfTrace trace, boolean disposeOnClose) {
1277 if (fTrace != null && fDisposeOnClose) {
1278 fTrace.dispose();
1279 }
1280 fTrace = trace;
1281 fPackDone = false;
1282 fHeaderState = HeaderState.SEARCH;
1283 fSelectedRank = 0;
1284 fDisposeOnClose = disposeOnClose;
1285
1286 // Perform the updates on the UI thread
1287 fTable.getDisplay().syncExec(new Runnable() {
1288 @Override
1289 public void run() {
1290 fTable.removeAll();
1291 // Clear filters if present
1292 fTable.setData(Key.FILTER_OBJ, null);
1293 fTable.setData(Key.FILTER_TXT, null);
1294 fTable.setData(Key.SEARCH_OBJ, null);
1295 fTable.setData(Key.SEARCH_TXT, null);
1296 for (TableColumn column : fTable.getColumns()) {
1297 column.setData(Key.FILTER_OBJ, null);
1298 column.setData(Key.FILTER_TXT, null);
1299 column.setData(Key.SEARCH_OBJ, null);
1300 column.setData(Key.SEARCH_TXT, null);
1301 }
1302 fCache.setTrace(fTrace); // Clear the cache
1303 if (fTrace != null) {
1304 if (!fTable.isDisposed() && fTrace != null) {
1305 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1306 }
1307 fRawViewer.setTrace(fTrace);
1308 }
1309 }
1310 });
1311 }
1312
1313 // ------------------------------------------------------------------------
1314 // Event cache
1315 // ------------------------------------------------------------------------
1316
1317 public void cacheUpdated(final boolean completed) {
1318 synchronized (fCacheUpdateSyncObj) {
1319 if (fCacheUpdateBusy) {
1320 fCacheUpdatePending = true;
1321 fCacheUpdateCompleted = completed;
1322 return;
1323 } else {
1324 fCacheUpdateBusy = true;
1325 }
1326 }
1327 // Event cache is now updated. Perform update on the UI thread
1328 if (!fTable.isDisposed()) {
1329 fTable.getDisplay().asyncExec(new Runnable() {
1330 @Override
1331 public void run() {
1332 if (!fTable.isDisposed()) {
1333 fTable.refresh();
1334 packColumns();
1335 }
1336 if (completed) {
1337 populateCompleted();
1338 }
1339 synchronized (fCacheUpdateSyncObj) {
1340 fCacheUpdateBusy = false;
1341 if (fCacheUpdatePending) {
1342 fCacheUpdatePending = false;
1343 cacheUpdated(fCacheUpdateCompleted);
1344 }
1345 }
1346 }
1347 });
1348 }
1349 }
1350
1351 protected void populateCompleted() {
1352 // Nothing by default;
1353 }
1354
1355 // ------------------------------------------------------------------------
1356 // Bookmark handling
1357 // ------------------------------------------------------------------------
1358
1359 public void addBookmark(IResource resource) {
1360 fBookmarksResource = resource;
1361 TableItem[] selection = fTable.getSelection();
1362 if (selection.length > 0) {
1363 TableItem tableItem = selection[0];
1364 if (tableItem.getData(Key.RANK) != null) {
1365 StringBuffer defaultMessage = new StringBuffer();
1366 for (int i = 0; i < fTable.getColumns().length; i++) {
1367 if (i > 0) {
1368 defaultMessage.append(", "); //$NON-NLS-1$
1369 }
1370 defaultMessage.append(tableItem.getText(i));
1371 }
1372 InputDialog dialog = new InputDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1373 Messages.TmfEventsTable_AddBookmarkDialogTitle, Messages.TmfEventsTable_AddBookmarkDialogText, defaultMessage.toString(), null);
1374 if (dialog.open() == Dialog.OK) {
1375 String message = dialog.getValue();
1376 try {
1377 IMarker bookmark = resource.createMarker(IMarker.BOOKMARK);
1378 if (bookmark.exists()) {
1379 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
1380 long rank = (Long) tableItem.getData(Key.RANK);
1381 int location = (int) rank;
1382 bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
1383 fBookmarksMap.put(rank, bookmark.getId());
1384 fTable.refresh();
1385 }
1386 } catch (CoreException e) {
1387 e.printStackTrace();
1388 }
1389 }
1390 }
1391 }
1392
1393 }
1394
1395 public void removeBookmark(IMarker bookmark) {
1396 for (Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
1397 if (entry.getValue().equals(bookmark.getId())) {
1398 fBookmarksMap.remove(entry.getKey());
1399 fTable.refresh();
1400 return;
1401 }
1402 }
1403 }
1404
1405 private void toggleBookmark(long rank) {
1406 if (fBookmarksResource == null) {
1407 return;
1408 }
1409 if (fBookmarksMap.containsKey(rank)) {
1410 Long markerId = fBookmarksMap.remove(rank);
1411 fTable.refresh();
1412 try {
1413 IMarker bookmark = fBookmarksResource.findMarker(markerId);
1414 if (bookmark != null) {
1415 bookmark.delete();
1416 }
1417 } catch (CoreException e) {
1418 e.printStackTrace();
1419 }
1420 } else {
1421 addBookmark(fBookmarksResource);
1422 }
1423 }
1424
1425 public void refreshBookmarks(IResource resource) {
1426 fBookmarksResource = resource;
1427 try {
1428 fBookmarksMap.clear();
1429 for (IMarker bookmark : resource.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
1430 int location = bookmark.getAttribute(IMarker.LOCATION, -1);
1431 if (location != -1) {
1432 long rank = (long) location;
1433 fBookmarksMap.put(rank, bookmark.getId());
1434 }
1435 }
1436 fTable.refresh();
1437 } catch (CoreException e) {
1438 e.printStackTrace();
1439 }
1440 }
1441
1442 @Override
1443 public void gotoMarker(IMarker marker) {
1444 int rank = marker.getAttribute(IMarker.LOCATION, -1);
1445 if (rank != -1) {
1446 int index = (int) rank;
1447 if (fTable.getData(Key.FILTER_OBJ) != null) {
1448 index = fCache.getFilteredEventIndex(rank) + 1; //+1 for top filter status row
1449 } else if (rank >= fTable.getItemCount()) {
1450 fPendingGotoRank = rank;
1451 }
1452 fTable.setSelection(index + 1); // +1 for header row
1453 }
1454 }
1455
1456 // ------------------------------------------------------------------------
1457 // Listeners
1458 // ------------------------------------------------------------------------
1459
1460 /* (non-Javadoc)
1461 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
1462 */
1463 @Override
1464 public void colorSettingsChanged(ColorSetting[] colorSettings) {
1465 fTable.refresh();
1466 }
1467
1468 @Override
1469 public void addEventsFilterListener(ITmfEventsFilterListener listener) {
1470 if (!fEventsFilterListeners.contains(listener)) {
1471 fEventsFilterListeners.add(listener);
1472 }
1473 }
1474
1475 @Override
1476 public void removeEventsFilterListener(ITmfEventsFilterListener listener) {
1477 fEventsFilterListeners.remove(listener);
1478 }
1479
1480 // ------------------------------------------------------------------------
1481 // Signal handlers
1482 // ------------------------------------------------------------------------
1483
1484 @TmfSignalHandler
1485 public void experimentUpdated(TmfExperimentUpdatedSignal signal) {
1486 if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) return;
1487 // Perform the refresh on the UI thread
1488 Display.getDefault().asyncExec(new Runnable() {
1489 @Override
1490 public void run() {
1491 if (!fTable.isDisposed() && fTrace != null) {
1492 if (fTable.getData(Key.FILTER_OBJ) == null) {
1493 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1494 }
1495 if (fPendingGotoRank != -1 && fPendingGotoRank + 1 < fTable.getItemCount()) { // +1 for header row
1496 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1497 fPendingGotoRank = -1;
1498 }
1499 }
1500 if (!fRawViewer.isDisposed() && fTrace != null) {
1501 fRawViewer.refreshEventCount();
1502 }
1503 }
1504 });
1505 }
1506
1507 @TmfSignalHandler
1508 public void traceUpdated(TmfTraceUpdatedSignal signal) {
1509 if ((signal.getTrace() != fTrace ) || fTable.isDisposed()) return;
1510 // Perform the refresh on the UI thread
1511 Display.getDefault().asyncExec(new Runnable() {
1512 @Override
1513 public void run() {
1514 if (!fTable.isDisposed() && fTrace != null) {
1515 if (fTable.getData(Key.FILTER_OBJ) == null) {
1516 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1517 if (fPendingGotoRank != -1 && fPendingGotoRank + 1 < fTable.getItemCount()) { // +1 for header row
1518 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1519 fPendingGotoRank = -1;
1520 }
1521 }
1522 }
1523 if (!fRawViewer.isDisposed() && fTrace != null) {
1524 fRawViewer.refreshEventCount();
1525 }
1526 }
1527 });
1528 }
1529
1530 // private boolean fRefreshPending = false;
1531 // @TmfSignalHandler
1532 // public synchronized void rangeSynched(TmfRangeSynchSignal signal) {
1533 // if (!fRefreshPending && !fTable.isDisposed()) {
1534 // // Perform the refresh on the UI thread
1535 // fRefreshPending = true;
1536 // Display.getDefault().asyncExec(new Runnable() {
1537 // @Override
1538 // public void run() {
1539 // fRefreshPending = false;
1540 // if (!fTable.isDisposed() && fTrace != null) {
1541 // fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1542 // }
1543 // }
1544 // });
1545 // }
1546 // }
1547
1548 @SuppressWarnings("unchecked")
1549 @TmfSignalHandler
1550 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
1551 if ((signal.getSource() != fTable) && (fTrace != null) && (!fTable.isDisposed())) {
1552
1553 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
1554 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
1555 // the method fTrace.getRank() from interfering and delaying ongoing requests.
1556 final TmfDataRequest<TmfEvent> subRequest = new TmfDataRequest<TmfEvent>(TmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
1557
1558 TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
1559
1560 @Override
1561 public void handleData(TmfEvent event) {
1562 super.handleData(event);
1563 }
1564
1565 @Override
1566 public void handleCompleted() {
1567
1568 // Verify if event is within the trace range
1569 final TmfTimestamp timestamp[] = new TmfTimestamp[1];
1570 timestamp[0] = ts; // signal.getCurrentTime();
1571 if (timestamp[0].compareTo(fTrace.getStartTime(), true) == -1) {
1572 timestamp[0] = fTrace.getStartTime();
1573 }
1574 if (timestamp[0].compareTo(fTrace.getEndTime(), true) == 1) {
1575 timestamp[0] = fTrace.getEndTime();
1576 }
1577
1578 // Get the rank for the event selection in the table
1579 final long rank = fTrace.getRank(timestamp[0]);
1580 fSelectedRank = rank;
1581
1582 fTable.getDisplay().asyncExec(new Runnable() {
1583 @Override
1584 public void run() {
1585 // Return if table is disposed
1586 if (fTable.isDisposed()) return;
1587
1588 int index = (int) rank;
1589 if (fTable.isDisposed()) return;
1590 if (fTable.getData(Key.FILTER_OBJ) != null) {
1591 index = fCache.getFilteredEventIndex(rank) + 1; //+1 for top filter status row
1592 }
1593 fTable.setSelection(index + 1); // +1 for header row
1594 fRawViewer.selectAndReveal(rank);
1595 }
1596 });
1597 super.handleCompleted();
1598 }
1599 };
1600
1601 ((ITmfDataProvider<TmfEvent>) fTrace).sendRequest(subRequest);
1602 }
1603 }
1604
1605 }
This page took 0.066065 seconds and 6 git commands to generate.