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
.linuxtools
.tmf
.ui
.viewers
.tree
;
15 import java
.util
.List
;
17 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
18 import org
.eclipse
.jface
.viewers
.IBaseLabelProvider
;
19 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
20 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
21 import org
.eclipse
.jface
.viewers
.ITableColorProvider
;
22 import org
.eclipse
.jface
.viewers
.ITableFontProvider
;
23 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
24 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
25 import org
.eclipse
.jface
.viewers
.TreeViewer
;
26 import org
.eclipse
.jface
.viewers
.Viewer
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfRangeSynchSignal
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTimeSynchSignal
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
31 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.TmfTimeViewer
;
32 import org
.eclipse
.swt
.SWT
;
33 import org
.eclipse
.swt
.graphics
.Color
;
34 import org
.eclipse
.swt
.graphics
.Font
;
35 import org
.eclipse
.swt
.graphics
.Image
;
36 import org
.eclipse
.swt
.widgets
.Composite
;
37 import org
.eclipse
.swt
.widgets
.Control
;
38 import org
.eclipse
.swt
.widgets
.Display
;
39 import org
.eclipse
.swt
.widgets
.Event
;
40 import org
.eclipse
.swt
.widgets
.Listener
;
41 import org
.eclipse
.swt
.widgets
.Tree
;
42 import org
.eclipse
.swt
.widgets
.TreeColumn
;
45 * Abstract class for viewers who will display data using a TreeViewer. It
46 * automatically synchronizes with time information of the UI. It also
47 * implements some common functionalities for all tree viewer, such as managing
48 * the column data, content initialization and update. The viewer implementing
49 * this does not have to worry about whether some code runs in the UI thread or
52 * @author Geneviève Bastien
55 public abstract class AbstractTmfTreeViewer
extends TmfTimeViewer
{
57 private final TreeViewer fTreeViewer
;
59 // ------------------------------------------------------------------------
61 // ------------------------------------------------------------------------
63 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
64 private class TreeContentProvider
implements ITreeContentProvider
{
67 public void dispose() {
71 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
75 public Object
[] getElements(Object inputElement
) {
76 if (inputElement
instanceof ITmfTreeViewerEntry
) {
77 return ((ITmfTreeViewerEntry
) inputElement
).getChildren().toArray(new ITmfTreeViewerEntry
[0]);
79 return new ITmfTreeViewerEntry
[0];
83 public Object
[] getChildren(Object parentElement
) {
84 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) parentElement
;
85 List
<?
extends ITmfTreeViewerEntry
> children
= entry
.getChildren();
86 return children
.toArray(new ITmfTreeViewerEntry
[children
.size()]);
90 public Object
getParent(Object element
) {
91 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
92 return entry
.getParent();
96 public boolean hasChildren(Object element
) {
97 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
98 return entry
.hasChildren();
104 * Base class to provide the labels for the tree viewer. Views extending
105 * this class typically need to override the getColumnText method if they
106 * have more than one column to display. It also allows to change the font
107 * and colors of the cells.
109 protected static class TreeLabelProvider
implements ITableLabelProvider
, ITableFontProvider
, ITableColorProvider
{
112 public void addListener(ILabelProviderListener listener
) {
116 public void dispose() {
120 public boolean isLabelProperty(Object element
, String property
) {
125 public void removeListener(ILabelProviderListener listener
) {
129 public Image
getColumnImage(Object element
, int columnIndex
) {
134 public String
getColumnText(Object element
, int columnIndex
) {
135 if ((element
instanceof ITmfTreeViewerEntry
) && (columnIndex
== 0)) {
136 ITmfTreeViewerEntry entry
= (ITmfTreeViewerEntry
) element
;
137 return entry
.getName();
143 public Color
getForeground(Object element
, int columnIndex
) {
144 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_FOREGROUND
);
148 public Color
getBackground(Object element
, int columnIndex
) {
149 return Display
.getCurrent().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
);
153 public Font
getFont(Object element
, int columnIndex
) {
159 // ------------------------------------------------------------------------
160 // Constructors and initialization methods
161 // ------------------------------------------------------------------------
167 * The parent composite that holds this viewer
168 * @param allowMultiSelect
169 * Whether multiple selections are allowed
171 public AbstractTmfTreeViewer(Composite parent
, boolean allowMultiSelect
) {
174 int flags
= SWT
.FULL_SELECTION
| SWT
.H_SCROLL
;
175 if (allowMultiSelect
) {
179 /* Build the tree viewer part of the view */
180 fTreeViewer
= new TreeViewer(parent
, flags
);
181 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
182 final Tree tree
= fTreeViewer
.getTree();
183 tree
.setHeaderVisible(true);
184 tree
.setLinesVisible(true);
185 fTreeViewer
.setContentProvider(new TreeContentProvider());
186 fTreeViewer
.setLabelProvider(new TreeLabelProvider());
187 List
<TmfTreeColumnData
> columns
= getColumnDataProvider().getColumnData();
188 this.setTreeColumns(columns
);
192 * Get the column data provider that will contain the list of columns to be
193 * part of this viewer. It is called once during the constructor.
195 * @return The tree column data provider for this viewer.
197 protected abstract ITmfTreeColumnDataProvider
getColumnDataProvider();
200 * Sets the tree columns for this tree viewer
203 * The tree column data
205 public void setTreeColumns(final List
<TmfTreeColumnData
> columns
) {
206 boolean hasPercentProvider
= false;
207 for (final TmfTreeColumnData columnData
: columns
) {
208 columnData
.createColumn(fTreeViewer
);
209 hasPercentProvider
|= (columnData
.getPercentageProvider() != null);
212 if (hasPercentProvider
) {
214 * Handler that will draw bar charts in the cell using a percentage
217 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
219 public void handleEvent(Event event
) {
220 if (columns
.get(event
.index
).getPercentageProvider() != null) {
222 double percentage
= columns
.get(event
.index
).getPercentageProvider().getPercentage(event
.item
.getData());
223 if (percentage
== 0) { // No bar to draw
227 if ((event
.detail
& SWT
.SELECTED
) > 0) {
229 * The item is selected. Draw our own background to
230 * avoid overwriting the bar.
232 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
233 event
.detail
&= ~SWT
.SELECTED
;
236 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(event
.index
).getWidth() - 8) * percentage
);
237 int oldAlpha
= event
.gc
.getAlpha();
238 Color oldForeground
= event
.gc
.getForeground();
239 Color oldBackground
= event
.gc
.getBackground();
241 * Draws a transparent gradient rectangle from the color
242 * of foreground and background.
244 event
.gc
.setAlpha(64);
245 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
246 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
247 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
248 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
249 /* Restores old values */
250 event
.gc
.setForeground(oldForeground
);
251 event
.gc
.setBackground(oldBackground
);
252 event
.gc
.setAlpha(oldAlpha
);
253 event
.detail
&= ~SWT
.BACKGROUND
;
261 * Set the label provider that will fill the columns of the tree viewer
263 * @param labelProvider
264 * The label provider to fill the columns
266 protected void setLabelProvider(IBaseLabelProvider labelProvider
) {
267 fTreeViewer
.setLabelProvider(labelProvider
);
271 * Get the tree viewer object
273 * @return The tree viewer object displayed by this viewer
275 protected TreeViewer
getTreeViewer() {
279 // ------------------------------------------------------------------------
281 // ------------------------------------------------------------------------
284 public Control
getControl() {
285 return fTreeViewer
.getControl();
289 public void refresh() {
290 Tree tree
= fTreeViewer
.getTree();
291 tree
.setRedraw(false);
292 fTreeViewer
.refresh();
293 fTreeViewer
.expandAll();
294 tree
.setRedraw(true);
298 public void loadTrace(ITmfTrace trace
) {
299 super.loadTrace(trace
);
300 Thread thread
= new Thread() {
303 initializeDataSource();
304 Display
.getDefault().asyncExec(new Runnable() {
308 updateContent(getWindowStartTime(), getWindowEndTime(), false);
316 // ------------------------------------------------------------------------
318 // ------------------------------------------------------------------------
321 * Add a selection listener to the tree viewer. This will be called when the
322 * selection changes and contain all the selected items.
324 * The selection change listener can be used like this:
327 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
329 * public void selectionChanged(SelectionChangedEvent event) {
330 * if (event.getSelection() instanceof IStructuredSelection) {
331 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
332 * if (selection instanceof ITmfTreeViewerEntry) {
341 * The {@link ISelectionChangedListener}
343 public void addSelectionChangeListener(ISelectionChangedListener listener
) {
344 fTreeViewer
.addSelectionChangedListener(listener
);
348 * Method called when the trace is loaded, to initialize any data once the
349 * trace has been set, but before the first call to update the content of
352 protected void initializeDataSource() {
357 * Clears the current content of the viewer.
359 protected void clearContent() {
360 fTreeViewer
.setInput(null);
364 * Requests an update of the viewer's content in a given time range or
365 * selection time range. An extra parameter defines whether these times
366 * correspond to the selection or the visible range, as the viewer may
367 * update differently in those cases.
370 * The start time of the requested content
372 * The end time of the requested content
374 * <code>true</code> if this time range is for a selection,
375 * <code>false</code> for the visible time range
377 protected void updateContent(final long start
, final long end
, final boolean isSelection
) {
378 Thread thread
= new Thread() {
381 final ITmfTreeViewerEntry rootEntry
= updateElements(start
, end
, isSelection
);
382 /* Set the input in main thread only if it didn't change */
383 if (rootEntry
!= null) {
384 Display
.getDefault().asyncExec(new Runnable() {
387 if (rootEntry
!= fTreeViewer
.getInput()) {
388 fTreeViewer
.setInput(rootEntry
);
390 fTreeViewer
.refresh();
392 // FIXME should add a bit of padding
393 for (TreeColumn column
: fTreeViewer
.getTree().getColumns()) {
405 * Update the entries to the given start/end time. An extra parameter
406 * defines whether these times correspond to the selection or the visible
407 * range, as the viewer may update differently in those cases. This methods
408 * returns a root node that is not meant to be visible. The children of this
409 * 'fake' root node are the first level of entries that will appear in the
410 * tree. If no update is necessary, the method should return
411 * <code>null</code>. To empty the tree, a root node containing an empty
412 * list of children should be returned.
414 * This method is not called in the UI thread when using the default viewer
415 * content update. Resource-intensive calculations here should not block the
419 * The start time of the requested content
421 * The end time of the requested content
423 * <code>true</code> if this time range is for a selection,
424 * <code>false</code> for the visible time range
425 * @return The root entry of the list of entries to display or
426 * <code>null</code> if no update necessary
428 protected abstract ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
);
431 * Get the current input displayed by the viewer
433 * @return The input of the tree viewer, the root entry
435 protected ITmfTreeViewerEntry
getInput() {
436 return (ITmfTreeViewerEntry
) fTreeViewer
.getInput();
439 // ------------------------------------------------------------------------
441 // ------------------------------------------------------------------------
444 * Signal handler for handling of the time synch signal. The times
445 * correspond to the selection by the user, not the visible time range.
448 * The time synch signal {@link TmfTimeSynchSignal}
452 public void selectionRangeUpdated(TmfTimeSynchSignal signal
) {
453 super.selectionRangeUpdated(signal
);
454 if ((signal
.getSource() != this) && (getTrace() != null)) {
455 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
460 * Signal handler for handling of the time range synch signal. This time
461 * range is the visible zone of the view.
464 * The time range synch signal {@link TmfRangeSynchSignal}
468 public void timeRangeUpdated(TmfRangeSynchSignal signal
) {
469 super.timeRangeUpdated(signal
);
470 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);