LTTNG: Bug 436857: Keep process selection in CPU Usage tree viewer
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / tree / AbstractTmfTreeViewer.java
CommitLineData
c0188b25
GB
1/*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
3 *
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
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13package org.eclipse.linuxtools.tmf.ui.viewers.tree;
14
15import java.util.List;
16
e9a0d1cb 17import org.eclipse.jdt.annotation.NonNull;
c0188b25
GB
18import org.eclipse.jface.viewers.AbstractTreeViewer;
19import org.eclipse.jface.viewers.IBaseLabelProvider;
20import org.eclipse.jface.viewers.ILabelProviderListener;
21import org.eclipse.jface.viewers.ISelectionChangedListener;
e9a0d1cb 22import org.eclipse.jface.viewers.IStructuredSelection;
c0188b25
GB
23import org.eclipse.jface.viewers.ITableColorProvider;
24import org.eclipse.jface.viewers.ITableFontProvider;
25import org.eclipse.jface.viewers.ITableLabelProvider;
26import org.eclipse.jface.viewers.ITreeContentProvider;
e9a0d1cb 27import org.eclipse.jface.viewers.StructuredSelection;
c0188b25
GB
28import org.eclipse.jface.viewers.TreeViewer;
29import org.eclipse.jface.viewers.Viewer;
30import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal;
31import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
32import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
33import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
34import org.eclipse.linuxtools.tmf.ui.viewers.TmfTimeViewer;
35import org.eclipse.swt.SWT;
36import org.eclipse.swt.graphics.Color;
37import org.eclipse.swt.graphics.Font;
38import org.eclipse.swt.graphics.Image;
39import org.eclipse.swt.widgets.Composite;
40import org.eclipse.swt.widgets.Control;
41import org.eclipse.swt.widgets.Display;
42import org.eclipse.swt.widgets.Event;
43import org.eclipse.swt.widgets.Listener;
44import org.eclipse.swt.widgets.Tree;
45import org.eclipse.swt.widgets.TreeColumn;
46
47/**
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
53 * not.
54 *
55 * @author Geneviève Bastien
56 * @since 3.0
57 */
58public abstract class AbstractTmfTreeViewer extends TmfTimeViewer {
59
60 private final TreeViewer fTreeViewer;
61
62 // ------------------------------------------------------------------------
63 // Internal classes
64 // ------------------------------------------------------------------------
65
66 /* The elements of the tree viewer are of type ITmfTreeViewerEntry */
67 private class TreeContentProvider implements ITreeContentProvider {
68
69 @Override
70 public void dispose() {
71 }
72
73 @Override
74 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
75 }
76
77 @Override
78 public Object[] getElements(Object inputElement) {
59c8ff34
MAL
79 if (inputElement instanceof ITmfTreeViewerEntry) {
80 return ((ITmfTreeViewerEntry) inputElement).getChildren().toArray(new ITmfTreeViewerEntry[0]);
c0188b25
GB
81 }
82 return new ITmfTreeViewerEntry[0];
83 }
84
85 @Override
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()]);
90 }
91
92 @Override
93 public Object getParent(Object element) {
94 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
95 return entry.getParent();
96 }
97
98 @Override
99 public boolean hasChildren(Object element) {
100 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
101 return entry.hasChildren();
102 }
103
104 }
105
106 /**
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.
111 */
112 protected static class TreeLabelProvider implements ITableLabelProvider, ITableFontProvider, ITableColorProvider {
113
114 @Override
115 public void addListener(ILabelProviderListener listener) {
116 }
117
118 @Override
119 public void dispose() {
120 }
121
122 @Override
123 public boolean isLabelProperty(Object element, String property) {
124 return false;
125 }
126
127 @Override
128 public void removeListener(ILabelProviderListener listener) {
129 }
130
131 @Override
132 public Image getColumnImage(Object element, int columnIndex) {
133 return null;
134 }
135
136 @Override
137 public String getColumnText(Object element, int columnIndex) {
138 if ((element instanceof ITmfTreeViewerEntry) && (columnIndex == 0)) {
139 ITmfTreeViewerEntry entry = (ITmfTreeViewerEntry) element;
140 return entry.getName();
141 }
142 return new String();
143 }
144
145 @Override
146 public Color getForeground(Object element, int columnIndex) {
147 return Display.getCurrent().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
148 }
149
150 @Override
151 public Color getBackground(Object element, int columnIndex) {
152 return Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
153 }
154
155 @Override
156 public Font getFont(Object element, int columnIndex) {
157 return null;
158 }
159
160 }
161
162 // ------------------------------------------------------------------------
163 // Constructors and initialization methods
164 // ------------------------------------------------------------------------
165
166 /**
167 * Constructor
168 *
169 * @param parent
170 * The parent composite that holds this viewer
171 * @param allowMultiSelect
172 * Whether multiple selections are allowed
173 */
174 public AbstractTmfTreeViewer(Composite parent, boolean allowMultiSelect) {
175 super(parent);
176
177 int flags = SWT.FULL_SELECTION | SWT.H_SCROLL;
178 if (allowMultiSelect) {
179 flags |= SWT.MULTI;
180 }
181
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);
192 }
193
194 /**
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.
197 *
198 * @return The tree column data provider for this viewer.
199 */
200 protected abstract ITmfTreeColumnDataProvider getColumnDataProvider();
201
202 /**
203 * Sets the tree columns for this tree viewer
204 *
205 * @param columns
206 * The tree column data
207 */
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);
213 }
214
215 if (hasPercentProvider) {
216 /*
217 * Handler that will draw bar charts in the cell using a percentage
218 * value.
219 */
220 fTreeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
221 @Override
222 public void handleEvent(Event event) {
223 if (columns.get(event.index).getPercentageProvider() != null) {
224
225 double percentage = columns.get(event.index).getPercentageProvider().getPercentage(event.item.getData());
226 if (percentage == 0) { // No bar to draw
227 return;
228 }
229
230 if ((event.detail & SWT.SELECTED) > 0) {
231 /*
232 * The item is selected. Draw our own background to
233 * avoid overwriting the bar.
234 */
235 event.gc.fillRectangle(event.x, event.y, event.width, event.height);
236 event.detail &= ~SWT.SELECTED;
237 }
238
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();
243 /*
244 * Draws a transparent gradient rectangle from the color
245 * of foreground and background.
246 */
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;
257 }
258 }
259 });
260 }
261 }
262
263 /**
264 * Set the label provider that will fill the columns of the tree viewer
265 *
266 * @param labelProvider
267 * The label provider to fill the columns
268 */
269 protected void setLabelProvider(IBaseLabelProvider labelProvider) {
270 fTreeViewer.setLabelProvider(labelProvider);
271 }
272
273 /**
274 * Get the tree viewer object
275 *
276 * @return The tree viewer object displayed by this viewer
277 */
278 protected TreeViewer getTreeViewer() {
279 return fTreeViewer;
280 }
281
282 // ------------------------------------------------------------------------
283 // ITmfViewer
284 // ------------------------------------------------------------------------
285
286 @Override
287 public Control getControl() {
288 return fTreeViewer.getControl();
289 }
290
291 @Override
292 public void refresh() {
293 Tree tree = fTreeViewer.getTree();
294 tree.setRedraw(false);
295 fTreeViewer.refresh();
296 fTreeViewer.expandAll();
297 tree.setRedraw(true);
298 }
299
300 @Override
301 public void loadTrace(ITmfTrace trace) {
302 super.loadTrace(trace);
303 Thread thread = new Thread() {
304 @Override
305 public void run() {
306 initializeDataSource();
307 Display.getDefault().asyncExec(new Runnable() {
308 @Override
309 public void run() {
310 clearContent();
311 updateContent(getWindowStartTime(), getWindowEndTime(), false);
312 }
313 });
314 }
315 };
316 thread.start();
317 }
318
319 // ------------------------------------------------------------------------
320 // Operations
321 // ------------------------------------------------------------------------
322
e9a0d1cb
GB
323 /**
324 * Set the currently selected items in the treeviewer
325 *
326 * @param selection
327 * The list of selected items
328 * @since 3.1
329 */
330 public void setSelection(@NonNull List<ITmfTreeViewerEntry> selection) {
331 IStructuredSelection sel = new StructuredSelection(selection);
332 fTreeViewer.setSelection(sel, true);
333 }
334
c0188b25
GB
335 /**
336 * Add a selection listener to the tree viewer. This will be called when the
337 * selection changes and contain all the selected items.
338 *
339 * The selection change listener can be used like this:
340 *
341 * <pre>
342 * getTreeViewer().addSelectionChangeListener(new ISelectionChangedListener() {
343 * &#064;Override
344 * public void selectionChanged(SelectionChangedEvent event) {
345 * if (event.getSelection() instanceof IStructuredSelection) {
346 * Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
347 * if (selection instanceof ITmfTreeViewerEntry) {
348 * // Do something
349 * }
350 * }
351 * }
352 * });
353 * </pre>
354 *
355 * @param listener
356 * The {@link ISelectionChangedListener}
357 */
358 public void addSelectionChangeListener(ISelectionChangedListener listener) {
359 fTreeViewer.addSelectionChangedListener(listener);
360 }
361
362 /**
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
365 * the viewer.
366 */
367 protected void initializeDataSource() {
368
369 }
370
371 /**
372 * Clears the current content of the viewer.
373 */
374 protected void clearContent() {
375 fTreeViewer.setInput(null);
376 }
377
e9a0d1cb
GB
378 /**
379 * Method called after the content has been updated and the new input has
380 * been set on the tree.
381 *
382 * @param rootEntry
383 * The new input of this viewer, or null if none
384 * @since 3.1
385 */
386 protected void contentChanged(ITmfTreeViewerEntry rootEntry) {
387
388 }
389
c0188b25
GB
390 /**
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.
395 *
396 * @param start
397 * The start time of the requested content
398 * @param end
399 * The end time of the requested content
400 * @param isSelection
401 * <code>true</code> if this time range is for a selection,
402 * <code>false</code> for the visible time range
403 */
404 protected void updateContent(final long start, final long end, final boolean isSelection) {
405 Thread thread = new Thread() {
406 @Override
407 public void run() {
59c8ff34 408 final ITmfTreeViewerEntry rootEntry = updateElements(start, end, isSelection);
c0188b25 409 /* Set the input in main thread only if it didn't change */
59c8ff34 410 if (rootEntry != null) {
c0188b25
GB
411 Display.getDefault().asyncExec(new Runnable() {
412 @Override
413 public void run() {
59c8ff34
MAL
414 if (rootEntry != fTreeViewer.getInput()) {
415 fTreeViewer.setInput(rootEntry);
e9a0d1cb 416 contentChanged(rootEntry);
c0188b25
GB
417 } else {
418 fTreeViewer.refresh();
419 }
420 // FIXME should add a bit of padding
421 for (TreeColumn column : fTreeViewer.getTree().getColumns()) {
422 column.pack();
423 }
424 }
425 });
426 }
427 }
428 };
429 thread.start();
430 }
431
432 /**
433 * Update the entries to the given start/end time. An extra parameter
434 * defines whether these times correspond to the selection or the visible
59c8ff34
MAL
435 * range, as the viewer may update differently in those cases. This methods
436 * returns a root node that is not meant to be visible. The children of this
437 * 'fake' root node are the first level of entries that will appear in the
438 * tree. If no update is necessary, the method should return
439 * <code>null</code>. To empty the tree, a root node containing an empty
440 * list of children should be returned.
c0188b25
GB
441 *
442 * This method is not called in the UI thread when using the default viewer
443 * content update. Resource-intensive calculations here should not block the
444 * UI.
445 *
446 * @param start
447 * The start time of the requested content
448 * @param end
449 * The end time of the requested content
450 * @param isSelection
451 * <code>true</code> if this time range is for a selection,
452 * <code>false</code> for the visible time range
59c8ff34
MAL
453 * @return The root entry of the list of entries to display or
454 * <code>null</code> if no update necessary
c0188b25 455 */
59c8ff34 456 protected abstract ITmfTreeViewerEntry updateElements(long start, long end, boolean isSelection);
c0188b25
GB
457
458 /**
459 * Get the current input displayed by the viewer
460 *
59c8ff34 461 * @return The input of the tree viewer, the root entry
c0188b25 462 */
59c8ff34
MAL
463 protected ITmfTreeViewerEntry getInput() {
464 return (ITmfTreeViewerEntry) fTreeViewer.getInput();
c0188b25
GB
465 }
466
467 // ------------------------------------------------------------------------
468 // Signal Handler
469 // ------------------------------------------------------------------------
470
471 /**
472 * Signal handler for handling of the time synch signal. The times
473 * correspond to the selection by the user, not the visible time range.
474 *
475 * @param signal
476 * The time synch signal {@link TmfTimeSynchSignal}
477 */
478 @Override
479 @TmfSignalHandler
480 public void selectionRangeUpdated(TmfTimeSynchSignal signal) {
481 super.selectionRangeUpdated(signal);
482 if ((signal.getSource() != this) && (getTrace() != null)) {
483 updateContent(this.getSelectionBeginTime(), this.getSelectionEndTime(), true);
484 }
485 }
486
487 /**
488 * Signal handler for handling of the time range synch signal. This time
489 * range is the visible zone of the view.
490 *
491 * @param signal
492 * The time range synch signal {@link TmfRangeSynchSignal}
493 */
494 @Override
495 @TmfSignalHandler
496 public void timeRangeUpdated(TmfRangeSynchSignal signal) {
497 super.timeRangeUpdated(signal);
498 updateContent(this.getWindowStartTime(), this.getWindowEndTime(), false);
499 }
500
d8adbbe6
BH
501 @Override
502 public void reset() {
503 super.reset();
504 clearContent();
505 }
506
c0188b25 507}
This page took 0.047849 seconds and 5 git commands to generate.