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