Fix for Bug327265
[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 *******************************************************************************/
14
15 package org.eclipse.linuxtools.tmf.ui.viewers.events;
16
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;
47
48 /**
49 * <b><u>TmfEventsTable</u></b>
50 */
51 public class TmfEventsTable extends TmfComponent {
52
53 // ------------------------------------------------------------------------
54 // Table data
55 // ------------------------------------------------------------------------
56
57 protected TmfVirtualTable fTable;
58 protected ITmfTrace fTrace;
59 protected boolean fPackDone = false;
60
61 // Table column names
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
68 };
69
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)
76 };
77
78 // ------------------------------------------------------------------------
79 // Event cache
80 // ------------------------------------------------------------------------
81
82 private final int fCacheSize;
83 private TmfEvent[] fCache;
84 private int fCacheStartIndex = 0;
85 private int fCacheEndIndex = 0;
86
87 private boolean fDisposeOnClose;
88
89 // ------------------------------------------------------------------------
90 // Constructor
91 // ------------------------------------------------------------------------
92
93 public TmfEventsTable(Composite parent, int cacheSize) {
94 this(parent, cacheSize, COLUMN_DATA);
95 }
96
97 public TmfEventsTable(Composite parent, int cacheSize, ColumnData[] columnData) {
98 super("TmfEventsTable"); //$NON-NLS-1$
99
100 fCacheSize = cacheSize;
101 fCache = new TmfEvent[fCacheSize];
102
103 // Create a virtual table
104 final int style = SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER;
105 fTable = new TmfVirtualTable(parent, style);
106
107 // Set the table layout
108 GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
109 fTable.setLayoutData(layoutData);
110
111 // Some cosmetic enhancements
112 fTable.setHeaderVisible(true);
113 fTable.setLinesVisible(true);
114
115 // Set the columns
116 setColumnHeaders(columnData);
117
118 // Handle the table item requests
119 fTable.addSelectionListener(new SelectionAdapter() {
120 @Override
121 public void widgetSelected(SelectionEvent e) {
122 TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData();
123 broadcast(new TmfTimeSynchSignal(fTable, ts));
124 }
125 });
126
127 // Handle the table item requests
128 fTable.addListener(SWT.SetData, new Listener() {
129
130 @Override
131 public void handleEvent(Event event) {
132
133 final TableItem item = (TableItem) event.item;
134 final int index = fTable.indexOf(item);
135
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()));
141 return;
142 }
143
144 // Else, fill the cache asynchronously (and off the UI thread)
145 populateCache(index);
146 }
147 });
148
149 fTable.setItemCount(0);
150 }
151
152 @Override
153 public void dispose() {
154 fTable.dispose();
155 if (fTrace != null && fDisposeOnClose) {
156 fTrace.dispose();
157 }
158 super.dispose();
159 }
160
161 public TmfVirtualTable getTable() {
162 return fTable;
163 }
164
165 public void setLayoutData(Object layoutData) {
166 // FIXME: fComposite.setLayoutData(layoutData);
167 }
168
169 /**
170 * @param table
171 *
172 * FIXME: Add support for column selection
173 */
174 protected void setColumnHeaders(ColumnData[] columnData) {
175 fTable.setColumnHeaders(columnData);
176 }
177
178 protected void packColumns() {
179 if (fPackDone) return;
180 for (TableColumn column : fTable.getColumns()) {
181 int headerWidth = column.getWidth();
182 column.pack();
183 if (column.getWidth() < headerWidth) {
184 column.setWidth(headerWidth);
185 }
186 }
187 fPackDone = true;
188 }
189
190 /**
191 * @param event
192 * @return
193 *
194 * FIXME: Add support for column selection
195 */
196 protected String[] extractItemFields(TmfEvent event) {
197 String[] fields = new String[0];
198 if (event != null) {
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()
205 };
206 }
207 return fields;
208 }
209
210 public void setFocus() {
211 fTable.setFocus();
212 }
213
214 /**
215 * @param trace
216 * @param disposeOnClose true if the trace should be disposed when the table is disposed
217 */
218 public void setTrace(ITmfTrace trace, boolean disposeOnClose) {
219 if (fTrace != null && fDisposeOnClose) {
220 fTrace.dispose();
221 }
222 fTrace = trace;
223 fDisposeOnClose = disposeOnClose;
224
225 // Perform the updates on the UI thread
226 fTable.getDisplay().syncExec(new Runnable() {
227 @Override
228 public void run() {
229 //fTable.setSelection(0);
230 fTable.removeAll();
231 fCacheStartIndex = fCacheEndIndex = 0; // Clear the cache
232
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());
237 }
238 }
239 });
240 }
241
242 // ------------------------------------------------------------------------
243 // Signal handlers
244 // ------------------------------------------------------------------------
245
246 @TmfSignalHandler
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() {
251 @Override
252 public void run() {
253 if (!fTable.isDisposed() && fTrace != null) {
254 fTable.setItemCount((int) fTrace.getNbEvents());
255 fTable.refresh();
256 }
257 }
258 });
259 }
260
261 @TmfSignalHandler
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() {
266 @Override
267 public void run() {
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());
272 }
273 }
274 });
275 }
276
277 private boolean fRefreshPending = false;
278 @TmfSignalHandler
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() {
284 @Override
285 public void run() {
286 fRefreshPending = false;
287 if (!fTable.isDisposed() && fTrace != null) {
288 fTable.setItemCount((int) fTrace.getNbEvents());
289 }
290 }
291 });
292 }
293 }
294
295 @TmfSignalHandler
296 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
297 if ((signal.getSource() != fTable) && (fTrace != null) && (!fTable.isDisposed())) {
298
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) {
303
304 @Override
305 public void handleData(TmfEvent event) {
306 super.handleData(event);
307 }
308
309 @Override
310 public void handleCompleted() {
311
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();
317 }
318 if (timestamp[0].compareTo(fTrace.getEndTime(), true) == 1) {
319 timestamp[0] = fTrace.getEndTime();
320 }
321
322 // Get the rank for the event selection in the table
323 final int index = (int) fTrace.getRank(timestamp[0]);
324
325 fTable.getDisplay().asyncExec(new Runnable() {
326 @Override
327 public void run() {
328 // Return if table is disposed
329 if (fTable.isDisposed()) return;
330
331 fTable.setSelection(index);
332
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]);
339 }
340
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);
350 }
351 }
352 });
353 super.handleCompleted();
354 }
355 };
356
357 @SuppressWarnings("unchecked")
358 TmfExperiment<TmfEvent> experiment = (TmfExperiment<TmfEvent>)TmfExperiment.getCurrentExperiment();
359 if (experiment != null) {
360 experiment.sendRequest(subRequest);
361 }
362 }
363 }
364
365 // ------------------------------------------------------------------------
366 // Event cache population
367 // ------------------------------------------------------------------------
368
369 // The event fetching job
370 private Job job;
371 private synchronized void populateCache(final int index) {
372
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
377 *
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.
381 */
382 if (job != null) {
383 if (job.getState() != Job.NONE) {
384 if (index >= fCacheStartIndex && index < (fCacheStartIndex + fCacheSize)) {
385 return;
386 }
387 // The new index is out of the requested range
388 // Kill the job and start a new one
389 job.cancel();
390 }
391 }
392
393 fCacheStartIndex = index;
394 fCacheEndIndex = index;
395
396 job = new Job("Fetching Events") { //$NON-NLS-1$
397 @Override
398 @SuppressWarnings("unchecked")
399 protected IStatus run(final IProgressMonitor monitor) {
400
401 TmfDataRequest<TmfEvent> request = new TmfDataRequest<TmfEvent>(TmfEvent.class, index, fCacheSize) {
402 private int count = 0;
403 @Override
404 public void handleData(TmfEvent event) {
405 // If the job is canceled, cancel the request so waitForCompletion() will unlock
406 if (monitor.isCanceled()) {
407 cancel();
408 return;
409 }
410 super.handleData(event);
411 if (event != null) {
412 fCache[count++] = event.clone();
413 fCacheEndIndex++; // TODO: Need to protect this??
414 }
415 }
416 };
417
418 ((ITmfDataProvider<TmfEvent>) fTrace).sendRequest(request);
419 try {
420 request.waitForCompletion();
421 } catch (InterruptedException e) {
422 e.printStackTrace();
423 }
424
425 // Event cache is now updated. Perform update on the UI thread
426 if (!fTable.isDisposed() && !monitor.isCanceled()) {
427 fTable.getDisplay().asyncExec(new Runnable() {
428 @Override
429 public void run() {
430 if (!fTable.isDisposed()) {
431 fTable.refresh();
432 fTable.notifyUpdatedSelection();
433 }
434 }
435 });
436 }
437
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$
441 }
442 else {
443 return new Status(IStatus.OK, TmfUiPlugin.PLUGIN_ID, "Completed"); //$NON-NLS-1$
444 }
445 }
446 };
447 job.setPriority(Job.SHORT);
448 job.schedule();
449 }
450 }
This page took 0.044061 seconds and 6 git commands to generate.