timing.ui: add Export to TSV to tables and statistics
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / tree / AbstractTmfTreeViewer.java
1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.ui.viewers.tree;
14
15 import java.util.List;
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.jdt.annotation.NonNull;
22 import org.eclipse.jface.viewers.AbstractTreeViewer;
23 import org.eclipse.jface.viewers.IBaseLabelProvider;
24 import org.eclipse.jface.viewers.ILabelProviderListener;
25 import org.eclipse.jface.viewers.ISelectionChangedListener;
26 import org.eclipse.jface.viewers.IStructuredSelection;
27 import org.eclipse.jface.viewers.ITableColorProvider;
28 import org.eclipse.jface.viewers.ITableFontProvider;
29 import org.eclipse.jface.viewers.ITableLabelProvider;
30 import org.eclipse.jface.viewers.ITreeContentProvider;
31 import org.eclipse.jface.viewers.StructuredSelection;
32 import org.eclipse.jface.viewers.TreeViewer;
33 import org.eclipse.jface.viewers.Viewer;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.graphics.Color;
36 import org.eclipse.swt.graphics.Font;
37 import org.eclipse.swt.graphics.Image;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Control;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Listener;
43 import org.eclipse.swt.widgets.Tree;
44 import org.eclipse.swt.widgets.TreeColumn;
45 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
47 import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
48 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
49 import org.eclipse.tracecompass.tmf.ui.viewers.TmfTimeViewer;
50
51 /**
52 * Abstract class for viewers who will display data using a TreeViewer. It
53 * automatically synchronizes with time information of the UI. It also
54 * implements some common functionalities for all tree viewer, such as managing
55 * the column data, content initialization and update. The viewer implementing
56 * this does not have to worry about whether some code runs in the UI thread or
57 * not.
58 *
59 * @author Geneviève Bastien
60 */
61 public abstract class AbstractTmfTreeViewer extends TmfTimeViewer {
62
63 private final TreeViewer fTreeViewer;
64
65 // ------------------------------------------------------------------------
66 // Internal classes
67 // ------------------------------------------------------------------------
68
69 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
70 private class TreeContentProvider implements ITreeContentProvider {
71
72 @Override
73 public void dispose() {
74 }
75
76 @Override
77 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
78 }
79
80 @Override
81 public Object[] getElements(Object inputElement) {
82 if (inputElement instanceof ITmfTreeViewerEntry) {
83 return ((ITmfTreeViewerEntry) inputElement).getChildren().toArray(new ITmfTreeViewerEntry[0]);
84 }
85 return new ITmfTreeViewerEntry[0];
86 }
87
88 @Override
89 public Object[] getChildren(Object parentElement) {
90 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) parentElement;
91 List<? extends ITmfTreeViewerEntry> children = entry.getChildren();
92 return children.toArray(new ITmfTreeViewerEntry[children.size()]);
93 }
94
95 @Override
96 public Object getParent(Object element) {
97 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
98 return entry.getParent();
99 }
100
101 @Override
102 public boolean hasChildren(Object element) {
103 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
104 return entry.hasChildren();
105 }
106
107 }
108
109 /**
110 * Base class to provide the labels for the tree viewer. Views extending
111 * this class typically need to override the getColumnText method if they
112 * have more than one column to display. It also allows to change the font
113 * and colors of the cells.
114 */
115 protected static class TreeLabelProvider implements ITableLabelProvider, ITableFontProvider, ITableColorProvider {
116
117 @Override
118 public void addListener(ILabelProviderListener listener) {
119 }
120
121 @Override
122 public void dispose() {
123 }
124
125 @Override
126 public boolean isLabelProperty(Object element, String property) {
127 return false;
128 }
129
130 @Override
131 public void removeListener(ILabelProviderListener listener) {
132 }
133
134 @Override
135 public Image getColumnImage(Object element, int columnIndex) {
136 return null;
137 }
138
139 @Override
140 public String getColumnText(Object element, int columnIndex) {
141 if ((element instanceof ITmfTreeViewerEntry) && (columnIndex == 0)) {
142 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
143 return entry.getName();
144 }
145 return new String();
146 }
147
148 @Override
149 public Color getForeground(Object element, int columnIndex) {
150 return Display.getCurrent().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
151 }
152
153 @Override
154 public Color getBackground(Object element, int columnIndex) {
155 return Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
156 }
157
158 @Override
159 public Font getFont(Object element, int columnIndex) {
160 return null;
161 }
162
163 }
164
165 // ------------------------------------------------------------------------
166 // Constructors and initialization methods
167 // ------------------------------------------------------------------------
168
169 /**
170 * Constructor
171 *
172 * @param parent
173 * The parent composite that holds this viewer
174 * @param allowMultiSelect
175 * Whether multiple selections are allowed
176 */
177 public AbstractTmfTreeViewer(Composite parent, boolean allowMultiSelect) {
178 super(parent);
179
180 int flags = SWT.FULL_SELECTION | SWT.H_SCROLL;
181 if (allowMultiSelect) {
182 flags |= SWT.MULTI;
183 }
184
185 /* Build the tree viewer part of the view */
186 fTreeViewer = new TreeViewer(parent, flags);
187 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
188 final Tree tree = fTreeViewer.getTree();
189 tree.setHeaderVisible(true);
190 tree.setLinesVisible(true);
191 fTreeViewer.setContentProvider(new TreeContentProvider());
192 fTreeViewer.setLabelProvider(new TreeLabelProvider());
193 List<TmfTreeColumnData> columns = getColumnDataProvider().getColumnData();
194 this.setTreeColumns(columns);
195
196 }
197
198 /**
199 * Get the column data provider that will contain the list of columns to be
200 * part of this viewer. It is called once during the constructor.
201 *
202 * @return The tree column data provider for this viewer.
203 */
204 protected abstract ITmfTreeColumnDataProvider getColumnDataProvider();
205
206 /**
207 * Sets the tree columns for this tree viewer
208 *
209 * @param columns
210 * The tree column data
211 */
212 public void setTreeColumns(final List<TmfTreeColumnData> columns) {
213 boolean hasPercentProvider = false;
214 for (final TmfTreeColumnData columnData : columns) {
215 columnData.createColumn(fTreeViewer);
216 hasPercentProvider |= (columnData.getPercentageProvider() != null);
217 }
218
219 if (hasPercentProvider) {
220 /*
221 * Handler that will draw bar charts in the cell using a percentage
222 * value.
223 */
224 fTreeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
225 @Override
226 public void handleEvent(Event event) {
227 if (columns.get(event.index).getPercentageProvider() != null) {
228
229 double percentage = columns.get(event.index).getPercentageProvider().getPercentage(event.item.getData());
230 if (percentage == 0) { // No bar to draw
231 return;
232 }
233
234 if ((event.detail & SWT.SELECTED) > 0) {
235 /*
236 * The item is selected. Draw our own background to
237 * avoid overwriting the bar.
238 */
239 event.gc.fillRectangle(event.x, event.y, event.width, event.height);
240 event.detail &= ~SWT.SELECTED;
241 }
242
243 int barWidth = (int) ((fTreeViewer.getTree().getColumn(event.index).getWidth() - 8) * percentage);
244 int oldAlpha = event.gc.getAlpha();
245 Color oldForeground = event.gc.getForeground();
246 Color oldBackground = event.gc.getBackground();
247 /*
248 * Draws a transparent gradient rectangle from the color
249 * of foreground and background.
250 */
251 event.gc.setAlpha(64);
252 event.gc.setForeground(event.item.getDisplay().getSystemColor(SWT.COLOR_BLUE));
253 event.gc.setBackground(event.item.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
254 event.gc.fillGradientRectangle(event.x, event.y, barWidth, event.height, true);
255 event.gc.drawRectangle(event.x, event.y, barWidth, event.height);
256 /* Restores old values */
257 event.gc.setForeground(oldForeground);
258 event.gc.setBackground(oldBackground);
259 event.gc.setAlpha(oldAlpha);
260 event.detail &= ~SWT.BACKGROUND;
261 }
262 }
263 });
264 }
265 }
266
267 /**
268 * Set the label provider that will fill the columns of the tree viewer
269 *
270 * @param labelProvider
271 * The label provider to fill the columns
272 */
273 protected void setLabelProvider(IBaseLabelProvider labelProvider) {
274 fTreeViewer.setLabelProvider(labelProvider);
275 }
276
277 /**
278 * Get the tree viewer object
279 *
280 * @return The tree viewer object displayed by this viewer
281 * @since 2.2
282 */
283 public TreeViewer getTreeViewer() {
284 return fTreeViewer;
285 }
286
287 // ------------------------------------------------------------------------
288 // ITmfViewer
289 // ------------------------------------------------------------------------
290
291 @Override
292 public Control getControl() {
293 return fTreeViewer.getControl();
294 }
295
296 @Override
297 public void refresh() {
298 Tree tree = fTreeViewer.getTree();
299 tree.setRedraw(false);
300 fTreeViewer.refresh();
301 fTreeViewer.expandAll();
302 tree.setRedraw(true);
303 }
304
305 @Override
306 public void loadTrace(ITmfTrace trace) {
307 super.loadTrace(trace);
308 Thread thread = new Thread() {
309 @Override
310 public void run() {
311 initializeDataSource();
312 Display.getDefault().asyncExec(new Runnable() {
313 @Override
314 public void run() {
315 clearContent();
316 updateContent(getWindowStartTime(), getWindowEndTime(), false);
317 }
318 });
319 }
320 };
321 thread.start();
322 }
323
324 // ------------------------------------------------------------------------
325 // Operations
326 // ------------------------------------------------------------------------
327
328 /**
329 * Set the currently selected items in the treeviewer
330 *
331 * @param selection
332 * The list of selected items
333 */
334 public void setSelection(@NonNull List<ITmfTreeViewerEntry> selection) {
335 IStructuredSelection sel = new StructuredSelection(selection);
336 fTreeViewer.setSelection(sel, true);
337 }
338
339 /**
340 * Add a selection listener to the tree viewer. This will be called when the
341 * selection changes and contain all the selected items.
342 *
343 * The selection change listener can be used like this:
344 *
345 * <pre>
346 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
347 * &#064;Override
348 * public void selectionChanged(SelectionChangedEvent event) {
349 * if (event.getSelection() instanceof IStructuredSelection) {
350 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
351 * if (selection instanceof ITmfTreeViewerEntry) {
352 * // Do something
353 * }
354 * }
355 * }
356 * });
357 * </pre>
358 *
359 * @param listener
360 * The {@link ISelectionChangedListener}
361 */
362 public void addSelectionChangeListener(ISelectionChangedListener listener) {
363 fTreeViewer.addSelectionChangedListener(listener);
364 }
365
366 /**
367 * Method called when the trace is loaded, to initialize any data once the
368 * trace has been set, but before the first call to update the content of
369 * the viewer.
370 */
371 protected void initializeDataSource() {
372
373 }
374
375 /**
376 * Clears the current content of the viewer.
377 */
378 protected void clearContent() {
379 fTreeViewer.setInput(null);
380 }
381
382 /**
383 * Method called after the content has been updated and the new input has
384 * been set on the tree.
385 *
386 * @param rootEntry
387 * The new input of this viewer, or null if none
388 */
389 protected void contentChanged(ITmfTreeViewerEntry rootEntry) {
390
391 }
392
393 /**
394 * Requests an update of the viewer's content in a given time range or
395 * selection time range. An extra parameter defines whether these times
396 * correspond to the selection or the visible range, as the viewer may
397 * update differently in those cases.
398 *
399 * @param start
400 * The start time of the requested content
401 * @param end
402 * The end time of the requested content
403 * @param isSelection
404 * <code>true</code> if this time range is for a selection,
405 * <code>false</code> for the visible time range
406 */
407 protected void updateContent(final long start, final long end, final boolean isSelection) {
408 Job thread = new Job("") { //$NON-NLS-1$
409 @Override
410 public IStatus run(IProgressMonitor monitor) {
411 final ITmfTreeViewerEntry rootEntry = updateElements(start, end, isSelection);
412 /* Set the input in main thread only if it didn't change */
413 if (rootEntry != null) {
414 Display.getDefault().asyncExec(new Runnable() {
415 @Override
416 public void run() {
417 if (fTreeViewer.getControl().isDisposed()) {
418 return;
419 }
420
421 if (rootEntry != fTreeViewer.getInput()) {
422 fTreeViewer.setInput(rootEntry);
423 contentChanged(rootEntry);
424 } else {
425 fTreeViewer.refresh();
426 fTreeViewer.expandToLevel(fTreeViewer.getAutoExpandLevel());
427 }
428 // FIXME should add a bit of padding
429 for (TreeColumn column : fTreeViewer.getTree().getColumns()) {
430 column.pack();
431 }
432 }
433 });
434 }
435 return Status.OK_STATUS;
436 }
437 };
438 thread.setSystem(true);
439 thread.schedule();
440 }
441
442 /**
443 * Update the entries to the given start/end time. An extra parameter
444 * defines whether these times correspond to the selection or the visible
445 * range, as the viewer may update differently in those cases. This methods
446 * returns a root node that is not meant to be visible. The children of this
447 * 'fake' root node are the first level of entries that will appear in the
448 * tree. If no update is necessary, the method should return
449 * <code>null</code>. To empty the tree, a root node containing an empty
450 * list of children should be returned.
451 *
452 * This method is not called in the UI thread when using the default viewer
453 * content update. Resource-intensive calculations here should not block the
454 * UI.
455 *
456 * @param start
457 * The start time of the requested content
458 * @param end
459 * The end time of the requested content
460 * @param isSelection
461 * <code>true</code> if this time range is for a selection,
462 * <code>false</code> for the visible time range
463 * @return The root entry of the list of entries to display or
464 * <code>null</code> if no update necessary
465 */
466 protected abstract ITmfTreeViewerEntry updateElements(long start, long end, boolean isSelection);
467
468 /**
469 * Get the current input displayed by the viewer
470 *
471 * @return The input of the tree viewer, the root entry
472 */
473 protected ITmfTreeViewerEntry getInput() {
474 return (ITmfTreeViewerEntry) fTreeViewer.getInput();
475 }
476
477 // ------------------------------------------------------------------------
478 // Signal Handler
479 // ------------------------------------------------------------------------
480
481 /**
482 * Signal handler for handling of the time synch signal. The times
483 * correspond to the selection by the user, not the visible time range.
484 *
485 * @param signal
486 * The time synch signal {@link TmfSelectionRangeUpdatedSignal}
487 */
488 @Override
489 @TmfSignalHandler
490 public void selectionRangeUpdated(TmfSelectionRangeUpdatedSignal signal) {
491 super.selectionRangeUpdated(signal);
492 if ((signal.getSource() != this) && (getTrace() != null)) {
493 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
494 }
495 }
496
497 /**
498 * Signal handler for handling of the window range signal. This time range
499 * is the visible zone of the view.
500 *
501 * @param signal
502 * The {@link TmfWindowRangeUpdatedSignal}
503 */
504 @Override
505 @TmfSignalHandler
506 public void windowRangeUpdated(TmfWindowRangeUpdatedSignal signal) {
507 super.windowRangeUpdated(signal);
508 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
509 }
510
511 @Override
512 public void reset() {
513 super.reset();
514 clearContent();
515 }
516
517 }
This page took 0.041987 seconds and 5 git commands to generate.