1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
;
15 import java
.util
.List
;
17 import org
.eclipse
.jdt
.annotation
.NonNull
;
18 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
19 import org
.eclipse
.jface
.viewers
.IBaseLabelProvider
;
20 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
21 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
22 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
23 import org
.eclipse
.jface
.viewers
.ITableColorProvider
;
24 import org
.eclipse
.jface
.viewers
.ITableFontProvider
;
25 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
26 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
27 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
28 import org
.eclipse
.jface
.viewers
.TreeViewer
;
29 import org
.eclipse
.jface
.viewers
.Viewer
;
30 import org
.eclipse
.swt
.SWT
;
31 import org
.eclipse
.swt
.graphics
.Color
;
32 import org
.eclipse
.swt
.graphics
.Font
;
33 import org
.eclipse
.swt
.graphics
.Image
;
34 import org
.eclipse
.swt
.widgets
.Composite
;
35 import org
.eclipse
.swt
.widgets
.Control
;
36 import org
.eclipse
.swt
.widgets
.Display
;
37 import org
.eclipse
.swt
.widgets
.Event
;
38 import org
.eclipse
.swt
.widgets
.Listener
;
39 import org
.eclipse
.swt
.widgets
.Tree
;
40 import org
.eclipse
.swt
.widgets
.TreeColumn
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfRangeSynchSignal
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTimeSynchSignal
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
45 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.TmfTimeViewer
;
48 * Abstract class for viewers who will display data using a TreeViewer. It
49 * automatically synchronizes with time information of the UI. It also
50 * implements some common functionalities for all tree viewer, such as managing
51 * the column data, content initialization and update. The viewer implementing
52 * this does not have to worry about whether some code runs in the UI thread or
55 * @author Geneviève Bastien
58 public abstract class AbstractTmfTreeViewer
extends TmfTimeViewer
{
60 private final TreeViewer fTreeViewer
;
62 // ------------------------------------------------------------------------
64 // ------------------------------------------------------------------------
66 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
67 private class TreeContentProvider
implements ITreeContentProvider
{
70 public void dispose() {
74 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
78 public Object
[] getElements(Object inputElement
) {
79 if (inputElement
instanceof ITmfTreeViewerEntry
) {
80 return ((ITmfTreeViewerEntry
) inputElement
).getChildren().toArray(new ITmfTreeViewerEntry
[0]);
82 return new ITmfTreeViewerEntry
[0];
86 public Object
[] getChildren(Object parentElement
) {
87 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) parentElement
;
88 List
<?
extends ITmfTreeViewerEntry
> children
= entry
.getChildren();
89 return children
.toArray(new ITmfTreeViewerEntry
[children
.size()]);
93 public Object
getParent(Object element
) {
94 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
95 return entry
.getParent();
99 public boolean hasChildren(Object element
) {
100 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
101 return entry
.hasChildren();
107 * Base class to provide the labels for the tree viewer. Views extending
108 * this class typically need to override the getColumnText method if they
109 * have more than one column to display. It also allows to change the font
110 * and colors of the cells.
112 protected static class TreeLabelProvider
implements ITableLabelProvider
, ITableFontProvider
, ITableColorProvider
{
115 public void addListener(ILabelProviderListener listener
) {
119 public void dispose() {
123 public boolean isLabelProperty(Object element
, String property
) {
128 public void removeListener(ILabelProviderListener listener
) {
132 public Image
getColumnImage(Object element
, int columnIndex
) {
137 public String
getColumnText(Object element
, int columnIndex
) {
138 if ((element
instanceof ITmfTreeViewerEntry
) && (columnIndex
== 0)) {
139 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
140 return entry
.getName();
146 public Color
getForeground(Object element
, int columnIndex
) {
147 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_FOREGROUND
);
151 public Color
getBackground(Object element
, int columnIndex
) {
152 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
);
156 public Font
getFont(Object element
, int columnIndex
) {
162 // ------------------------------------------------------------------------
163 // Constructors and initialization methods
164 // ------------------------------------------------------------------------
170 * The parent composite that holds this viewer
171 * @param allowMultiSelect
172 * Whether multiple selections are allowed
174 public AbstractTmfTreeViewer(Composite parent
, boolean allowMultiSelect
) {
177 int flags
= SWT
.FULL_SELECTION
| SWT
.H_SCROLL
;
178 if (allowMultiSelect
) {
182 /* Build the tree viewer part of the view */
183 fTreeViewer
= new TreeViewer(parent
, flags
);
184 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
185 final Tree tree
= fTreeViewer
.getTree();
186 tree
.setHeaderVisible(true);
187 tree
.setLinesVisible(true);
188 fTreeViewer
.setContentProvider(new TreeContentProvider());
189 fTreeViewer
.setLabelProvider(new TreeLabelProvider());
190 List
<TmfTreeColumnData
> columns
= getColumnDataProvider().getColumnData();
191 this.setTreeColumns(columns
);
195 * Get the column data provider that will contain the list of columns to be
196 * part of this viewer. It is called once during the constructor.
198 * @return The tree column data provider for this viewer.
200 protected abstract ITmfTreeColumnDataProvider
getColumnDataProvider();
203 * Sets the tree columns for this tree viewer
206 * The tree column data
208 public void setTreeColumns(final List
<TmfTreeColumnData
> columns
) {
209 boolean hasPercentProvider
= false;
210 for (final TmfTreeColumnData columnData
: columns
) {
211 columnData
.createColumn(fTreeViewer
);
212 hasPercentProvider
|= (columnData
.getPercentageProvider() != null);
215 if (hasPercentProvider
) {
217 * Handler that will draw bar charts in the cell using a percentage
220 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
222 public void handleEvent(Event event
) {
223 if (columns
.get(event
.index
).getPercentageProvider() != null) {
225 double percentage
= columns
.get(event
.index
).getPercentageProvider().getPercentage(event
.item
.getData());
226 if (percentage
== 0) { // No bar to draw
230 if ((event
.detail
& SWT
.SELECTED
) > 0) {
232 * The item is selected. Draw our own background to
233 * avoid overwriting the bar.
235 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
236 event
.detail
&= ~SWT
.SELECTED
;
239 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(event
.index
).getWidth() - 8) * percentage
);
240 int oldAlpha
= event
.gc
.getAlpha();
241 Color oldForeground
= event
.gc
.getForeground();
242 Color oldBackground
= event
.gc
.getBackground();
244 * Draws a transparent gradient rectangle from the color
245 * of foreground and background.
247 event
.gc
.setAlpha(64);
248 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
249 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
250 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
251 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
252 /* Restores old values */
253 event
.gc
.setForeground(oldForeground
);
254 event
.gc
.setBackground(oldBackground
);
255 event
.gc
.setAlpha(oldAlpha
);
256 event
.detail
&= ~SWT
.BACKGROUND
;
264 * Set the label provider that will fill the columns of the tree viewer
266 * @param labelProvider
267 * The label provider to fill the columns
269 protected void setLabelProvider(IBaseLabelProvider labelProvider
) {
270 fTreeViewer
.setLabelProvider(labelProvider
);
274 * Get the tree viewer object
276 * @return The tree viewer object displayed by this viewer
278 protected TreeViewer
getTreeViewer() {
282 // ------------------------------------------------------------------------
284 // ------------------------------------------------------------------------
287 public Control
getControl() {
288 return fTreeViewer
.getControl();
292 public void refresh() {
293 Tree tree
= fTreeViewer
.getTree();
294 tree
.setRedraw(false);
295 fTreeViewer
.refresh();
296 fTreeViewer
.expandAll();
297 tree
.setRedraw(true);
301 public void loadTrace(ITmfTrace trace
) {
302 super.loadTrace(trace
);
303 Thread thread
= new Thread() {
306 initializeDataSource();
307 Display
.getDefault().asyncExec(new Runnable() {
311 updateContent(getWindowStartTime(), getWindowEndTime(), false);
319 // ------------------------------------------------------------------------
321 // ------------------------------------------------------------------------
324 * Set the currently selected items in the treeviewer
327 * The list of selected items
330 public void setSelection(@NonNull List
<ITmfTreeViewerEntry
> selection
) {
331 IStructuredSelection sel
= new StructuredSelection(selection
);
332 fTreeViewer
.setSelection(sel
, true);
336 * Add a selection listener to the tree viewer. This will be called when the
337 * selection changes and contain all the selected items.
339 * The selection change listener can be used like this:
342 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
344 * public void selectionChanged(SelectionChangedEvent event) {
345 * if (event.getSelection() instanceof IStructuredSelection) {
346 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
347 * if (selection instanceof ITmfTreeViewerEntry) {
356 * The {@link ISelectionChangedListener}
358 public void addSelectionChangeListener(ISelectionChangedListener listener
) {
359 fTreeViewer
.addSelectionChangedListener(listener
);
363 * Method called when the trace is loaded, to initialize any data once the
364 * trace has been set, but before the first call to update the content of
367 protected void initializeDataSource() {
372 * Clears the current content of the viewer.
374 protected void clearContent() {
375 fTreeViewer
.setInput(null);
379 * Method called after the content has been updated and the new input has
380 * been set on the tree.
383 * The new input of this viewer, or null if none
386 protected void contentChanged(ITmfTreeViewerEntry rootEntry
) {
391 * Requests an update of the viewer's content in a given time range or
392 * selection time range. An extra parameter defines whether these times
393 * correspond to the selection or the visible range, as the viewer may
394 * update differently in those cases.
397 * The start time of the requested content
399 * The end time of the requested content
401 * <code>true</code> if this time range is for a selection,
402 * <code>false</code> for the visible time range
404 protected void updateContent(final long start
, final long end
, final boolean isSelection
) {
405 Thread thread
= new Thread() {
408 final ITmfTreeViewerEntry rootEntry
= updateElements(start
, end
, isSelection
);
409 /* Set the input in main thread only if it didn't change */
410 if (rootEntry
!= null) {
411 Display
.getDefault().asyncExec(new Runnable() {
414 if (rootEntry
!= fTreeViewer
.getInput()) {
415 fTreeViewer
.setInput(rootEntry
);
416 contentChanged(rootEntry
);
418 fTreeViewer
.refresh();
419 fTreeViewer
.expandToLevel(fTreeViewer
.getAutoExpandLevel());
421 // FIXME should add a bit of padding
422 for (TreeColumn column
: fTreeViewer
.getTree().getColumns()) {
434 * Update the entries to the given start/end time. An extra parameter
435 * defines whether these times correspond to the selection or the visible
436 * range, as the viewer may update differently in those cases. This methods
437 * returns a root node that is not meant to be visible. The children of this
438 * 'fake' root node are the first level of entries that will appear in the
439 * tree. If no update is necessary, the method should return
440 * <code>null</code>. To empty the tree, a root node containing an empty
441 * list of children should be returned.
443 * This method is not called in the UI thread when using the default viewer
444 * content update. Resource-intensive calculations here should not block the
448 * The start time of the requested content
450 * The end time of the requested content
452 * <code>true</code> if this time range is for a selection,
453 * <code>false</code> for the visible time range
454 * @return The root entry of the list of entries to display or
455 * <code>null</code> if no update necessary
457 protected abstract ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
);
460 * Get the current input displayed by the viewer
462 * @return The input of the tree viewer, the root entry
464 protected ITmfTreeViewerEntry
getInput() {
465 return (ITmfTreeViewerEntry
) fTreeViewer
.getInput();
468 // ------------------------------------------------------------------------
470 // ------------------------------------------------------------------------
473 * Signal handler for handling of the time synch signal. The times
474 * correspond to the selection by the user, not the visible time range.
477 * The time synch signal {@link TmfTimeSynchSignal}
481 public void selectionRangeUpdated(TmfTimeSynchSignal signal
) {
482 super.selectionRangeUpdated(signal
);
483 if ((signal
.getSource() != this) && (getTrace() != null)) {
484 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
489 * Signal handler for handling of the time range synch signal. This time
490 * range is the visible zone of the view.
493 * The time range synch signal {@link TmfRangeSynchSignal}
497 public void timeRangeUpdated(TmfRangeSynchSignal signal
) {
498 super.timeRangeUpdated(signal
);
499 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
503 public void reset() {