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
.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
;
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
59 * @author Geneviève Bastien
61 public abstract class AbstractTmfTreeViewer
extends TmfTimeViewer
{
63 private final TreeViewer fTreeViewer
;
65 // ------------------------------------------------------------------------
67 // ------------------------------------------------------------------------
69 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
70 private class TreeContentProvider
implements ITreeContentProvider
{
73 public void dispose() {
77 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
81 public Object
[] getElements(Object inputElement
) {
82 if (inputElement
instanceof ITmfTreeViewerEntry
) {
83 return ((ITmfTreeViewerEntry
) inputElement
).getChildren().toArray(new ITmfTreeViewerEntry
[0]);
85 return new ITmfTreeViewerEntry
[0];
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()]);
96 public Object
getParent(Object element
) {
97 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
98 return entry
.getParent();
102 public boolean hasChildren(Object element
) {
103 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
104 return entry
.hasChildren();
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.
115 protected static class TreeLabelProvider
implements ITableLabelProvider
, ITableFontProvider
, ITableColorProvider
{
118 public void addListener(ILabelProviderListener listener
) {
122 public void dispose() {
126 public boolean isLabelProperty(Object element
, String property
) {
131 public void removeListener(ILabelProviderListener listener
) {
135 public Image
getColumnImage(Object element
, int columnIndex
) {
140 public String
getColumnText(Object element
, int columnIndex
) {
141 if ((element
instanceof ITmfTreeViewerEntry
) && (columnIndex
== 0)) {
142 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
143 return entry
.getName();
149 public Color
getForeground(Object element
, int columnIndex
) {
150 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_FOREGROUND
);
154 public Color
getBackground(Object element
, int columnIndex
) {
155 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
);
159 public Font
getFont(Object element
, int columnIndex
) {
165 // ------------------------------------------------------------------------
166 // Constructors and initialization methods
167 // ------------------------------------------------------------------------
173 * The parent composite that holds this viewer
174 * @param allowMultiSelect
175 * Whether multiple selections are allowed
177 public AbstractTmfTreeViewer(Composite parent
, boolean allowMultiSelect
) {
180 int flags
= SWT
.FULL_SELECTION
| SWT
.H_SCROLL
;
181 if (allowMultiSelect
) {
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
);
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.
202 * @return The tree column data provider for this viewer.
204 protected abstract ITmfTreeColumnDataProvider
getColumnDataProvider();
207 * Sets the tree columns for this tree viewer
210 * The tree column data
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);
219 if (hasPercentProvider
) {
221 * Handler that will draw bar charts in the cell using a percentage
224 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
226 public void handleEvent(Event event
) {
227 if (columns
.get(event
.index
).getPercentageProvider() != null) {
229 double percentage
= columns
.get(event
.index
).getPercentageProvider().getPercentage(event
.item
.getData());
230 if (percentage
== 0) { // No bar to draw
234 if ((event
.detail
& SWT
.SELECTED
) > 0) {
236 * The item is selected. Draw our own background to
237 * avoid overwriting the bar.
239 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
240 event
.detail
&= ~SWT
.SELECTED
;
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();
248 * Draws a transparent gradient rectangle from the color
249 * of foreground and background.
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
;
268 * Set the label provider that will fill the columns of the tree viewer
270 * @param labelProvider
271 * The label provider to fill the columns
273 protected void setLabelProvider(IBaseLabelProvider labelProvider
) {
274 fTreeViewer
.setLabelProvider(labelProvider
);
278 * Get the tree viewer object
280 * @return The tree viewer object displayed by this viewer
283 public TreeViewer
getTreeViewer() {
287 // ------------------------------------------------------------------------
289 // ------------------------------------------------------------------------
292 public Control
getControl() {
293 return fTreeViewer
.getControl();
297 public void refresh() {
298 Tree tree
= fTreeViewer
.getTree();
299 tree
.setRedraw(false);
300 fTreeViewer
.refresh();
301 fTreeViewer
.expandAll();
302 tree
.setRedraw(true);
306 public void loadTrace(ITmfTrace trace
) {
307 super.loadTrace(trace
);
308 Thread thread
= new Thread() {
311 initializeDataSource();
312 Display
.getDefault().asyncExec(new Runnable() {
316 updateContent(getWindowStartTime(), getWindowEndTime(), false);
324 // ------------------------------------------------------------------------
326 // ------------------------------------------------------------------------
329 * Set the currently selected items in the treeviewer
332 * The list of selected items
334 public void setSelection(@NonNull List
<ITmfTreeViewerEntry
> selection
) {
335 IStructuredSelection sel
= new StructuredSelection(selection
);
336 fTreeViewer
.setSelection(sel
, true);
340 * Add a selection listener to the tree viewer. This will be called when the
341 * selection changes and contain all the selected items.
343 * The selection change listener can be used like this:
346 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
348 * public void selectionChanged(SelectionChangedEvent event) {
349 * if (event.getSelection() instanceof IStructuredSelection) {
350 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
351 * if (selection instanceof ITmfTreeViewerEntry) {
360 * The {@link ISelectionChangedListener}
362 public void addSelectionChangeListener(ISelectionChangedListener listener
) {
363 fTreeViewer
.addSelectionChangedListener(listener
);
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
371 protected void initializeDataSource() {
376 * Clears the current content of the viewer.
378 protected void clearContent() {
379 fTreeViewer
.setInput(null);
383 * Method called after the content has been updated and the new input has
384 * been set on the tree.
387 * The new input of this viewer, or null if none
389 protected void contentChanged(ITmfTreeViewerEntry rootEntry
) {
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.
400 * The start time of the requested content
402 * The end time of the requested content
404 * <code>true</code> if this time range is for a selection,
405 * <code>false</code> for the visible time range
407 protected void updateContent(final long start
, final long end
, final boolean isSelection
) {
408 Job thread
= new Job("") { //$NON-NLS-1$
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() {
417 if (fTreeViewer
.getControl().isDisposed()) {
421 if (rootEntry
!= fTreeViewer
.getInput()) {
422 fTreeViewer
.setInput(rootEntry
);
423 contentChanged(rootEntry
);
425 fTreeViewer
.refresh();
426 fTreeViewer
.expandToLevel(fTreeViewer
.getAutoExpandLevel());
428 // FIXME should add a bit of padding
429 for (TreeColumn column
: fTreeViewer
.getTree().getColumns()) {
435 return Status
.OK_STATUS
;
438 thread
.setSystem(true);
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.
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
457 * The start time of the requested content
459 * The end time of the requested content
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
466 protected abstract ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
);
469 * Get the current input displayed by the viewer
471 * @return The input of the tree viewer, the root entry
473 protected ITmfTreeViewerEntry
getInput() {
474 return (ITmfTreeViewerEntry
) fTreeViewer
.getInput();
477 // ------------------------------------------------------------------------
479 // ------------------------------------------------------------------------
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.
486 * The time synch signal {@link TmfSelectionRangeUpdatedSignal}
490 public void selectionRangeUpdated(TmfSelectionRangeUpdatedSignal signal
) {
491 super.selectionRangeUpdated(signal
);
492 if ((signal
.getSource() != this) && (getTrace() != null)) {
493 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
498 * Signal handler for handling of the window range signal. This time range
499 * is the visible zone of the view.
502 * The {@link TmfWindowRangeUpdatedSignal}
506 public void windowRangeUpdated(TmfWindowRangeUpdatedSignal signal
) {
507 super.windowRangeUpdated(signal
);
508 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
512 public void reset() {