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 *******************************************************************************/
15 package org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.events
;
17 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
18 import org
.eclipse
.core
.runtime
.IStatus
;
19 import org
.eclipse
.core
.runtime
.Status
;
20 import org
.eclipse
.core
.runtime
.jobs
.Job
;
21 import org
.eclipse
.linuxtools
.tmf
.component
.ITmfDataProvider
;
22 import org
.eclipse
.linuxtools
.tmf
.component
.TmfComponent
;
23 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
24 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
25 import org
.eclipse
.linuxtools
.tmf
.experiment
.TmfExperiment
;
26 import org
.eclipse
.linuxtools
.tmf
.request
.ITmfDataRequest
.ExecutionType
;
27 import org
.eclipse
.linuxtools
.tmf
.request
.TmfDataRequest
;
28 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfExperimentUpdatedSignal
;
29 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfRangeSynchSignal
;
30 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalHandler
;
31 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfTimeSynchSignal
;
32 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfTraceUpdatedSignal
;
33 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfTrace
;
34 import org
.eclipse
.linuxtools
.tmf
.ui
.TmfUiPlugin
;
35 import org
.eclipse
.linuxtools
.tmf
.ui
.internal
.Messages
;
36 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.ColumnData
;
37 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.TmfVirtualTable
;
38 import org
.eclipse
.swt
.SWT
;
39 import org
.eclipse
.swt
.events
.SelectionAdapter
;
40 import org
.eclipse
.swt
.events
.SelectionEvent
;
41 import org
.eclipse
.swt
.layout
.GridData
;
42 import org
.eclipse
.swt
.widgets
.Composite
;
43 import org
.eclipse
.swt
.widgets
.Event
;
44 import org
.eclipse
.swt
.widgets
.Listener
;
45 import org
.eclipse
.swt
.widgets
.TableColumn
;
46 import org
.eclipse
.swt
.widgets
.TableItem
;
49 * <b><u>TmfEventsTable</u></b>
51 public class TmfEventsTable
extends TmfComponent
{
53 // ------------------------------------------------------------------------
55 // ------------------------------------------------------------------------
57 protected TmfVirtualTable fTable
;
58 protected ITmfTrace fTrace
;
59 protected boolean fPackDone
= false;
62 static private final String
[] COLUMN_NAMES
= new String
[] {
63 Messages
.TmfEventsTable_TimestampColumnHeader
,
64 Messages
.TmfEventsTable_SourceColumnHeader
,
65 Messages
.TmfEventsTable_TypeColumnHeader
,
66 Messages
.TmfEventsTable_ReferenceColumnHeader
,
67 Messages
.TmfEventsTable_ContentColumnHeader
70 static private ColumnData
[] COLUMN_DATA
= new ColumnData
[] {
71 new ColumnData(COLUMN_NAMES
[0], 100, SWT
.LEFT
),
72 new ColumnData(COLUMN_NAMES
[1], 100, SWT
.LEFT
),
73 new ColumnData(COLUMN_NAMES
[2], 100, SWT
.LEFT
),
74 new ColumnData(COLUMN_NAMES
[3], 100, SWT
.LEFT
),
75 new ColumnData(COLUMN_NAMES
[4], 100, SWT
.LEFT
)
78 // ------------------------------------------------------------------------
80 // ------------------------------------------------------------------------
82 private final int fCacheSize
;
83 private TmfEvent
[] fCache
;
84 private int fCacheStartIndex
= 0;
85 private int fCacheEndIndex
= 0;
87 private boolean fDisposeOnClose
;
89 // ------------------------------------------------------------------------
91 // ------------------------------------------------------------------------
93 public TmfEventsTable(Composite parent
, int cacheSize
) {
94 this(parent
, cacheSize
, COLUMN_DATA
);
97 public TmfEventsTable(Composite parent
, int cacheSize
, ColumnData
[] columnData
) {
98 super("TmfEventsTable"); //$NON-NLS-1$
100 fCacheSize
= cacheSize
;
101 fCache
= new TmfEvent
[fCacheSize
];
103 // Create a virtual table
104 final int style
= SWT
.SINGLE
| SWT
.FULL_SELECTION
| SWT
.BORDER
;
105 fTable
= new TmfVirtualTable(parent
, style
);
107 // Set the table layout
108 GridData layoutData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
109 fTable
.setLayoutData(layoutData
);
111 // Some cosmetic enhancements
112 fTable
.setHeaderVisible(true);
113 fTable
.setLinesVisible(true);
116 setColumnHeaders(columnData
);
118 // Handle the table item requests
119 fTable
.addSelectionListener(new SelectionAdapter() {
121 public void widgetSelected(SelectionEvent e
) {
122 TmfTimestamp ts
= (TmfTimestamp
) fTable
.getSelection()[0].getData();
123 broadcast(new TmfTimeSynchSignal(fTable
, ts
));
127 // Handle the table item requests
128 fTable
.addListener(SWT
.SetData
, new Listener() {
131 public void handleEvent(Event event
) {
133 final TableItem item
= (TableItem
) event
.item
;
134 final int index
= fTable
.indexOf(item
);
136 // If available, return the cached data
137 if ((index
>= fCacheStartIndex
) && (index
< fCacheEndIndex
)) {
138 int i
= index
- fCacheStartIndex
;
139 item
.setText(extractItemFields(fCache
[i
]));
140 item
.setData(new TmfTimestamp(fCache
[i
].getTimestamp()));
144 // Else, fill the cache asynchronously (and off the UI thread)
145 populateCache(index
);
149 fTable
.setItemCount(0);
153 public void dispose() {
155 if (fTrace
!= null && fDisposeOnClose
) {
161 public TmfVirtualTable
getTable() {
165 public void setLayoutData(Object layoutData
) {
166 // FIXME: fComposite.setLayoutData(layoutData);
172 * FIXME: Add support for column selection
174 protected void setColumnHeaders(ColumnData
[] columnData
) {
175 fTable
.setColumnHeaders(columnData
);
178 protected void packColumns() {
179 if (fPackDone
) return;
180 for (TableColumn column
: fTable
.getColumns()) {
181 int headerWidth
= column
.getWidth();
183 if (column
.getWidth() < headerWidth
) {
184 column
.setWidth(headerWidth
);
194 * FIXME: Add support for column selection
196 protected String
[] extractItemFields(TmfEvent event
) {
197 String
[] fields
= new String
[0];
199 fields
= new String
[] {
200 new Long(event
.getTimestamp().getValue()).toString(),
201 event
.getSource().getSourceId().toString(),
202 event
.getType().getTypeId().toString(),
203 event
.getReference().getReference().toString(),
204 event
.getContent().toString()
210 public void setFocus() {
216 * @param disposeOnClose true if the trace should be disposed when the table is disposed
218 public void setTrace(ITmfTrace trace
, boolean disposeOnClose
) {
219 if (fTrace
!= null && fDisposeOnClose
) {
223 fDisposeOnClose
= disposeOnClose
;
225 // Perform the updates on the UI thread
226 fTable
.getDisplay().syncExec(new Runnable() {
229 //fTable.setSelection(0);
231 fCacheStartIndex
= fCacheEndIndex
= 0; // Clear the cache
233 if (!fTable
.isDisposed() && fTrace
!= null) {
234 //int nbEvents = (int) fTrace.getNbEvents();
235 //fTable.setItemCount((nbEvents > 100) ? nbEvents : 100);
236 fTable
.setItemCount((int) fTrace
.getNbEvents());
242 // ------------------------------------------------------------------------
244 // ------------------------------------------------------------------------
247 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
248 if ((signal
.getExperiment() != fTrace
) || fTable
.isDisposed()) return;
249 // Perform the refresh on the UI thread
250 fTable
.getDisplay().asyncExec(new Runnable() {
253 if (!fTable
.isDisposed() && fTrace
!= null) {
254 fTable
.setItemCount((int) fTrace
.getNbEvents());
262 public void traceUpdated(TmfTraceUpdatedSignal signal
) {
263 if ((signal
.getTrace() != fTrace
) || fTable
.isDisposed()) return;
264 // Perform the refresh on the UI thread
265 fTable
.getDisplay().asyncExec(new Runnable() {
268 if (!fTable
.isDisposed() && fTrace
!= null) {
269 //int nbEvents = (int) fTrace.getNbEvents();
270 //fTable.setItemCount((nbEvents > 100) ? nbEvents : 100);
271 fTable
.setItemCount((int) fTrace
.getNbEvents());
277 private boolean fRefreshPending
= false;
279 public synchronized void rangeSynched(TmfRangeSynchSignal signal
) {
280 if (!fRefreshPending
&& !fTable
.isDisposed()) {
281 // Perform the refresh on the UI thread
282 fRefreshPending
= true;
283 fTable
.getDisplay().asyncExec(new Runnable() {
286 fRefreshPending
= false;
287 if (!fTable
.isDisposed() && fTrace
!= null) {
288 fTable
.setItemCount((int) fTrace
.getNbEvents());
296 public void currentTimeUpdated(final TmfTimeSynchSignal signal
) {
297 if ((signal
.getSource() != fTable
) && (fTrace
!= null) && (!fTable
.isDisposed())) {
299 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
300 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
301 // the method fTrace.getRank() from interfering and delaying ongoing requests.
302 final TmfDataRequest
<TmfEvent
> subRequest
= new TmfDataRequest
<TmfEvent
>(TmfEvent
.class, 0, 1, ExecutionType
.FOREGROUND
) {
305 public void handleData(TmfEvent event
) {
306 super.handleData(event
);
310 public void handleCompleted() {
312 // Verify if event is within the trace range
313 final TmfTimestamp timestamp
[] = new TmfTimestamp
[1];
314 timestamp
[0] = signal
.getCurrentTime();
315 if (timestamp
[0].compareTo(fTrace
.getStartTime(), true) == -1) {
316 timestamp
[0] = fTrace
.getStartTime();
318 if (timestamp
[0].compareTo(fTrace
.getEndTime(), true) == 1) {
319 timestamp
[0] = fTrace
.getEndTime();
322 // Get the rank for the event selection in the table
323 final int index
= (int) fTrace
.getRank(timestamp
[0]);
325 fTable
.getDisplay().asyncExec(new Runnable() {
328 // Return if table is disposed
329 if (fTable
.isDisposed()) return;
331 fTable
.setSelection(index
);
333 // If index is in cache, then notify about updated selection.
334 // Otherwise it's done after fetching the relevant events from the trace
335 if ((index
>= fCacheStartIndex
) && (index
< fCacheEndIndex
)) {
336 // Use the timestamp in signal to broadcast to avoid moving the selection
337 // at the source of the signal
338 fTable
.notifyUpdatedSelection(timestamp
[0]);
341 // The timestamp might not correspond to an actual event
342 // and the selection will point to the next experiment event.
343 // But we would like to display both the event before and
344 // after the selected timestamp.
345 // This works fine by default except when the selected event
346 // is the top displayed event. The following ensures that we
347 // always see both events.
348 if ((index
> 0) && (index
== fTable
.getTopIndex())) {
349 fTable
.setTopIndex(index
- 1);
353 super.handleCompleted();
357 @SuppressWarnings("unchecked")
358 TmfExperiment
<TmfEvent
> experiment
= (TmfExperiment
<TmfEvent
>)TmfExperiment
.getCurrentExperiment();
359 if (experiment
!= null) {
360 experiment
.sendRequest(subRequest
);
365 // ------------------------------------------------------------------------
366 // Event cache population
367 // ------------------------------------------------------------------------
369 // The event fetching job
371 private synchronized void populateCache(final int index
) {
373 /* Check if the current job will fetch the requested event:
374 * 1. The job must exist
375 * 2. It must be running (i.e. not completed)
376 * 3. The requested index must be within the cache range
378 * If the job meets these conditions, we simply exit.
379 * Otherwise, we create a new job but we might have to cancel
380 * an existing job for an obsolete range.
383 if (job
.getState() != Job
.NONE
) {
384 if (index
>= fCacheStartIndex
&& index
< (fCacheStartIndex
+ fCacheSize
)) {
387 // The new index is out of the requested range
388 // Kill the job and start a new one
393 fCacheStartIndex
= index
;
394 fCacheEndIndex
= index
;
396 job
= new Job("Fetching Events") { //$NON-NLS-1$
398 @SuppressWarnings("unchecked")
399 protected IStatus
run(final IProgressMonitor monitor
) {
401 TmfDataRequest
<TmfEvent
> request
= new TmfDataRequest
<TmfEvent
>(TmfEvent
.class, index
, fCacheSize
) {
402 private int count
= 0;
404 public void handleData(TmfEvent event
) {
405 // If the job is canceled, cancel the request so waitForCompletion() will unlock
406 if (monitor
.isCanceled()) {
410 super.handleData(event
);
412 fCache
[count
++] = event
.clone();
413 fCacheEndIndex
++; // TODO: Need to protect this??
418 ((ITmfDataProvider
<TmfEvent
>) fTrace
).sendRequest(request
);
420 request
.waitForCompletion();
421 } catch (InterruptedException e
) {
425 // Event cache is now updated. Perform update on the UI thread
426 if (!fTable
.isDisposed() && !monitor
.isCanceled()) {
427 fTable
.getDisplay().asyncExec(new Runnable() {
430 if (!fTable
.isDisposed()) {
432 fTable
.notifyUpdatedSelection();
438 // Flag the UI thread that the cache is ready
439 if (monitor
.isCanceled()) {
440 return new Status(IStatus
.CANCEL
, TmfUiPlugin
.PLUGIN_ID
, "Canceled"); //$NON-NLS-1$
443 return new Status(IStatus
.OK
, TmfUiPlugin
.PLUGIN_ID
, "Completed"); //$NON-NLS-1$
447 job
.setPriority(Job
.SHORT
);