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