1 /*******************************************************************************
2 * Copyright (c) 2010 Ericsson
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
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 *******************************************************************************/
16 package org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.events
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Arrays
;
20 import java
.util
.HashMap
;
22 import java
.util
.Map
.Entry
;
23 import java
.util
.regex
.Pattern
;
24 import java
.util
.regex
.PatternSyntaxException
;
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
;
103 * <b><u>TmfEventsTable</u></b>
105 public class TmfEventsTable
extends TmfComponent
implements IGotoMarker
, IColorSettingsListener
, ITmfEventsFilterProvider
{
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
;
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$
126 public static enum HeaderState
{
131 interface Direction
{
136 // ------------------------------------------------------------------------
138 // ------------------------------------------------------------------------
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;
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
>();
159 // Bookmark map <Rank, MarkerId>
160 protected Map
<Long
, Long
> fBookmarksMap
= new HashMap
<Long
, Long
>();
161 protected IResource fBookmarksResource
;
162 protected long fPendingGotoRank
= -1;
165 protected LocalResourceManager fResourceManager
= new LocalResourceManager(JFaceResources
.getResources());
166 protected Color fGrayColor
;
167 protected Color fGreenColor
;
168 protected Font fBoldFont
;
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
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
)
187 private class FilteredEvent
{
191 public FilteredEvent (TmfEvent event
, long rank
) {
197 // ------------------------------------------------------------------------
199 // ------------------------------------------------------------------------
201 private final int fCacheSize
;
202 private TmfEvent
[] fCache
;
203 private int fCacheStartIndex
= 0;
204 private int fCacheEndIndex
= 0;
206 private boolean fDisposeOnClose
;
208 // ------------------------------------------------------------------------
210 // ------------------------------------------------------------------------
212 public TmfEventsTable(Composite parent
, int cacheSize
) {
213 this(parent
, cacheSize
, COLUMN_DATA
);
216 public TmfEventsTable(Composite parent
, int cacheSize
, ColumnData
[] columnData
) {
217 super("TmfEventsTable"); //$NON-NLS-1$
219 fComposite
= new Composite(parent
, SWT
.NONE
);
220 GridLayout gl
= new GridLayout(1, false);
223 gl
.verticalSpacing
= 0;
224 fComposite
.setLayout(gl
);
226 fSashForm
= new SashForm(fComposite
, SWT
.HORIZONTAL
);
227 fSashForm
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
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
);
233 // Set the table layout
234 GridData layoutData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
235 fTable
.setLayoutData(layoutData
);
237 // Some cosmetic enhancements
238 fTable
.setHeaderVisible(true);
239 fTable
.setLinesVisible(true);
242 setColumnHeaders(columnData
);
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
);
253 // Set the frozen row for header row
254 fTable
.setFrozenRowCount(1);
256 // Create the header row cell editor
257 createHeaderEditor();
259 // Handle the table item selection
260 fTable
.addSelectionListener(new SelectionAdapter() {
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
));
271 if (selectedTableItem
.getData(Key
.TIMESTAMP
) instanceof TmfTimestamp
) {
272 TmfTimestamp ts
= (TmfTimestamp
) selectedTableItem
.getData(Key
.TIMESTAMP
);
273 broadcast(new TmfTimeSynchSignal(fTable
, ts
));
280 fCacheSize
= Math
.max(cacheSize
, Display
.getDefault().getBounds().height
/ fTable
.getItemHeight());
281 fCache
= new TmfEvent
[fCacheSize
];
283 // Handle the table item requests
284 fTable
.addListener(SWT
.SetData
, new Listener() {
287 public void handleEvent(Event event
) {
289 final TableItem item
= (TableItem
) event
.item
;
290 final int index
= event
.index
- 1; // -1 for the header row
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
;
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
);
311 item
.setForeground(i
, fGrayColor
);
312 item
.setFont(i
, fTable
.getFont());
314 item
.setText(i
, filter
);
315 item
.setForeground(i
, fGreenColor
);
316 item
.setFont(i
, fBoldFont
);
322 // If available, return the cached data
323 if ((index
>= fCacheStartIndex
) && (index
< fCacheEndIndex
)) {
324 int i
= index
- fCacheStartIndex
;
325 setItemData(item
, fCache
[i
], index
);
329 // If filter is applied, use the filtered event cache
330 if (fTable
.getData(Key
.FILTER_OBJ
) != null) {
331 setFilteredItemData(item
, index
);
335 // Else, fill the cache asynchronously (and off the UI thread)
336 populateCache(index
);
341 fTable
.addMouseListener(new MouseAdapter() {
343 public void mouseDoubleClick(MouseEvent event
) {
344 if (event
.button
!= 1) {
347 // Identify the selected row
348 Point point
= new Point(event
.x
, event
.y
);
349 TableItem item
= fTable
.getItem(point
);
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
);
356 toggleBookmark(rank
);
366 ColorSettingsManager
.addColorSettingsListener(this);
368 fTable
.setItemCount(+1); // +1 for header row
370 fRawViewer
= new TmfRawEventViewer(fSashForm
, SWT
.H_SCROLL
| SWT
.V_SCROLL
);
372 fRawViewer
.addSelectionListener(new SelectionAdapter() {
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
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());
392 TableItem
[] selection
= fTable
.getSelection();
393 if (selection
!= null && selection
.length
> 0) {
394 TmfTimestamp ts
= (TmfTimestamp
) fTable
.getSelection()[0].getData(Key
.TIMESTAMP
);
396 broadcast(new TmfTimeSynchSignal(fTable
, ts
));
402 fSashForm
.setWeights(new int[] {1, 1});
403 fRawViewer
.setVisible(false);
408 protected void createPopupMenu() {
409 final IAction showTableAction
= new Action(Messages
.TmfEventsTable_ShowTableActionText
) {
412 fTable
.setVisible(true);
417 final IAction hideTableAction
= new Action(Messages
.TmfEventsTable_HideTableActionText
) {
420 fTable
.setVisible(false);
425 final IAction showRawAction
= new Action(Messages
.TmfEventsTable_ShowRawActionText
) {
428 fRawViewer
.setVisible(true);
430 int index
= fTable
.getSelectionIndex();
431 if (index
>= +1) { // +1 for header row
432 fRawViewer
.selectAndReveal(index
- 1);
437 final IAction hideRawAction
= new Action(Messages
.TmfEventsTable_HideRawActionText
) {
440 fRawViewer
.setVisible(false);
445 final IAction showSearchBarAction
= new Action(Messages
.TmfEventsTable_ShowSearchBarActionText
) {
448 fHeaderState
= HeaderState
.SEARCH
;
453 final IAction showFilterBarAction
= new Action(Messages
.TmfEventsTable_ShowFilterBarActionText
) {
456 fHeaderState
= HeaderState
.FILTER
;
461 final IAction clearFiltersAction
= new Action(Messages
.TmfEventsTable_ClearFiltersActionText
) {
470 class ToggleBookmarkAction
extends Action
{
472 public ToggleBookmarkAction(String text
, long rank
) {
479 toggleBookmark(fRank
);
483 final MenuManager tablePopupMenu
= new MenuManager();
484 tablePopupMenu
.setRemoveAllWhenShown(true);
485 tablePopupMenu
.addMenuListener(new IMenuListener() {
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
);
493 tablePopupMenu
.add(showFilterBarAction
);
497 Point point
= fTable
.toControl(Display
.getDefault().getCursorLocation());
498 TableItem item
= fTable
.getItem(point
);
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
));
509 tablePopupMenu
.add(new ToggleBookmarkAction(Messages
.TmfEventsTable_AddBookmarkActionText
, rank
));
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
);
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()) {
535 synchronized (fFilteredEventCache
) {
536 fFilteredEventCache
.clear();
537 fFilterMatchCount
= 0;
538 fFilterCheckCount
= 0;
540 fCacheStartIndex
= 0;
543 fTable
.setData(Key
.FILTER_OBJ
, filter
);
544 fTable
.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
546 fireFilterApplied(filter
);
550 tablePopupMenu
.add(subMenu
);
552 appendToTablePopupMenu(tablePopupMenu
, item
);
556 final MenuManager rawViewerPopupMenu
= new MenuManager();
557 rawViewerPopupMenu
.setRemoveAllWhenShown(true);
558 rawViewerPopupMenu
.addMenuListener(new IMenuListener() {
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
);
569 appendToRawPopupMenu(tablePopupMenu
);
573 Menu menu
= tablePopupMenu
.createContextMenu(fTable
);
574 fTable
.setMenu(menu
);
576 menu
= rawViewerPopupMenu
.createContextMenu(fRawViewer
);
577 fRawViewer
.setMenu(menu
);
580 protected void appendToTablePopupMenu(MenuManager tablePopupMenu
, TableItem selectedItem
) {
581 // override to append more actions
584 protected void appendToRawPopupMenu(MenuManager rawViewerPopupMenu
) {
585 // override to append more actions
589 public void dispose() {
592 ColorSettingsManager
.removeColorSettingsListener(this);
593 fComposite
.dispose();
594 if (fTrace
!= null && fDisposeOnClose
) {
597 fResourceManager
.dispose();
601 public void setLayoutData(Object layoutData
) {
602 fComposite
.setLayoutData(layoutData
);
605 public TmfVirtualTable
getTable() {
612 * FIXME: Add support for column selection
614 protected void setColumnHeaders(ColumnData
[] columnData
) {
615 fTable
.setColumnHeaders(columnData
);
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
);
623 boolean bookmark
= false;
624 if (fBookmarksMap
.containsKey(rank
)) {
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
)) {
635 searchNoMatch
= true;
639 ColorSetting colorSetting
= ColorSettingsManager
.getColorSetting(event
);
641 item
.setForeground(colorSetting
.getDimmedForegroundColor());
642 item
.setBackground(colorSetting
.getDimmedBackgroundColor());
644 item
.setForeground(colorSetting
.getForegroundColor());
645 item
.setBackground(colorSetting
.getBackgroundColor());
650 item
.setImage(SEARCH_MATCH_BOOKMARK_IMAGE
);
652 item
.setImage(SEARCH_MATCH_IMAGE
);
654 } else if (bookmark
) {
655 item
.setImage(BOOKMARK_IMAGE
);
657 item
.setImage((Image
) null);
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
);
668 for (int i
= 0; i
< fTable
.getColumns().length
; i
++) {
670 if (fFilterCheckCount
== fTrace
.getNbEvents()) {
671 item
.setImage(FILTER_IMAGE
);
673 item
.setImage(STOP_IMAGE
);
675 item
.setText(0, fFilterMatchCount
+ "/" + fFilterCheckCount
); //$NON-NLS-1$
677 item
.setText(i
, ""); //$NON-NLS-1$
681 item
.setData(Key
.TIMESTAMP
, null);
682 item
.setData(Key
.RANK
, null);
683 item
.setForeground(null);
684 item
.setBackground(null);
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
) {
696 return fFilteredEventCache
.size();
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;
706 // Handle the header row selection
707 fTable
.addMouseListener(new MouseAdapter() {
713 public void mouseDown(MouseEvent event
) {
714 if (event
.button
!= 1) {
717 // Identify the selected row
718 Point point
= new Point(event
.x
, event
.y
);
719 item
= fTable
.getItem(point
);
721 // Header row selected
722 if (item
!= null && fTable
.indexOf(item
) == 0) {
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
;
735 // Identify the selected column
737 for (int i
= 0; i
< fTable
.getColumns().length
; i
++) {
738 Rectangle rect
= item
.getBounds(i
);
739 if (rect
.contains(point
)) {
745 if (columnIndex
== -1) {
749 column
= fTable
.getColumns()[columnIndex
];
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
;
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
);
764 newEditor
.addFocusListener(new FocusAdapter() {
766 public void focusLost(FocusEvent e
) {
767 boolean changed
= updateHeader(newEditor
.getText());
773 newEditor
.addKeyListener(new KeyAdapter() {
775 public void keyPressed(KeyEvent e
) {
776 if (e
.character
== SWT
.CR
) {
777 updateHeader(newEditor
.getText());
779 } else if (e
.character
== SWT
.ESC
) {
780 tableEditor
.getEditor().dispose();
784 newEditor
.selectAll();
785 newEditor
.setFocus();
786 tableEditor
.setEditor(newEditor
, item
, columnIndex
);
791 * returns true is value was changed
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
;
803 if (text
.trim().length() > 0) {
805 String regex
= TmfFilterMatchesNode
.regexFix(text
);
806 Pattern
.compile(regex
);
807 if (regex
.equals(column
.getData(txtKey
))) {
808 tableEditor
.getEditor().dispose();
811 TmfFilterMatchesNode filter
= new TmfFilterMatchesNode(null);
812 String fieldId
= (String
) column
.getData(Key
.FIELD_ID
);
813 if (fieldId
== null) {
814 fieldId
= column
.getText();
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());
826 if (column
.getData(txtKey
) == null) {
827 tableEditor
.getEditor().dispose();
830 column
.setData(objKey
, null);
831 column
.setData(txtKey
, null);
836 private void applyHeader() {
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
);
846 if (filter
.getChildrenCount() > 0) {
847 fTable
.setData(Key
.SEARCH_OBJ
, filter
);
850 fireSearchApplied(filter
);
852 fTable
.setData(Key
.SEARCH_OBJ
, null);
854 fireSearchApplied(null);
856 } else if (fHeaderState
== HeaderState
.FILTER
) {
857 synchronized (fFilteredEventCache
) {
858 fFilteredEventCache
.clear();
859 fFilterMatchCount
= 0;
860 fFilterCheckCount
= 0;
862 fCacheStartIndex
= 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
);
871 if (filter
.getChildrenCount() > 0) {
873 fTable
.setData(Key
.FILTER_OBJ
, filter
);
874 fTable
.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
876 fireFilterApplied(filter
);
880 fTable
.setData(Key
.FILTER_OBJ
, null);
881 fTable
.setItemCount((int) fTrace
.getNbEvents() + 1); // +1 for header row
882 fireFilterApplied(null);
886 tableEditor
.getEditor().dispose();
890 fTable
.addKeyListener(new KeyAdapter() {
892 public void keyPressed(KeyEvent e
) {
894 if (e
.character
== SWT
.ESC
) {
898 } else if (e
.character
== SWT
.DEL
) {
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);
906 fTable
.setData(Key
.SEARCH_OBJ
, null);
908 fireSearchApplied(null);
909 } else if (fHeaderState
== HeaderState
.FILTER
) {
912 } else if (e
.character
== SWT
.CR
) {
913 if ((e
.stateMask
& SWT
.SHIFT
) == 0) {
923 protected void fireFilterApplied(ITmfFilter filter
) {
924 for (ITmfEventsFilterListener listener
: fEventsFilterListeners
) {
925 listener
.filterApplied(filter
, fTrace
);
929 protected void fireSearchApplied(ITmfFilter filter
) {
930 for (ITmfEventsFilterListener listener
: fEventsFilterListeners
) {
931 listener
.searchApplied(filter
, fTrace
);
935 protected void startFilterThread() {
936 synchronized (fFilterSyncObj
) {
937 if (fFilterThread
!= null) {
938 fFilterThread
.cancel();
940 final ITmfFilterTreeNode filter
= (ITmfFilterTreeNode
) fTable
.getData(Key
.FILTER_OBJ
);
941 fFilterThread
= new FilterThread(filter
);
942 fFilterThread
.start();
946 protected void stopFilterThread() {
947 synchronized (fFilterSyncObj
) {
948 if (fFilterThread
!= null) {
949 fFilterThread
.cancel();
954 protected void clearFilters() {
955 if (fTable
.getData(Key
.FILTER_OBJ
) == null) {
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
;
966 for (TableColumn column
: fTable
.getColumns()) {
967 column
.setData(Key
.FILTER_OBJ
, null);
968 column
.setData(Key
.FILTER_TXT
, null);
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;
977 if (selectedRank
>= 0) {
978 fTable
.setSelection((int) selectedRank
+ 1); // +1 for header row
980 fTable
.setSelection(0);
982 fireFilterApplied(null);
985 protected class FilterThread
extends Thread
{
986 private final ITmfFilterTreeNode filter
;
987 private TmfDataRequest
<TmfEvent
> request
;
988 private long lastRefreshTime
;
990 public FilterThread(ITmfFilterTreeNode filter
) {
991 super("Filter Thread"); //$NON-NLS-1$
992 this.filter
= filter
;
995 @SuppressWarnings("unchecked")
998 final Display display
= Display
.getDefault();
999 lastRefreshTime
= System
.currentTimeMillis();
1001 request
= new TmfDataRequest
<TmfEvent
>(TmfEvent
.class, 0, Integer
.MAX_VALUE
, ExecutionType
.BACKGROUND
) {
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
++;
1011 display
.asyncExec(new Runnable() {
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
1020 if (fFilterCheckCount
% 100 == 0) {
1021 long currentTime
= System
.currentTimeMillis();
1022 if (currentTime
- lastRefreshTime
> 1000) {
1023 lastRefreshTime
= currentTime
;
1024 display
.asyncExec(new Runnable() {
1027 if (request
.isCancelled()) return;
1028 if (fTable
.isDisposed()) return;
1036 ((ITmfDataProvider
<TmfEvent
>) fTrace
).sendRequest(request
);
1038 request
.waitForCompletion();
1039 } catch (InterruptedException e
) {
1041 display
.asyncExec(new Runnable() {
1044 if (fTable
.isDisposed()) return;
1050 public void cancel() {
1055 protected void searchNext() {
1056 synchronized (fSearchSyncObj
) {
1057 if (fSearchThread
!= null) {
1060 final ITmfFilterTreeNode filter
= (ITmfFilterTreeNode
) fTable
.getData(Key
.SEARCH_OBJ
);
1061 if (filter
== null) {
1064 int selectionIndex
= fTable
.getSelectionIndex();
1066 if (selectionIndex
> 0) {
1067 startIndex
= selectionIndex
; // -1 for header row, +1 for next event
1069 // header row is selected, start at top event
1070 startIndex
= Math
.max(0, fTable
.getTopIndex() - 1); // -1 for header row
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
1075 fSearchThread
= new SearchThread(filter
, startIndex
, Direction
.FORWARD
);
1077 fSearchThread
.schedule();
1081 protected void searchPrevious() {
1082 synchronized (fSearchSyncObj
) {
1083 if (fSearchThread
!= null) {
1086 final ITmfFilterTreeNode filter
= (ITmfFilterTreeNode
) fTable
.getData(Key
.SEARCH_OBJ
);
1087 if (filter
== null) {
1090 int selectionIndex
= fTable
.getSelectionIndex();
1092 if (selectionIndex
> 0) {
1093 startIndex
= selectionIndex
- 2; // -1 for header row, -1 for previous event
1095 // header row is selected, start at precedent of top event
1096 startIndex
= fTable
.getTopIndex() - 2; // -1 for header row, -1 for previous event
1098 if (fTable
.getData(Key
.FILTER_OBJ
) != null) {
1099 fSearchThread
= new SearchFilteredThread(filter
, startIndex
- 1, Direction
.BACKWARD
); // -1 for top filter status row
1101 fSearchThread
= new SearchThread(filter
, startIndex
, Direction
.BACKWARD
);
1103 fSearchThread
.schedule();
1107 protected void stopSearchThread() {
1108 fPendingGotoRank
= -1;
1109 synchronized (fSearchSyncObj
) {
1110 if (fSearchThread
!= null) {
1111 fSearchThread
.cancel();
1112 fSearchThread
= null;
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
;
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
;
1131 @SuppressWarnings("unchecked")
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) {
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
) {
1145 public void handleData(TmfEvent event
) {
1146 super.handleData(event
);
1147 if (filter
.matches(event
)) {
1148 rank
= startRank
+ getNbRead() - 1;
1153 ((ITmfDataProvider
<TmfEvent
>) fTrace
).sendRequest(request
);
1155 request
.waitForCompletion();
1156 if (request
.isCancelled()) {
1157 return Status
.OK_STATUS
;
1159 } catch (InterruptedException e
) {
1160 return Status
.OK_STATUS
;
1163 if (direction
== Direction
.FORWARD
) {
1164 if (startIndex
== 0) {
1165 return Status
.OK_STATUS
;
1167 nbRequested
= startIndex
;
1172 if (startIndex
< 0) {
1173 startIndex
= (int) fTrace
.getNbEvents() - 1;
1175 if (startIndex
== startRank
) {
1176 return Status
.OK_STATUS
;
1181 final int selection
= (int) rank
+ 1; // +1 for header row
1183 display
.asyncExec(new Runnable() {
1186 if (monitor
.isCanceled()) return;
1187 if (fTable
.isDisposed()) return;
1188 fTable
.setSelection(selection
);
1189 synchronized (fSearchSyncObj
) {
1190 fSearchThread
= null;
1194 return Status
.OK_STATUS
;
1198 protected void canceling() {
1203 protected class SearchFilteredThread
extends SearchThread
{
1205 public SearchFilteredThread(ITmfFilterTreeNode filter
, int startIndex
, int direction
) {
1206 super(filter
, startIndex
, direction
);
1210 protected IStatus
run(final IProgressMonitor monitor
) {
1211 final Display display
= Display
.getDefault();
1212 if (fFilteredEventCache
.size() == 0) {
1213 return Status
.OK_STATUS
;
1215 if (startRank
< 0) {
1216 startRank
= fFilteredEventCache
.size() - 1;
1218 int index
= (int) startRank
;
1219 if (direction
== Direction
.FORWARD
) {
1220 if (index
>= fFilteredEventCache
.size()) {
1225 index
= fFilteredEventCache
.size() - 1;
1228 TmfEvent event
= null;
1229 while (!monitor
.isCanceled()) {
1230 event
= fFilteredEventCache
.get(index
).event
;
1231 if (filter
.matches(event
)) {
1234 if (direction
== Direction
.FORWARD
) {
1236 if (index
== startRank
) {
1237 return Status
.OK_STATUS
;
1239 if (index
>= fFilteredEventCache
.size()) {
1244 if (index
== startRank
) {
1245 return Status
.OK_STATUS
;
1248 index
= fFilteredEventCache
.size() - 1;
1252 final int selection
= (int) index
+ 2; // +1 for header row, +1 for top filter status row
1254 display
.asyncExec(new Runnable() {
1257 if (monitor
.isCanceled()) return;
1258 if (fTable
.isDisposed()) return;
1259 fTable
.setSelection(selection
);
1260 synchronized (fSearchSyncObj
) {
1261 fSearchThread
= null;
1265 return Status
.OK_STATUS
;
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
));
1275 protected void packColumns() {
1276 if (fPackDone
) return;
1277 for (TableColumn column
: fTable
.getColumns()) {
1278 int headerWidth
= column
.getWidth();
1280 if (column
.getWidth() < headerWidth
) {
1281 column
.setWidth(headerWidth
);
1291 * FIXME: Add support for column selection
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()
1307 public void setFocus() {
1313 * @param disposeOnClose true if the trace should be disposed when the table is disposed
1315 public void setTrace(ITmfTrace trace
, boolean disposeOnClose
) {
1316 if (fTrace
!= null && fDisposeOnClose
) {
1321 fHeaderState
= HeaderState
.SEARCH
;
1323 fDisposeOnClose
= disposeOnClose
;
1325 // Perform the updates on the UI thread
1326 fTable
.getDisplay().syncExec(new Runnable() {
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);
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
1346 fRawViewer
.setTrace(fTrace
);
1352 // ------------------------------------------------------------------------
1353 // Event cache population
1354 // ------------------------------------------------------------------------
1356 // The event fetching job
1358 private synchronized void populateCache(final int index
) {
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
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.
1370 if (job
.getState() != Job
.NONE
) {
1371 if (index
>= fCacheStartIndex
&& index
< (fCacheStartIndex
+ fCacheSize
)) {
1374 // The new index is out of the requested range
1375 // Kill the job and start a new one
1380 fCacheStartIndex
= index
;
1381 fCacheEndIndex
= index
;
1383 job
= new Job("Fetching Events") { //$NON-NLS-1$
1385 @SuppressWarnings("unchecked")
1386 protected IStatus
run(final IProgressMonitor monitor
) {
1388 TmfDataRequest
<TmfEvent
> request
= new TmfDataRequest
<TmfEvent
>(TmfEvent
.class, index
, fCacheSize
) {
1389 private int count
= 0;
1391 public void handleData(TmfEvent event
) {
1392 // If the job is canceled, cancel the request so waitForCompletion() will unlock
1393 if (monitor
.isCanceled()) {
1397 super.handleData(event
);
1398 if (event
!= null) {
1399 fCache
[count
++] = event
.clone();
1400 fCacheEndIndex
++; // TODO: Need to protect this??
1405 ((ITmfDataProvider
<TmfEvent
>) fTrace
).sendRequest(request
);
1407 request
.waitForCompletion();
1408 } catch (InterruptedException e
) {
1409 e
.printStackTrace();
1412 // Event cache is now updated. Perform update on the UI thread
1413 if (!fTable
.isDisposed() && !monitor
.isCanceled()) {
1414 fTable
.getDisplay().asyncExec(new Runnable() {
1417 if (!fTable
.isDisposed()) {
1421 // fTrace.seekEvent(fSelectedRank);
1426 // Flag the UI thread that the cache is ready
1427 if (monitor
.isCanceled()) {
1428 return Status
.CANCEL_STATUS
;
1430 return Status
.OK_STATUS
;
1434 //job.setSystem(true);
1435 job
.setPriority(Job
.SHORT
);
1439 // ------------------------------------------------------------------------
1440 // Bookmark handling
1441 // ------------------------------------------------------------------------
1443 public void addBookmark(IResource resource
) {
1444 fBookmarksResource
= resource
;
1445 TableItem
[] selection
= fTable
.getSelection();
1446 if (selection
.length
> 0) {
1447 TableItem tableItem
= selection
[0];
1448 if (tableItem
.getData(Key
.RANK
) != null) {
1449 StringBuffer defaultMessage
= new StringBuffer();
1450 for (int i
= 0; i
< fTable
.getColumns().length
; i
++) {
1452 defaultMessage
.append(", "); //$NON-NLS-1$
1454 defaultMessage
.append(tableItem
.getText(i
));
1456 InputDialog dialog
= new InputDialog(PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getShell(),
1457 Messages
.TmfEventsTable_AddBookmarkDialogTitle
, Messages
.TmfEventsTable_AddBookmarkDialogText
, defaultMessage
.toString(), null);
1458 if (dialog
.open() == Dialog
.OK
) {
1459 String message
= dialog
.getValue();
1461 IMarker bookmark
= resource
.createMarker(IMarker
.BOOKMARK
);
1462 if (bookmark
.exists()) {
1463 bookmark
.setAttribute(IMarker
.MESSAGE
, message
.toString());
1464 long rank
= (Long
) tableItem
.getData(Key
.RANK
);
1465 int location
= (int) rank
;
1466 bookmark
.setAttribute(IMarker
.LOCATION
, (Integer
) location
);
1467 fBookmarksMap
.put(rank
, bookmark
.getId());
1470 } catch (CoreException e
) {
1471 e
.printStackTrace();
1479 public void removeBookmark(IMarker bookmark
) {
1480 for (Entry
<Long
, Long
> entry
: fBookmarksMap
.entrySet()) {
1481 if (entry
.getValue().equals(bookmark
.getId())) {
1482 fBookmarksMap
.remove(entry
.getKey());
1489 private void toggleBookmark(long rank
) {
1490 if (fBookmarksResource
== null) {
1493 if (fBookmarksMap
.containsKey(rank
)) {
1494 Long markerId
= fBookmarksMap
.remove(rank
);
1497 IMarker bookmark
= fBookmarksResource
.findMarker(markerId
);
1498 if (bookmark
!= null) {
1501 } catch (CoreException e
) {
1502 e
.printStackTrace();
1505 addBookmark(fBookmarksResource
);
1509 public void refreshBookmarks(IResource resource
) {
1510 fBookmarksResource
= resource
;
1512 fBookmarksMap
.clear();
1513 for (IMarker bookmark
: resource
.findMarkers(IMarker
.BOOKMARK
, false, IResource
.DEPTH_ZERO
)) {
1514 int location
= bookmark
.getAttribute(IMarker
.LOCATION
, -1);
1515 if (location
!= -1) {
1516 long rank
= (long) location
;
1517 fBookmarksMap
.put(rank
, bookmark
.getId());
1521 } catch (CoreException e
) {
1522 e
.printStackTrace();
1527 public void gotoMarker(IMarker marker
) {
1528 int rank
= marker
.getAttribute(IMarker
.LOCATION
, -1);
1530 int index
= (int) rank
;
1531 if (fTable
.getData(Key
.FILTER_OBJ
) != null) {
1532 index
= getFilteredEventIndex(rank
) + 1; //+1 for top filter status row
1533 } else if (rank
>= fTable
.getItemCount()) {
1534 fPendingGotoRank
= rank
;
1536 fTable
.setSelection(index
+ 1); // +1 for header row
1540 // ------------------------------------------------------------------------
1542 // ------------------------------------------------------------------------
1545 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
1548 public void colorSettingsChanged(ColorSetting
[] colorSettings
) {
1553 public void addEventsFilterListener(ITmfEventsFilterListener listener
) {
1554 if (!fEventsFilterListeners
.contains(listener
)) {
1555 fEventsFilterListeners
.add(listener
);
1560 public void removeEventsFilterListener(ITmfEventsFilterListener listener
) {
1561 fEventsFilterListeners
.remove(listener
);
1564 // ------------------------------------------------------------------------
1566 // ------------------------------------------------------------------------
1569 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
1570 if ((signal
.getExperiment() != fTrace
) || fTable
.isDisposed()) return;
1571 // Perform the refresh on the UI thread
1572 Display
.getDefault().asyncExec(new Runnable() {
1575 if (!fTable
.isDisposed() && fTrace
!= null) {
1576 if (fTable
.getData(Key
.FILTER_OBJ
) == null) {
1577 fTable
.setItemCount((int) fTrace
.getNbEvents() + 1); // +1 for header row
1579 if (fPendingGotoRank
!= -1 && fPendingGotoRank
+ 1 < fTable
.getItemCount()) { // +1 for header row
1580 fTable
.setSelection((int) fPendingGotoRank
+ 1); // +1 for header row
1581 fPendingGotoRank
= -1;
1584 if (!fRawViewer
.isDisposed() && fTrace
!= null) {
1585 fRawViewer
.refreshEventCount();
1592 public void traceUpdated(TmfTraceUpdatedSignal signal
) {
1593 if ((signal
.getTrace() != fTrace
) || fTable
.isDisposed()) return;
1594 // Perform the refresh on the UI thread
1595 Display
.getDefault().asyncExec(new Runnable() {
1598 if (!fTable
.isDisposed() && fTrace
!= null) {
1599 if (fTable
.getData(Key
.FILTER_OBJ
) == null) {
1600 fTable
.setItemCount((int) fTrace
.getNbEvents() + 1); // +1 for header row
1601 if (fPendingGotoRank
!= -1 && fPendingGotoRank
+ 1 < fTable
.getItemCount()) { // +1 for header row
1602 fTable
.setSelection((int) fPendingGotoRank
+ 1); // +1 for header row
1603 fPendingGotoRank
= -1;
1607 if (!fRawViewer
.isDisposed() && fTrace
!= null) {
1608 fRawViewer
.refreshEventCount();
1614 // private boolean fRefreshPending = false;
1615 // @TmfSignalHandler
1616 // public synchronized void rangeSynched(TmfRangeSynchSignal signal) {
1617 // if (!fRefreshPending && !fTable.isDisposed()) {
1618 // // Perform the refresh on the UI thread
1619 // fRefreshPending = true;
1620 // Display.getDefault().asyncExec(new Runnable() {
1622 // public void run() {
1623 // fRefreshPending = false;
1624 // if (!fTable.isDisposed() && fTrace != null) {
1625 // fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1632 @SuppressWarnings("unchecked")
1634 public void currentTimeUpdated(final TmfTimeSynchSignal signal
) {
1635 if ((signal
.getSource() != fTable
) && (fTrace
!= null) && (!fTable
.isDisposed())) {
1637 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
1638 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
1639 // the method fTrace.getRank() from interfering and delaying ongoing requests.
1640 final TmfDataRequest
<TmfEvent
> subRequest
= new TmfDataRequest
<TmfEvent
>(TmfEvent
.class, 0, 1, ExecutionType
.FOREGROUND
) {
1643 public void handleData(TmfEvent event
) {
1644 super.handleData(event
);
1648 public void handleCompleted() {
1650 // Verify if event is within the trace range
1651 final TmfTimestamp timestamp
[] = new TmfTimestamp
[1];
1652 timestamp
[0] = signal
.getCurrentTime();
1653 if (timestamp
[0].compareTo(fTrace
.getStartTime(), true) == -1) {
1654 timestamp
[0] = fTrace
.getStartTime();
1656 if (timestamp
[0].compareTo(fTrace
.getEndTime(), true) == 1) {
1657 timestamp
[0] = fTrace
.getEndTime();
1660 // Get the rank for the event selection in the table
1661 final long rank
= fTrace
.getRank(timestamp
[0]);
1662 fSelectedRank
= rank
;
1664 fTable
.getDisplay().asyncExec(new Runnable() {
1667 // Return if table is disposed
1668 if (fTable
.isDisposed()) return;
1670 int index
= (int) rank
;
1671 if (fTable
.isDisposed()) return;
1672 if (fTable
.getData(Key
.FILTER_OBJ
) != null) {
1673 index
= getFilteredEventIndex(rank
) + 1; //+1 for top filter status row
1675 fTable
.setSelection(index
+ 1); // +1 for header row
1676 fRawViewer
.selectAndReveal(rank
);
1679 super.handleCompleted();
1683 ((ITmfDataProvider
<TmfEvent
>) fTrace
).sendRequest(subRequest
);