Fix for Bug337914
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
CommitLineData
abfad0aa
FC
1/*******************************************************************************\r
2 * Copyright (c) 2010 Ericsson\r
3 * \r
4 * All rights reserved. This program and the accompanying materials are\r
5 * made available under the terms of the Eclipse Public License v1.0 which\r
6 * accompanies this distribution, and is available at\r
7 * http://www.eclipse.org/legal/epl-v10.html\r
8 * \r
9 * Contributors:\r
10 * Francois Chouinard - Initial API and implementation\r
11 * Patrick Tasse - Factored out from events view\r
9ccc6d01 12 * Francois Chouinard - Replaced Table by TmfVirtualTable\r
abfad0aa
FC
13 *******************************************************************************/\r
14\r
15package org.eclipse.linuxtools.tmf.ui.viewers.events;\r
16\r
bbb3457d
FC
17import org.eclipse.core.runtime.IProgressMonitor;\r
18import org.eclipse.core.runtime.IStatus;\r
19import org.eclipse.core.runtime.Status;\r
20import org.eclipse.core.runtime.jobs.Job;\r
abfad0aa
FC
21import org.eclipse.linuxtools.tmf.component.ITmfDataProvider;\r
22import org.eclipse.linuxtools.tmf.component.TmfComponent;\r
23import org.eclipse.linuxtools.tmf.event.TmfEvent;\r
24import org.eclipse.linuxtools.tmf.event.TmfTimestamp;\r
c1c69938
FC
25import org.eclipse.linuxtools.tmf.experiment.TmfExperiment;\r
26import org.eclipse.linuxtools.tmf.request.ITmfDataRequest.ExecutionType;\r
abfad0aa
FC
27import org.eclipse.linuxtools.tmf.request.TmfDataRequest;\r
28import org.eclipse.linuxtools.tmf.signal.TmfExperimentUpdatedSignal;\r
29import org.eclipse.linuxtools.tmf.signal.TmfRangeSynchSignal;\r
30import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler;\r
31import org.eclipse.linuxtools.tmf.signal.TmfTimeSynchSignal;\r
32import org.eclipse.linuxtools.tmf.signal.TmfTraceUpdatedSignal;\r
33import org.eclipse.linuxtools.tmf.trace.ITmfTrace;\r
bbb3457d 34import org.eclipse.linuxtools.tmf.ui.TmfUiPlugin;\r
9ccc6d01
FC
35import org.eclipse.linuxtools.tmf.ui.widgets.ColumnData;\r
36import org.eclipse.linuxtools.tmf.ui.widgets.TmfVirtualTable;\r
abfad0aa
FC
37import org.eclipse.swt.SWT;\r
38import org.eclipse.swt.events.SelectionAdapter;\r
39import org.eclipse.swt.events.SelectionEvent;\r
40import org.eclipse.swt.layout.GridData;\r
41import org.eclipse.swt.widgets.Composite;\r
42import org.eclipse.swt.widgets.Event;\r
43import org.eclipse.swt.widgets.Listener;\r
abfad0aa
FC
44import org.eclipse.swt.widgets.TableColumn;\r
45import org.eclipse.swt.widgets.TableItem;\r
46\r
47/**\r
48 * <b><u>TmfEventsTable</u></b>\r
49 */\r
50public class TmfEventsTable extends TmfComponent {\r
51\r
abfad0aa
FC
52 // ------------------------------------------------------------------------\r
53 // Table data\r
54 // ------------------------------------------------------------------------\r
55\r
9ccc6d01 56 protected TmfVirtualTable fTable;\r
abfad0aa
FC
57 protected ITmfTrace fTrace;\r
58 protected boolean fPackDone = false;\r
59\r
60 // Table column names\r
529ee6a9 61 static private final String[] COLUMN_NAMES = new String[] {\r
b9763f53
FC
62 Messages.TmfEventsTable_TimestampColumnHeader,\r
63 Messages.TmfEventsTable_SourceColumnHeader,\r
64 Messages.TmfEventsTable_TypeColumnHeader,\r
65 Messages.TmfEventsTable_ReferenceColumnHeader,\r
66 Messages.TmfEventsTable_ContentColumnHeader\r
abfad0aa
FC
67 };\r
68\r
529ee6a9
FC
69 static private ColumnData[] COLUMN_DATA = new ColumnData[] {\r
70 new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),\r
71 new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT),\r
72 new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),\r
73 new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT),\r
74 new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT)\r
abfad0aa
FC
75 };\r
76\r
77 // ------------------------------------------------------------------------\r
78 // Event cache\r
79 // ------------------------------------------------------------------------\r
80\r
9ccc6d01
FC
81 private final int fCacheSize;\r
82 private TmfEvent[] fCache;\r
83 private int fCacheStartIndex = 0;\r
84 private int fCacheEndIndex = 0;\r
85\r
529ee6a9 86 private boolean fDisposeOnClose;\r
abfad0aa
FC
87\r
88 // ------------------------------------------------------------------------\r
89 // Constructor\r
90 // ------------------------------------------------------------------------\r
91\r
92 public TmfEventsTable(Composite parent, int cacheSize) {\r
9ccc6d01
FC
93 this(parent, cacheSize, COLUMN_DATA);\r
94 }\r
95\r
96 public TmfEventsTable(Composite parent, int cacheSize, ColumnData[] columnData) {\r
3b38ea61 97 super("TmfEventsTable"); //$NON-NLS-1$\r
abfad0aa
FC
98 \r
99 fCacheSize = cacheSize;\r
9ccc6d01 100 fCache = new TmfEvent[fCacheSize];\r
abfad0aa
FC
101 \r
102 // Create a virtual table\r
9ccc6d01
FC
103 final int style = SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER;\r
104 fTable = new TmfVirtualTable(parent, style);\r
abfad0aa
FC
105\r
106 // Set the table layout\r
107 GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);\r
108 fTable.setLayoutData(layoutData);\r
109\r
110 // Some cosmetic enhancements\r
111 fTable.setHeaderVisible(true);\r
112 fTable.setLinesVisible(true);\r
113\r
114 // Set the columns\r
9ccc6d01 115 setColumnHeaders(columnData);\r
abfad0aa
FC
116\r
117 // Handle the table item requests \r
118 fTable.addSelectionListener(new SelectionAdapter() {\r
abfad0aa
FC
119 @Override\r
120 public void widgetSelected(SelectionEvent e) {\r
121 TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData();\r
122 broadcast(new TmfTimeSynchSignal(fTable, ts));\r
123 }\r
124 });\r
125\r
126 // Handle the table item requests \r
127 fTable.addListener(SWT.SetData, new Listener() {\r
128\r
d4011df2 129 @Override\r
abfad0aa
FC
130 public void handleEvent(Event event) {\r
131\r
132 final TableItem item = (TableItem) event.item;\r
133 final int index = fTable.indexOf(item);\r
134\r
bbb3457d 135 // If available, return the cached data \r
9ccc6d01
FC
136 if ((index >= fCacheStartIndex) && (index < fCacheEndIndex)) {\r
137 int i = index - fCacheStartIndex;\r
138 item.setText(extractItemFields(fCache[i]));\r
139 item.setData(new TmfTimestamp(fCache[i].getTimestamp()));\r
abfad0aa
FC
140 return;\r
141 }\r
142\r
bbb3457d
FC
143 // Else, fill the cache asynchronously (and off the UI thread)\r
144 populateCache(index);\r
abfad0aa
FC
145 }\r
146 });\r
147\r
148 fTable.setItemCount(0);\r
149 }\r
150\r
9ccc6d01
FC
151 @Override\r
152 public void dispose() {\r
abfad0aa 153 fTable.dispose();\r
529ee6a9 154 if (fTrace != null && fDisposeOnClose) {\r
abfad0aa
FC
155 fTrace.dispose();\r
156 }\r
157 super.dispose();\r
158 }\r
159\r
9ccc6d01 160 public TmfVirtualTable getTable() {\r
abfad0aa
FC
161 return fTable;\r
162 }\r
163 \r
164 /**\r
165 * @param table\r
166 * \r
167 * FIXME: Add support for column selection\r
168 */\r
9ccc6d01
FC
169 protected void setColumnHeaders(ColumnData[] columnData) {\r
170 fTable.setColumnHeaders(columnData);\r
abfad0aa
FC
171 }\r
172\r
9ccc6d01 173 protected void packColumns() {\r
abfad0aa
FC
174 if (fPackDone) return;\r
175 for (TableColumn column : fTable.getColumns()) {\r
176 int headerWidth = column.getWidth();\r
177 column.pack();\r
178 if (column.getWidth() < headerWidth) {\r
179 column.setWidth(headerWidth);\r
180 }\r
181 }\r
182 fPackDone = true;\r
183 }\r
184 \r
185 /**\r
186 * @param event\r
187 * @return\r
188 * \r
189 * FIXME: Add support for column selection\r
190 */\r
78c0de16 191 protected String[] extractItemFields(TmfEvent event) {\r
abfad0aa
FC
192 String[] fields = new String[0];\r
193 if (event != null) {\r
194 fields = new String[] {\r
195 new Long(event.getTimestamp().getValue()).toString(), \r
196 event.getSource().getSourceId().toString(),\r
197 event.getType().getTypeId().toString(),\r
198 event.getReference().getReference().toString(),\r
199 event.getContent().toString()\r
200 };\r
201 }\r
202 return fields;\r
203 }\r
204\r
205 public void setFocus() {\r
206 fTable.setFocus();\r
207 }\r
208\r
529ee6a9
FC
209 /**\r
210 * @param trace\r
211 * @param disposeOnClose true if the trace should be disposed when the table is disposed\r
212 */\r
213 public void setTrace(ITmfTrace trace, boolean disposeOnClose) {\r
214 if (fTrace != null && fDisposeOnClose) {\r
215 fTrace.dispose();\r
216 }\r
abfad0aa 217 fTrace = trace;\r
529ee6a9 218 fDisposeOnClose = disposeOnClose;\r
abfad0aa
FC
219 \r
220 // Perform the updates on the UI thread\r
221 fTable.getDisplay().syncExec(new Runnable() {\r
d4011df2
FC
222 @Override\r
223 public void run() {\r
529ee6a9 224 //fTable.setSelection(0);\r
abfad0aa 225 fTable.removeAll();\r
9ccc6d01 226 fCacheStartIndex = fCacheEndIndex = 0; // Clear the cache\r
abfad0aa
FC
227 \r
228 if (!fTable.isDisposed() && fTrace != null) {\r
229 //int nbEvents = (int) fTrace.getNbEvents();\r
230 //fTable.setItemCount((nbEvents > 100) ? nbEvents : 100);\r
231 fTable.setItemCount((int) fTrace.getNbEvents());\r
232 }\r
233 }\r
234 });\r
abfad0aa
FC
235 }\r
236\r
237 // ------------------------------------------------------------------------\r
238 // Signal handlers\r
239 // ------------------------------------------------------------------------\r
240 \r
241 @TmfSignalHandler\r
242 public void experimentUpdated(TmfExperimentUpdatedSignal signal) {\r
db1ea19b 243 if ((signal.getExperiment() != fTrace) || fTable.isDisposed()) return;\r
abfad0aa
FC
244 // Perform the refresh on the UI thread\r
245 fTable.getDisplay().asyncExec(new Runnable() {\r
d4011df2
FC
246 @Override\r
247 public void run() {\r
abfad0aa
FC
248 if (!fTable.isDisposed() && fTrace != null) {\r
249 fTable.setItemCount((int) fTrace.getNbEvents());\r
9ccc6d01 250 fTable.refresh();\r
abfad0aa
FC
251 }\r
252 }\r
253 });\r
254 }\r
255 \r
256 @TmfSignalHandler\r
257 public void traceUpdated(TmfTraceUpdatedSignal signal) {\r
db1ea19b 258 if ((signal.getTrace() != fTrace ) || fTable.isDisposed()) return;\r
abfad0aa
FC
259 // Perform the refresh on the UI thread\r
260 fTable.getDisplay().asyncExec(new Runnable() {\r
d4011df2
FC
261 @Override\r
262 public void run() {\r
abfad0aa
FC
263 if (!fTable.isDisposed() && fTrace != null) {\r
264 //int nbEvents = (int) fTrace.getNbEvents();\r
265 //fTable.setItemCount((nbEvents > 100) ? nbEvents : 100);\r
266 fTable.setItemCount((int) fTrace.getNbEvents());\r
267 }\r
268 }\r
269 });\r
270 }\r
271\r
272 private boolean fRefreshPending = false;\r
273 @TmfSignalHandler\r
274 public synchronized void rangeSynched(TmfRangeSynchSignal signal) {\r
db1ea19b 275 if (!fRefreshPending && !fTable.isDisposed()) {\r
abfad0aa
FC
276 // Perform the refresh on the UI thread\r
277 fRefreshPending = true;\r
278 fTable.getDisplay().asyncExec(new Runnable() {\r
d4011df2
FC
279 @Override\r
280 public void run() {\r
abfad0aa
FC
281 fRefreshPending = false;\r
282 if (!fTable.isDisposed() && fTrace != null) {\r
283 fTable.setItemCount((int) fTrace.getNbEvents());\r
284 }\r
285 }\r
286 });\r
287 }\r
288 }\r
289 \r
290 @TmfSignalHandler\r
529ee6a9 291 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {\r
c1c69938
FC
292 if ((signal.getSource() != fTable) && (fTrace != null) && (!fTable.isDisposed())) {\r
293\r
294 // Create a request for one event that will be queued after other ongoing requests. When this request is completed \r
295 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents \r
296 // the method fTrace.getRank() from interfering and delaying ongoing requests.\r
297 final TmfDataRequest<TmfEvent> subRequest = new TmfDataRequest<TmfEvent>(TmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {\r
298\r
299 @Override\r
300 public void handleData(TmfEvent event) {\r
301 super.handleData(event);\r
302 }\r
303\r
304 @Override\r
305 public void handleCompleted() {\r
89dcf304
BH
306\r
307 // Verify if event is within the trace range\r
308 final TmfTimestamp timestamp[] = new TmfTimestamp[1];\r
309 timestamp[0] = signal.getCurrentTime();\r
310 if (timestamp[0].compareTo(fTrace.getStartTime(), true) == -1) {\r
311 timestamp[0] = fTrace.getStartTime();\r
312 }\r
313 if (timestamp[0].compareTo(fTrace.getEndTime(), true) == 1) {\r
314 timestamp[0] = fTrace.getEndTime();\r
315 }\r
316\r
c1c69938 317 // Get the rank for the event selection in the table\r
89dcf304 318 final int index = (int) fTrace.getRank(timestamp[0]);\r
c1c69938
FC
319\r
320 fTable.getDisplay().asyncExec(new Runnable() {\r
321 @Override\r
322 public void run() {\r
323 // Return if table is disposed\r
324 if (fTable.isDisposed()) return;\r
325\r
326 fTable.setSelection(index);\r
89dcf304
BH
327\r
328 // If index is in cache, then notify about updated selection. \r
329 // Otherwise it's done after fetching the relevant events from the trace\r
330 if ((index >= fCacheStartIndex) && (index < fCacheEndIndex)) {\r
331 // Use the timestamp in signal to broadcast to avoid moving the selection\r
332 // at the source of the signal\r
333 fTable.notifyUpdatedSelection(timestamp[0]);\r
334 }\r
335\r
c1c69938
FC
336 // The timestamp might not correspond to an actual event\r
337 // and the selection will point to the next experiment event.\r
338 // But we would like to display both the event before and\r
339 // after the selected timestamp.\r
340 // This works fine by default except when the selected event\r
341 // is the top displayed event. The following ensures that we\r
342 // always see both events.\r
343 if ((index > 0) && (index == fTable.getTopIndex())) {\r
344 fTable.setTopIndex(index - 1);\r
345 }\r
346 }\r
347 });\r
348 super.handleCompleted();\r
349 }\r
350 };\r
351\r
352 @SuppressWarnings("unchecked")\r
353 TmfExperiment<TmfEvent> experiment = (TmfExperiment<TmfEvent>)TmfExperiment.getCurrentExperiment();\r
354 if (experiment != null) {\r
355 experiment.sendRequest(subRequest);\r
356 }\r
357 }\r
358 }\r
359\r
bbb3457d
FC
360 // ------------------------------------------------------------------------\r
361 // Event cache population\r
362 // ------------------------------------------------------------------------\r
363 \r
364 // The event fetching job\r
365 private Job job;\r
bbb3457d
FC
366 private synchronized void populateCache(final int index) {\r
367\r
368 /* Check if the current job will fetch the requested event:\r
369 * 1. The job must exist\r
370 * 2. It must be running (i.e. not completed)\r
371 * 3. The requested index must be within the cache range\r
372 * \r
373 * If the job meets these conditions, we simply exit.\r
374 * Otherwise, we create a new job but we might have to cancel\r
375 * an existing job for an obsolete range.\r
376 */\r
377 if (job != null) {\r
378 if (job.getState() != Job.NONE) {\r
379 if (index >= fCacheStartIndex && index < (fCacheStartIndex + fCacheSize)) {\r
380 return;\r
381 }\r
382 // The new index is out of the requested range\r
383 // Kill the job and start a new one\r
384 job.cancel();\r
385 }\r
386 }\r
387 \r
388 fCacheStartIndex = index;\r
389 fCacheEndIndex = index;\r
390\r
391 job = new Job("Fetching Events") { //$NON-NLS-1$\r
392 @Override\r
393 @SuppressWarnings("unchecked")\r
394 protected IStatus run(final IProgressMonitor monitor) {\r
395\r
396 TmfDataRequest<TmfEvent> request = new TmfDataRequest<TmfEvent>(TmfEvent.class, index, fCacheSize) {\r
397 private int count = 0;\r
398 @Override\r
399 public void handleData(TmfEvent event) {\r
400 // If the job is canceled, cancel the request so waitForCompletion() will unlock\r
401 if (monitor.isCanceled()) {\r
402 cancel();\r
403 return;\r
404 }\r
405 super.handleData(event);\r
406 if (event != null) {\r
407 fCache[count++] = event.clone();\r
408 fCacheEndIndex++; // TODO: Need to protect this??\r
409 }\r
410 }\r
411 };\r
412\r
413 ((ITmfDataProvider<TmfEvent>) fTrace).sendRequest(request);\r
414 try {\r
415 request.waitForCompletion();\r
416 } catch (InterruptedException e) {\r
417 e.printStackTrace();\r
418 }\r
419\r
420 // Event cache is now updated. Perform update on the UI thread\r
421 if (!fTable.isDisposed() && !monitor.isCanceled()) {\r
422 fTable.getDisplay().asyncExec(new Runnable() {\r
423 @Override\r
424 public void run() {\r
425 if (!fTable.isDisposed()) {\r
426 fTable.refresh();\r
886b12b1 427 fTable.notifyUpdatedSelection();\r
bbb3457d
FC
428 }\r
429 }\r
430 });\r
431 }\r
432 \r
433 // Flag the UI thread that the cache is ready\r
434 if (monitor.isCanceled()) {\r
435 return new Status(IStatus.CANCEL, TmfUiPlugin.PLUGIN_ID, "Canceled"); //$NON-NLS-1$\r
436 }\r
437 else {\r
438 return new Status(IStatus.OK, TmfUiPlugin.PLUGIN_ID, "Completed"); //$NON-NLS-1$\r
439 }\r
440 }\r
441 };\r
442 job.setPriority(Job.SHORT);\r
443 job.schedule();\r
444 }\r
abfad0aa 445}\r
This page took 0.04867 seconds and 5 git commands to generate.