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