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
);
198 * Get the column data provider that will contain the list of columns to be
199 * part of this viewer. It is called once during the constructor.
201 * @return The tree column data provider for this viewer.
203 protected abstract ITmfTreeColumnDataProvider
getColumnDataProvider();
206 * Sets the tree columns for this tree viewer
209 * The tree column data
211 public void setTreeColumns(final List
<TmfTreeColumnData
> columns
) {
212 boolean hasPercentProvider
= false;
213 for (final TmfTreeColumnData columnData
: columns
) {
214 columnData
.createColumn(fTreeViewer
);
215 hasPercentProvider
|= (columnData
.getPercentageProvider() != null);
218 if (hasPercentProvider
) {
220 * Handler that will draw bar charts in the cell using a percentage
223 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
225 public void handleEvent(Event event
) {
226 if (columns
.get(event
.index
).getPercentageProvider() != null) {
228 double percentage
= columns
.get(event
.index
).getPercentageProvider().getPercentage(event
.item
.getData());
229 if (percentage
== 0) { // No bar to draw
233 if ((event
.detail
& SWT
.SELECTED
) > 0) {
235 * The item is selected. Draw our own background to
236 * avoid overwriting the bar.
238 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
239 event
.detail
&= ~SWT
.SELECTED
;
242 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(event
.index
).getWidth() - 8) * percentage
);
243 int oldAlpha
= event
.gc
.getAlpha();
244 Color oldForeground
= event
.gc
.getForeground();
245 Color oldBackground
= event
.gc
.getBackground();
247 * Draws a transparent gradient rectangle from the color
248 * of foreground and background.
250 event
.gc
.setAlpha(64);
251 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
252 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
253 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
254 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
255 /* Restores old values */
256 event
.gc
.setForeground(oldForeground
);
257 event
.gc
.setBackground(oldBackground
);
258 event
.gc
.setAlpha(oldAlpha
);
259 event
.detail
&= ~SWT
.BACKGROUND
;
267 * Set the label provider that will fill the columns of the tree viewer
269 * @param labelProvider
270 * The label provider to fill the columns
272 protected void setLabelProvider(IBaseLabelProvider labelProvider
) {
273 fTreeViewer
.setLabelProvider(labelProvider
);
277 * Get the tree viewer object
279 * @return The tree viewer object displayed by this viewer
281 protected TreeViewer
getTreeViewer() {
285 // ------------------------------------------------------------------------
287 // ------------------------------------------------------------------------
290 public Control
getControl() {
291 return fTreeViewer
.getControl();
295 public void refresh() {
296 Tree tree
= fTreeViewer
.getTree();
297 tree
.setRedraw(false);
298 fTreeViewer
.refresh();
299 fTreeViewer
.expandAll();
300 tree
.setRedraw(true);
304 public void loadTrace(ITmfTrace trace
) {
305 super.loadTrace(trace
);
306 Thread thread
= new Thread() {
309 initializeDataSource();
310 Display
.getDefault().asyncExec(new Runnable() {
314 updateContent(getWindowStartTime(), getWindowEndTime(), false);
322 // ------------------------------------------------------------------------
324 // ------------------------------------------------------------------------
327 * Set the currently selected items in the treeviewer
330 * The list of selected items
332 public void setSelection(@NonNull List
<ITmfTreeViewerEntry
> selection
) {
333 IStructuredSelection sel
= new StructuredSelection(selection
);
334 fTreeViewer
.setSelection(sel
, true);
338 * Add a selection listener to the tree viewer. This will be called when the
339 * selection changes and contain all the selected items.
341 * The selection change listener can be used like this:
344 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
346 * public void selectionChanged(SelectionChangedEvent event) {
347 * if (event.getSelection() instanceof IStructuredSelection) {
348 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
349 * if (selection instanceof ITmfTreeViewerEntry) {
358 * The {@link ISelectionChangedListener}
360 public void addSelectionChangeListener(ISelectionChangedListener listener
) {
361 fTreeViewer
.addSelectionChangedListener(listener
);
365 * Method called when the trace is loaded, to initialize any data once the
366 * trace has been set, but before the first call to update the content of
369 protected void initializeDataSource() {
374 * Clears the current content of the viewer.
376 protected void clearContent() {
377 fTreeViewer
.setInput(null);
381 * Method called after the content has been updated and the new input has
382 * been set on the tree.
385 * The new input of this viewer, or null if none
387 protected void contentChanged(ITmfTreeViewerEntry rootEntry
) {
392 * Requests an update of the viewer's content in a given time range or
393 * selection time range. An extra parameter defines whether these times
394 * correspond to the selection or the visible range, as the viewer may
395 * update differently in those cases.
398 * The start time of the requested content
400 * The end time of the requested content
402 * <code>true</code> if this time range is for a selection,
403 * <code>false</code> for the visible time range
405 protected void updateContent(final long start
, final long end
, final boolean isSelection
) {
406 Job thread
= new Job("") { //$NON-NLS-1$
408 public IStatus
run(IProgressMonitor monitor
) {
409 final ITmfTreeViewerEntry rootEntry
= updateElements(start
, end
, isSelection
);
410 /* Set the input in main thread only if it didn't change */
411 if (rootEntry
!= null) {
412 Display
.getDefault().asyncExec(new Runnable() {
415 if (fTreeViewer
.getControl().isDisposed()) {
419 if (rootEntry
!= fTreeViewer
.getInput()) {
420 fTreeViewer
.setInput(rootEntry
);
421 contentChanged(rootEntry
);
423 fTreeViewer
.refresh();
424 fTreeViewer
.expandToLevel(fTreeViewer
.getAutoExpandLevel());
426 // FIXME should add a bit of padding
427 for (TreeColumn column
: fTreeViewer
.getTree().getColumns()) {
433 return Status
.OK_STATUS
;
436 thread
.setSystem(true);
441 * Update the entries to the given start/end time. An extra parameter
442 * defines whether these times correspond to the selection or the visible
443 * range, as the viewer may update differently in those cases. This methods
444 * returns a root node that is not meant to be visible. The children of this
445 * 'fake' root node are the first level of entries that will appear in the
446 * tree. If no update is necessary, the method should return
447 * <code>null</code>. To empty the tree, a root node containing an empty
448 * list of children should be returned.
450 * This method is not called in the UI thread when using the default viewer
451 * content update. Resource-intensive calculations here should not block the
455 * The start time of the requested content
457 * The end time of the requested content
459 * <code>true</code> if this time range is for a selection,
460 * <code>false</code> for the visible time range
461 * @return The root entry of the list of entries to display or
462 * <code>null</code> if no update necessary
464 protected abstract ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
);
467 * Get the current input displayed by the viewer
469 * @return The input of the tree viewer, the root entry
471 protected ITmfTreeViewerEntry
getInput() {
472 return (ITmfTreeViewerEntry
) fTreeViewer
.getInput();
475 // ------------------------------------------------------------------------
477 // ------------------------------------------------------------------------
480 * Signal handler for handling of the time synch signal. The times
481 * correspond to the selection by the user, not the visible time range.
484 * The time synch signal {@link TmfSelectionRangeUpdatedSignal}
488 public void selectionRangeUpdated(TmfSelectionRangeUpdatedSignal signal
) {
489 super.selectionRangeUpdated(signal
);
490 if ((signal
.getSource() != this) && (getTrace() != null)) {
491 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
496 * Signal handler for handling of the window range signal. This time range
497 * is the visible zone of the view.
500 * The {@link TmfWindowRangeUpdatedSignal}
504 public void windowRangeUpdated(TmfWindowRangeUpdatedSignal signal
) {
505 super.windowRangeUpdated(signal
);
506 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
510 public void reset() {