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
57 public abstract class AbstractTmfTreeViewer
extends TmfTimeViewer
{
59 private final TreeViewer fTreeViewer
;
61 // ------------------------------------------------------------------------
63 // ------------------------------------------------------------------------
65 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
66 private class TreeContentProvider
implements ITreeContentProvider
{
69 public void dispose() {
73 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
77 public Object
[] getElements(Object inputElement
) {
78 if (inputElement
instanceof ITmfTreeViewerEntry
) {
79 return ((ITmfTreeViewerEntry
) inputElement
).getChildren().toArray(new ITmfTreeViewerEntry
[0]);
81 return new ITmfTreeViewerEntry
[0];
85 public Object
[] getChildren(Object parentElement
) {
86 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) parentElement
;
87 List
<?
extends ITmfTreeViewerEntry
> children
= entry
.getChildren();
88 return children
.toArray(new ITmfTreeViewerEntry
[children
.size()]);
92 public Object
getParent(Object element
) {
93 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
94 return entry
.getParent();
98 public boolean hasChildren(Object element
) {
99 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
100 return entry
.hasChildren();
106 * Base class to provide the labels for the tree viewer. Views extending
107 * this class typically need to override the getColumnText method if they
108 * have more than one column to display. It also allows to change the font
109 * and colors of the cells.
111 protected static class TreeLabelProvider
implements ITableLabelProvider
, ITableFontProvider
, ITableColorProvider
{
114 public void addListener(ILabelProviderListener listener
) {
118 public void dispose() {
122 public boolean isLabelProperty(Object element
, String property
) {
127 public void removeListener(ILabelProviderListener listener
) {
131 public Image
getColumnImage(Object element
, int columnIndex
) {
136 public String
getColumnText(Object element
, int columnIndex
) {
137 if ((element
instanceof ITmfTreeViewerEntry
) && (columnIndex
== 0)) {
138 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
139 return entry
.getName();
145 public Color
getForeground(Object element
, int columnIndex
) {
146 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_FOREGROUND
);
150 public Color
getBackground(Object element
, int columnIndex
) {
151 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
);
155 public Font
getFont(Object element
, int columnIndex
) {
161 // ------------------------------------------------------------------------
162 // Constructors and initialization methods
163 // ------------------------------------------------------------------------
169 * The parent composite that holds this viewer
170 * @param allowMultiSelect
171 * Whether multiple selections are allowed
173 public AbstractTmfTreeViewer(Composite parent
, boolean allowMultiSelect
) {
176 int flags
= SWT
.FULL_SELECTION
| SWT
.H_SCROLL
;
177 if (allowMultiSelect
) {
181 /* Build the tree viewer part of the view */
182 fTreeViewer
= new TreeViewer(parent
, flags
);
183 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
184 final Tree tree
= fTreeViewer
.getTree();
185 tree
.setHeaderVisible(true);
186 tree
.setLinesVisible(true);
187 fTreeViewer
.setContentProvider(new TreeContentProvider());
188 fTreeViewer
.setLabelProvider(new TreeLabelProvider());
189 List
<TmfTreeColumnData
> columns
= getColumnDataProvider().getColumnData();
190 this.setTreeColumns(columns
);
194 * Get the column data provider that will contain the list of columns to be
195 * part of this viewer. It is called once during the constructor.
197 * @return The tree column data provider for this viewer.
199 protected abstract ITmfTreeColumnDataProvider
getColumnDataProvider();
202 * Sets the tree columns for this tree viewer
205 * The tree column data
207 public void setTreeColumns(final List
<TmfTreeColumnData
> columns
) {
208 boolean hasPercentProvider
= false;
209 for (final TmfTreeColumnData columnData
: columns
) {
210 columnData
.createColumn(fTreeViewer
);
211 hasPercentProvider
|= (columnData
.getPercentageProvider() != null);
214 if (hasPercentProvider
) {
216 * Handler that will draw bar charts in the cell using a percentage
219 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
221 public void handleEvent(Event event
) {
222 if (columns
.get(event
.index
).getPercentageProvider() != null) {
224 double percentage
= columns
.get(event
.index
).getPercentageProvider().getPercentage(event
.item
.getData());
225 if (percentage
== 0) { // No bar to draw
229 if ((event
.detail
& SWT
.SELECTED
) > 0) {
231 * The item is selected. Draw our own background to
232 * avoid overwriting the bar.
234 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
235 event
.detail
&= ~SWT
.SELECTED
;
238 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(event
.index
).getWidth() - 8) * percentage
);
239 int oldAlpha
= event
.gc
.getAlpha();
240 Color oldForeground
= event
.gc
.getForeground();
241 Color oldBackground
= event
.gc
.getBackground();
243 * Draws a transparent gradient rectangle from the color
244 * of foreground and background.
246 event
.gc
.setAlpha(64);
247 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
248 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
249 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
250 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
251 /* Restores old values */
252 event
.gc
.setForeground(oldForeground
);
253 event
.gc
.setBackground(oldBackground
);
254 event
.gc
.setAlpha(oldAlpha
);
255 event
.detail
&= ~SWT
.BACKGROUND
;
263 * Set the label provider that will fill the columns of the tree viewer
265 * @param labelProvider
266 * The label provider to fill the columns
268 protected void setLabelProvider(IBaseLabelProvider labelProvider
) {
269 fTreeViewer
.setLabelProvider(labelProvider
);
273 * Get the tree viewer object
275 * @return The tree viewer object displayed by this viewer
277 protected TreeViewer
getTreeViewer() {
281 // ------------------------------------------------------------------------
283 // ------------------------------------------------------------------------
286 public Control
getControl() {
287 return fTreeViewer
.getControl();
291 public void refresh() {
292 Tree tree
= fTreeViewer
.getTree();
293 tree
.setRedraw(false);
294 fTreeViewer
.refresh();
295 fTreeViewer
.expandAll();
296 tree
.setRedraw(true);
300 public void loadTrace(ITmfTrace trace
) {
301 super.loadTrace(trace
);
302 Thread thread
= new Thread() {
305 initializeDataSource();
306 Display
.getDefault().asyncExec(new Runnable() {
310 updateContent(getWindowStartTime(), getWindowEndTime(), false);
318 // ------------------------------------------------------------------------
320 // ------------------------------------------------------------------------
323 * Set the currently selected items in the treeviewer
326 * The list of selected items
328 public void setSelection(@NonNull List
<ITmfTreeViewerEntry
> selection
) {
329 IStructuredSelection sel
= new StructuredSelection(selection
);
330 fTreeViewer
.setSelection(sel
, true);
334 * Add a selection listener to the tree viewer. This will be called when the
335 * selection changes and contain all the selected items.
337 * The selection change listener can be used like this:
340 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
342 * public void selectionChanged(SelectionChangedEvent event) {
343 * if (event.getSelection() instanceof IStructuredSelection) {
344 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
345 * if (selection instanceof ITmfTreeViewerEntry) {
354 * The {@link ISelectionChangedListener}
356 public void addSelectionChangeListener(ISelectionChangedListener listener
) {
357 fTreeViewer
.addSelectionChangedListener(listener
);
361 * Method called when the trace is loaded, to initialize any data once the
362 * trace has been set, but before the first call to update the content of
365 protected void initializeDataSource() {
370 * Clears the current content of the viewer.
372 protected void clearContent() {
373 fTreeViewer
.setInput(null);
377 * Method called after the content has been updated and the new input has
378 * been set on the tree.
381 * The new input of this viewer, or null if none
383 protected void contentChanged(ITmfTreeViewerEntry rootEntry
) {
388 * Requests an update of the viewer's content in a given time range or
389 * selection time range. An extra parameter defines whether these times
390 * correspond to the selection or the visible range, as the viewer may
391 * update differently in those cases.
394 * The start time of the requested content
396 * The end time of the requested content
398 * <code>true</code> if this time range is for a selection,
399 * <code>false</code> for the visible time range
401 protected void updateContent(final long start
, final long end
, final boolean isSelection
) {
402 Thread thread
= new Thread() {
405 final ITmfTreeViewerEntry rootEntry
= updateElements(start
, end
, isSelection
);
406 /* Set the input in main thread only if it didn't change */
407 if (rootEntry
!= null) {
408 Display
.getDefault().asyncExec(new Runnable() {
411 if (rootEntry
!= fTreeViewer
.getInput()) {
412 fTreeViewer
.setInput(rootEntry
);
413 contentChanged(rootEntry
);
415 fTreeViewer
.refresh();
416 fTreeViewer
.expandToLevel(fTreeViewer
.getAutoExpandLevel());
418 // FIXME should add a bit of padding
419 for (TreeColumn column
: fTreeViewer
.getTree().getColumns()) {
431 * Update the entries to the given start/end time. An extra parameter
432 * defines whether these times correspond to the selection or the visible
433 * range, as the viewer may update differently in those cases. This methods
434 * returns a root node that is not meant to be visible. The children of this
435 * 'fake' root node are the first level of entries that will appear in the
436 * tree. If no update is necessary, the method should return
437 * <code>null</code>. To empty the tree, a root node containing an empty
438 * list of children should be returned.
440 * This method is not called in the UI thread when using the default viewer
441 * content update. Resource-intensive calculations here should not block the
445 * The start time of the requested content
447 * The end time of the requested content
449 * <code>true</code> if this time range is for a selection,
450 * <code>false</code> for the visible time range
451 * @return The root entry of the list of entries to display or
452 * <code>null</code> if no update necessary
454 protected abstract ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
);
457 * Get the current input displayed by the viewer
459 * @return The input of the tree viewer, the root entry
461 protected ITmfTreeViewerEntry
getInput() {
462 return (ITmfTreeViewerEntry
) fTreeViewer
.getInput();
465 // ------------------------------------------------------------------------
467 // ------------------------------------------------------------------------
470 * Signal handler for handling of the time synch signal. The times
471 * correspond to the selection by the user, not the visible time range.
474 * The time synch signal {@link TmfTimeSynchSignal}
478 public void selectionRangeUpdated(TmfTimeSynchSignal signal
) {
479 super.selectionRangeUpdated(signal
);
480 if ((signal
.getSource() != this) && (getTrace() != null)) {
481 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
486 * Signal handler for handling of the time range synch signal. This time
487 * range is the visible zone of the view.
490 * The time range synch signal {@link TmfRangeSynchSignal}
494 public void timeRangeUpdated(TmfRangeSynchSignal signal
) {
495 super.timeRangeUpdated(signal
);
496 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
500 public void reset() {