TMF: Add some @NonNull annotations to the AbstractTimeGraphView
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / TimeGraphCombo.java
CommitLineData
837a2f8c 1/*******************************************************************************
4c9c0c87 2 * Copyright (c) 2012, 2014 Ericsson, others
837a2f8c
PT
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 * Patrick Tasse - Initial API and implementation
f8840316 11 * François Rajotte - Filter implementation
bec1f1ac 12 * Geneviève Bastien - Add event links between entries
837a2f8c
PT
13 *******************************************************************************/
14
2bdf0193 15package org.eclipse.tracecompass.tmf.ui.widgets.timegraph;
837a2f8c
PT
16
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.HashMap;
70e10acc 20import java.util.HashSet;
6ac5a950
AM
21import java.util.List;
22import java.util.Map;
70e10acc 23import java.util.Set;
837a2f8c 24
dfa0ef96 25import org.eclipse.jdt.annotation.NonNull;
6ac5a950 26import org.eclipse.jface.action.Action;
4c9c0c87 27import org.eclipse.jface.viewers.AbstractTreeViewer;
837a2f8c
PT
28import org.eclipse.jface.viewers.ILabelProviderListener;
29import org.eclipse.jface.viewers.ISelectionChangedListener;
30import org.eclipse.jface.viewers.IStructuredSelection;
31import org.eclipse.jface.viewers.ITableLabelProvider;
32import org.eclipse.jface.viewers.ITreeContentProvider;
33import org.eclipse.jface.viewers.ITreeViewerListener;
34import org.eclipse.jface.viewers.SelectionChangedEvent;
35import org.eclipse.jface.viewers.StructuredSelection;
36import org.eclipse.jface.viewers.TreeExpansionEvent;
37import org.eclipse.jface.viewers.TreeViewer;
38import org.eclipse.jface.viewers.Viewer;
6ac5a950 39import org.eclipse.jface.viewers.ViewerFilter;
837a2f8c
PT
40import org.eclipse.swt.SWT;
41import org.eclipse.swt.custom.SashForm;
42import org.eclipse.swt.events.ControlAdapter;
43import org.eclipse.swt.events.ControlEvent;
44import org.eclipse.swt.events.MouseEvent;
45import org.eclipse.swt.events.MouseTrackAdapter;
46import org.eclipse.swt.events.MouseWheelListener;
47import org.eclipse.swt.events.PaintEvent;
48import org.eclipse.swt.events.PaintListener;
49import org.eclipse.swt.events.SelectionAdapter;
50import org.eclipse.swt.events.SelectionEvent;
51import org.eclipse.swt.graphics.Image;
52import org.eclipse.swt.graphics.Point;
588dff10 53import org.eclipse.swt.graphics.Rectangle;
837a2f8c
PT
54import org.eclipse.swt.layout.FillLayout;
55import org.eclipse.swt.widgets.Composite;
56import org.eclipse.swt.widgets.Display;
57import org.eclipse.swt.widgets.Event;
58import org.eclipse.swt.widgets.Listener;
59import org.eclipse.swt.widgets.Slider;
60import org.eclipse.swt.widgets.Tree;
61import org.eclipse.swt.widgets.TreeColumn;
62import org.eclipse.swt.widgets.TreeItem;
2bdf0193
AM
63import org.eclipse.tracecompass.internal.tmf.ui.Activator;
64import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
65import org.eclipse.tracecompass.internal.tmf.ui.Messages;
66import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
67import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
68import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
837a2f8c
PT
69
70/**
71 * Time graph "combo" view (with the list/tree on the left and the gantt chart
72 * on the right)
73 *
74 * @version 1.0
75 * @author Patrick Tasse
76 */
77public class TimeGraphCombo extends Composite {
78
79 // ------------------------------------------------------------------------
80 // Constants
81 // ------------------------------------------------------------------------
82
f4617471
PT
83 /** Constant indicating that all levels of the time graph should be expanded
84 * @since 3.1 */
85 public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
86
837a2f8c
PT
87 private static final Object FILLER = new Object();
88
c004295c
PT
89 private static final String ITEM_HEIGHT = "$height$"; //$NON-NLS-1$
90
837a2f8c
PT
91 // ------------------------------------------------------------------------
92 // Fields
93 // ------------------------------------------------------------------------
94
4999a196 95 /** The tree viewer */
837a2f8c
PT
96 private TreeViewer fTreeViewer;
97
4999a196 98 /** The time viewer */
dfa0ef96 99 private @NonNull TimeGraphViewer fTimeGraphViewer;
837a2f8c 100
4999a196 101 /** The selection listener map */
507b1336 102 private final Map<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<>();
837a2f8c 103
4999a196 104 /** The map of viewer filters */
507b1336 105 private final Map<ViewerFilter, ViewerFilter> fViewerFilterMap = new HashMap<>();
6ac5a950 106
4999a196
GB
107 /**
108 * Flag to block the tree selection changed listener when triggered by the
109 * time graph combo
110 */
837a2f8c
PT
111 private boolean fInhibitTreeSelection = false;
112
4999a196 113 /** Number of filler rows used by the tree content provider */
837a2f8c
PT
114 private int fNumFillerRows;
115
4999a196 116 /** Calculated item height for Linux workaround */
837a2f8c
PT
117 private int fLinuxItemHeight = 0;
118
4999a196 119 /** The button that opens the filter dialog */
6ac5a950
AM
120 private Action showFilterAction;
121
4999a196 122 /** The filter dialog */
6ac5a950
AM
123 private TimeGraphFilterDialog fFilterDialog;
124
4999a196 125 /** The filter generated from the filter dialog */
6ac5a950
AM
126 private RawViewerFilter fFilter;
127
4999a196
GB
128 /** Default weight of each part of the sash */
129 private static final int[] DEFAULT_WEIGHTS = { 1, 1 };
130
588dff10
PT
131 /** List of all expanded items whose parents are also expanded */
132 private List<TreeItem> fVisibleExpandedItems = null;
133
837a2f8c
PT
134 // ------------------------------------------------------------------------
135 // Classes
136 // ------------------------------------------------------------------------
137
138 /**
139 * The TreeContentProviderWrapper is used to insert filler items after
140 * the elements of the tree's real content provider.
141 */
142 private class TreeContentProviderWrapper implements ITreeContentProvider {
143 private final ITreeContentProvider contentProvider;
144
145 public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {
146 this.contentProvider = contentProvider;
147 }
148
149 @Override
150 public void dispose() {
151 contentProvider.dispose();
152 }
153
154 @Override
155 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
156 contentProvider.inputChanged(viewer, oldInput, newInput);
157 }
158
159 @Override
160 public Object[] getElements(Object inputElement) {
161 Object[] elements = contentProvider.getElements(inputElement);
162 // add filler elements to ensure alignment with time analysis viewer
f1fae91f 163 Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, Object[].class);
837a2f8c
PT
164 for (int i = 0; i < fNumFillerRows; i++) {
165 oElements[elements.length + i] = FILLER;
166 }
167 return oElements;
168 }
169
170 @Override
171 public Object[] getChildren(Object parentElement) {
172 if (parentElement instanceof ITimeGraphEntry) {
173 return contentProvider.getChildren(parentElement);
174 }
175 return new Object[0];
176 }
177
178 @Override
179 public Object getParent(Object element) {
180 if (element instanceof ITimeGraphEntry) {
181 return contentProvider.getParent(element);
182 }
183 return null;
184 }
185
186 @Override
187 public boolean hasChildren(Object element) {
188 if (element instanceof ITimeGraphEntry) {
189 return contentProvider.hasChildren(element);
190 }
191 return false;
192 }
193 }
194
195 /**
196 * The TreeLabelProviderWrapper is used to intercept the filler items
197 * from the calls to the tree's real label provider.
198 */
199 private class TreeLabelProviderWrapper implements ITableLabelProvider {
200 private final ITableLabelProvider labelProvider;
201
202 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
203 this.labelProvider = labelProvider;
204 }
205
206 @Override
207 public void addListener(ILabelProviderListener listener) {
208 labelProvider.addListener(listener);
209 }
210
211 @Override
212 public void dispose() {
213 labelProvider.dispose();
214 }
215
216 @Override
217 public boolean isLabelProperty(Object element, String property) {
218 if (element instanceof ITimeGraphEntry) {
219 return labelProvider.isLabelProperty(element, property);
220 }
221 return false;
222 }
223
224 @Override
225 public void removeListener(ILabelProviderListener listener) {
226 labelProvider.removeListener(listener);
227 }
228
229 @Override
230 public Image getColumnImage(Object element, int columnIndex) {
231 if (element instanceof ITimeGraphEntry) {
232 return labelProvider.getColumnImage(element, columnIndex);
233 }
234 return null;
235 }
236
237 @Override
238 public String getColumnText(Object element, int columnIndex) {
239 if (element instanceof ITimeGraphEntry) {
240 return labelProvider.getColumnText(element, columnIndex);
241 }
242 return null;
243 }
244
245 }
246
247 /**
248 * The SelectionListenerWrapper is used to intercept the filler items from
249 * the time graph combo's real selection listener, and to prevent double
250 * notifications from being sent when selection changes in both tree and
251 * time graph at the same time.
252 */
253 private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {
254 private final ITimeGraphSelectionListener listener;
255 private ITimeGraphEntry selection = null;
256
257 public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {
258 this.listener = listener;
259 }
260
261 @Override
262 public void selectionChanged(SelectionChangedEvent event) {
263 if (fInhibitTreeSelection) {
264 return;
265 }
266 Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
267 if (element instanceof ITimeGraphEntry) {
268 ITimeGraphEntry entry = (ITimeGraphEntry) element;
269 if (entry != selection) {
270 selection = entry;
271 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
272 }
273 }
274 }
275
276 @Override
277 public void selectionChanged(TimeGraphSelectionEvent event) {
278 ITimeGraphEntry entry = event.getSelection();
279 if (entry != selection) {
280 selection = entry;
281 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
282 }
283 }
284 }
285
6ac5a950
AM
286 /**
287 * The ViewerFilterWrapper is used to intercept the filler items from
288 * the time graph combo's real ViewerFilters. These filler items should
289 * always be visible.
290 */
291 private class ViewerFilterWrapper extends ViewerFilter {
292
f1fae91f 293 private ViewerFilter fWrappedFilter;
6ac5a950
AM
294
295 ViewerFilterWrapper(ViewerFilter filter) {
296 super();
297 this.fWrappedFilter = filter;
298 }
299
300 @Override
301 public boolean select(Viewer viewer, Object parentElement, Object element) {
302 if (element instanceof ITimeGraphEntry) {
303 return fWrappedFilter.select(viewer, parentElement, element);
304 }
305 return true;
306 }
307
308 }
309
310 /**
f8840316
FR
311 * This filter simply keeps a list of elements that should be filtered out.
312 * All the other elements will be shown.
8f28f9d8 313 * By default and when the list is set to null, all elements are shown.
6ac5a950
AM
314 */
315 private class RawViewerFilter extends ViewerFilter {
316
f8840316 317 private List<Object> fFiltered = null;
6ac5a950 318
f8840316
FR
319 public void setFiltered(List<Object> objects) {
320 fFiltered = objects;
6ac5a950
AM
321 }
322
f8840316
FR
323 public List<Object> getFiltered() {
324 return fFiltered;
6ac5a950
AM
325 }
326
327 @Override
328 public boolean select(Viewer viewer, Object parentElement, Object element) {
f8840316 329 if (fFiltered == null) {
8f28f9d8
PT
330 return true;
331 }
f8840316 332 return !fFiltered.contains(element);
6ac5a950
AM
333 }
334 }
335
837a2f8c
PT
336 // ------------------------------------------------------------------------
337 // Constructors
338 // ------------------------------------------------------------------------
339
340 /**
341 * Constructs a new instance of this class given its parent
342 * and a style value describing its behavior and appearance.
343 *
344 * @param parent a widget which will be the parent of the new instance (cannot be null)
345 * @param style the style of widget to construct
346 */
347 public TimeGraphCombo(Composite parent, int style) {
4999a196
GB
348 this(parent, style, DEFAULT_WEIGHTS);
349 }
350
351 /**
352 * Constructs a new instance of this class given its parent and a style
353 * value describing its behavior and appearance.
354 *
355 * @param parent
356 * a widget which will be the parent of the new instance (cannot
357 * be null)
358 * @param style
359 * the style of widget to construct
360 * @param weights
361 * The relative weights of each side of the sash form
362 * @since 2.1
363 */
364 public TimeGraphCombo(Composite parent, int style, int[] weights) {
837a2f8c
PT
365 super(parent, style);
366 setLayout(new FillLayout());
367
368 final SashForm sash = new SashForm(this, SWT.NONE);
369
370 fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);
4c9c0c87 371 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
837a2f8c
PT
372 final Tree tree = fTreeViewer.getTree();
373 tree.setHeaderVisible(true);
374 tree.setLinesVisible(true);
375
376 fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);
377 fTimeGraphViewer.setItemHeight(getItemHeight(tree));
378 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
379 fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
380 fTimeGraphViewer.setNameWidthPref(0);
381
6ac5a950
AM
382 fFilter = new RawViewerFilter();
383 addFilter(fFilter);
384
385 fFilterDialog = new TimeGraphFilterDialog(getShell());
386
837a2f8c
PT
387 // Feature in Windows. The tree vertical bar reappears when
388 // the control is resized so we need to hide it again.
389 // Bug in Linux. The tree header height is 0 in constructor,
390 // so we need to reset it later when the control is resized.
391 tree.addControlListener(new ControlAdapter() {
f1fae91f 392 private int depth = 0;
837a2f8c
PT
393 @Override
394 public void controlResized(ControlEvent e) {
395 if (depth == 0) {
396 depth++;
397 tree.getVerticalBar().setEnabled(false);
398 // this can trigger controlResized recursively
399 tree.getVerticalBar().setVisible(false);
400 depth--;
401 }
402 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
403 }
404 });
405
406 // ensure synchronization of expanded items between tree and time graph
407 fTreeViewer.addTreeListener(new ITreeViewerListener() {
408 @Override
409 public void treeCollapsed(TreeExpansionEvent event) {
410 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);
588dff10
PT
411 // queue the alignment update because the tree items may only be
412 // actually collapsed after the listeners have been notified
413 fVisibleExpandedItems = null; // invalidate the cache
414 getDisplay().asyncExec(new Runnable() {
415 @Override
416 public void run() {
417 alignTreeItems(true);
418 }});
837a2f8c
PT
419 }
420
421 @Override
422 public void treeExpanded(TreeExpansionEvent event) {
e7708b02
PT
423 ITimeGraphEntry entry = (ITimeGraphEntry) event.getElement();
424 fTimeGraphViewer.setExpandedState(entry, true);
70e10acc 425 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 426 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
427 if (child.hasChildren()) {
428 boolean expanded = expandedElements.contains(child);
429 fTimeGraphViewer.setExpandedState(child, expanded);
430 }
e7708b02 431 }
588dff10
PT
432 // queue the alignment update because the tree items may only be
433 // actually expanded after the listeners have been notified
434 fVisibleExpandedItems = null; // invalidate the cache
837a2f8c
PT
435 getDisplay().asyncExec(new Runnable() {
436 @Override
437 public void run() {
588dff10 438 alignTreeItems(true);
837a2f8c
PT
439 }});
440 }
441 });
442
443 // ensure synchronization of expanded items between tree and time graph
444 fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {
445 @Override
446 public void treeCollapsed(TimeGraphTreeExpansionEvent event) {
447 fTreeViewer.setExpandedState(event.getEntry(), false);
588dff10 448 alignTreeItems(true);
837a2f8c
PT
449 }
450
451 @Override
452 public void treeExpanded(TimeGraphTreeExpansionEvent event) {
e7708b02
PT
453 ITimeGraphEntry entry = event.getEntry();
454 fTreeViewer.setExpandedState(entry, true);
70e10acc 455 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 456 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
457 if (child.hasChildren()) {
458 boolean expanded = expandedElements.contains(child);
459 fTimeGraphViewer.setExpandedState(child, expanded);
460 }
e7708b02 461 }
588dff10 462 alignTreeItems(true);
837a2f8c
PT
463 }
464 });
465
466 // prevent mouse button from selecting a filler tree item
467 tree.addListener(SWT.MouseDown, new Listener() {
468 @Override
469 public void handleEvent(Event event) {
470 TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
471 if (treeItem == null || treeItem.getData() == FILLER) {
472 event.doit = false;
588dff10 473 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
837a2f8c
PT
474 if (treeItems.size() == 0) {
475 fTreeViewer.setSelection(new StructuredSelection());
476 fTimeGraphViewer.setSelection(null);
477 return;
478 }
479 // this prevents from scrolling up when selecting
480 // the partially visible tree item at the bottom
481 tree.select(treeItems.get(treeItems.size() - 1));
482 fTreeViewer.setSelection(new StructuredSelection());
483 fTimeGraphViewer.setSelection(null);
484 }
485 }
486 });
487
488 // prevent mouse wheel from scrolling down into filler tree items
489 tree.addListener(SWT.MouseWheel, new Listener() {
490 @Override
491 public void handleEvent(Event event) {
492 event.doit = false;
493 Slider scrollBar = fTimeGraphViewer.getVerticalBar();
494 fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
588dff10 495 alignTreeItems(false);
837a2f8c
PT
496 }
497 });
498
499 // prevent key stroke from selecting a filler tree item
500 tree.addListener(SWT.KeyDown, new Listener() {
501 @Override
502 public void handleEvent(Event event) {
588dff10 503 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
837a2f8c
PT
504 if (treeItems.size() == 0) {
505 fTreeViewer.setSelection(new StructuredSelection());
506 event.doit = false;
507 return;
508 }
509 if (event.keyCode == SWT.ARROW_DOWN) {
510 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
511 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
512 event.doit = false;
513 } else if (event.keyCode == SWT.PAGE_DOWN) {
514 int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
515 int countPerPage = height / getItemHeight(tree);
516 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
517 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
518 event.doit = false;
519 } else if (event.keyCode == SWT.END) {
520 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
521 event.doit = false;
522 }
837a2f8c
PT
523 if (fTimeGraphViewer.getSelectionIndex() >= 0) {
524 fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
525 } else {
526 fTreeViewer.setSelection(new StructuredSelection());
527 }
588dff10 528 alignTreeItems(false);
837a2f8c
PT
529 }
530 });
531
532 // ensure alignment of top item between tree and time graph
533 fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {
534 @Override
535 public void controlResized(ControlEvent e) {
588dff10 536 alignTreeItems(false);
837a2f8c
PT
537 }
538 });
539
540 // ensure synchronization of selected item between tree and time graph
541 fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
542 @Override
543 public void selectionChanged(SelectionChangedEvent event) {
544 if (fInhibitTreeSelection) {
545 return;
546 }
547 if (event.getSelection() instanceof IStructuredSelection) {
548 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
549 if (selection instanceof ITimeGraphEntry) {
550 fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
551 }
588dff10 552 alignTreeItems(false);
837a2f8c
PT
553 }
554 }
555 });
556
557 // ensure synchronization of selected item between tree and time graph
558 fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {
559 @Override
560 public void selectionChanged(TimeGraphSelectionEvent event) {
561 ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
562 fInhibitTreeSelection = true; // block the tree selection changed listener
563 if (entry != null) {
564 StructuredSelection selection = new StructuredSelection(entry);
565 fTreeViewer.setSelection(selection);
566 } else {
567 fTreeViewer.setSelection(new StructuredSelection());
568 }
569 fInhibitTreeSelection = false;
588dff10 570 alignTreeItems(false);
837a2f8c
PT
571 }
572 });
573
574 // ensure alignment of top item between tree and time graph
575 fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
576 @Override
577 public void widgetSelected(SelectionEvent e) {
588dff10 578 alignTreeItems(false);
837a2f8c
PT
579 }
580 });
581
582 // ensure alignment of top item between tree and time graph
583 fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
584 @Override
585 public void mouseScrolled(MouseEvent e) {
588dff10 586 alignTreeItems(false);
837a2f8c
PT
587 }
588 });
589
590 // ensure the tree has focus control when mouse is over it if the time graph had control
591 fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {
592 @Override
593 public void mouseEnter(MouseEvent e) {
594 if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {
595 fTreeViewer.getControl().setFocus();
596 }
597 }
598 });
599
600 // ensure the time graph has focus control when mouse is over it if the tree had control
601 fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
602 @Override
603 public void mouseEnter(MouseEvent e) {
604 if (fTreeViewer.getControl().isFocusControl()) {
605 fTimeGraphViewer.getTimeGraphControl().setFocus();
606 }
607 }
608 });
609 fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
610 @Override
611 public void mouseEnter(MouseEvent e) {
612 if (fTreeViewer.getControl().isFocusControl()) {
613 fTimeGraphViewer.getTimeGraphControl().setFocus();
614 }
615 }
616 });
617
618 // The filler rows are required to ensure alignment when the tree does not have a
619 // visible horizontal scroll bar. The tree does not allow its top item to be set
620 // to a value that would cause blank space to be drawn at the bottom of the tree.
621 fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
622
4999a196 623 sash.setWeights(weights);
837a2f8c
PT
624 }
625
626 // ------------------------------------------------------------------------
627 // Accessors
628 // ------------------------------------------------------------------------
629
630 /**
631 * Returns this time graph combo's tree viewer.
632 *
633 * @return the tree viewer
634 */
635 public TreeViewer getTreeViewer() {
636 return fTreeViewer;
637 }
638
639 /**
640 * Returns this time graph combo's time graph viewer.
641 *
642 * @return the time graph viewer
643 */
dfa0ef96 644 public @NonNull TimeGraphViewer getTimeGraphViewer() {
837a2f8c
PT
645 return fTimeGraphViewer;
646 }
647
6ac5a950
AM
648 /**
649 * Callback for the show filter action
650 *
651 * @since 2.0
652 */
653 public void showFilterDialog() {
4c9c0c87
PT
654 ITimeGraphEntry[] topInput = fTimeGraphViewer.getTimeGraphContentProvider().getElements(fTimeGraphViewer.getInput());
655 if (topInput != null) {
656 List<? extends ITimeGraphEntry> allElements = listAllInputs(Arrays.asList(topInput));
657 fFilterDialog.setInput(fTimeGraphViewer.getInput());
6ac5a950
AM
658 fFilterDialog.setTitle(Messages.TmfTimeFilterDialog_WINDOW_TITLE);
659 fFilterDialog.setMessage(Messages.TmfTimeFilterDialog_MESSAGE);
8f28f9d8 660 fFilterDialog.setExpandedElements(allElements.toArray());
f8840316 661 if (fFilter.getFiltered() != null) {
507b1336 662 ArrayList<? extends ITimeGraphEntry> nonFilteredElements = new ArrayList<>(allElements);
f8840316
FR
663 nonFilteredElements.removeAll(fFilter.getFiltered());
664 fFilterDialog.setInitialElementSelections(nonFilteredElements);
8f28f9d8
PT
665 } else {
666 fFilterDialog.setInitialElementSelections(allElements);
667 }
6ac5a950
AM
668 fFilterDialog.create();
669 fFilterDialog.open();
670 // Process selected elements
671 if (fFilterDialog.getResult() != null) {
672 fInhibitTreeSelection = true;
8f28f9d8 673 if (fFilterDialog.getResult().length != allElements.size()) {
f8840316
FR
674 ArrayList<Object> filteredElements = new ArrayList<Object>(allElements);
675 filteredElements.removeAll(Arrays.asList(fFilterDialog.getResult()));
676 fFilter.setFiltered(filteredElements);
8f28f9d8 677 } else {
f8840316 678 fFilter.setFiltered(null);
8f28f9d8 679 }
6ac5a950
AM
680 fTreeViewer.refresh();
681 fTreeViewer.expandAll();
682 fTimeGraphViewer.refresh();
683 fInhibitTreeSelection = false;
588dff10 684 alignTreeItems(true);
40b7b614 685 // Reset selection
6ac5a950 686 if (fFilterDialog.getResult().length > 0) {
40b7b614 687 setSelection(null);
6ac5a950
AM
688 }
689 }
690 }
691 }
692
693 /**
694 * Get the show filter action.
695 *
696 * @return The Action object
697 * @since 2.0
698 */
699 public Action getShowFilterAction() {
700 if (showFilterAction == null) {
701 // showFilter
702 showFilterAction = new Action() {
703 @Override
704 public void run() {
705 showFilterDialog();
706 }
707 };
708 showFilterAction.setText(Messages.TmfTimeGraphCombo_FilterActionNameText);
709 showFilterAction.setToolTipText(Messages.TmfTimeGraphCombo_FilterActionToolTipText);
710 // TODO find a nice, distinctive icon
711 showFilterAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS));
712 }
713
714 return showFilterAction;
715 }
716
837a2f8c
PT
717 // ------------------------------------------------------------------------
718 // Control
719 // ------------------------------------------------------------------------
720
837a2f8c
PT
721 @Override
722 public void redraw() {
723 fTimeGraphViewer.getControl().redraw();
724 super.redraw();
725 }
726
727 // ------------------------------------------------------------------------
728 // Operations
729 // ------------------------------------------------------------------------
730
731 /**
732 * Sets the tree content provider used by this time graph combo.
733 *
734 * @param contentProvider the tree content provider
735 */
736 public void setTreeContentProvider(ITreeContentProvider contentProvider) {
737 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
738 }
739
740 /**
741 * Sets the tree label provider used by this time graph combo.
742 *
743 * @param labelProvider the tree label provider
744 */
745 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
746 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
747 }
748
6ac5a950
AM
749 /**
750 * Sets the tree content provider used by the filter dialog
751 *
752 * @param contentProvider the tree content provider
753 * @since 2.0
754 */
755 public void setFilterContentProvider(ITreeContentProvider contentProvider) {
756 fFilterDialog.setContentProvider(contentProvider);
757 }
758
759 /**
760 * Sets the tree label provider used by the filter dialog
761 *
762 * @param labelProvider the tree label provider
763 * @since 2.0
764 */
765 public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
766 fFilterDialog.setLabelProvider(labelProvider);
767 }
768
837a2f8c
PT
769 /**
770 * Sets the tree columns for this time graph combo.
771 *
772 * @param columnNames the tree column names
773 */
774 public void setTreeColumns(String[] columnNames) {
775 final Tree tree = fTreeViewer.getTree();
776 for (String columnName : columnNames) {
777 TreeColumn column = new TreeColumn(tree, SWT.LEFT);
778 column.setText(columnName);
779 column.pack();
780 }
781 }
782
6ac5a950
AM
783 /**
784 * Sets the tree columns for this time graph combo's filter dialog.
785 *
786 * @param columnNames the tree column names
787 * @since 2.0
788 */
789 public void setFilterColumns(String[] columnNames) {
790 fFilterDialog.setColumnNames(columnNames);
791 }
792
837a2f8c 793 /**
4c9c0c87
PT
794 * Sets the time graph content provider used by this time graph combo.
795 *
796 * @param timeGraphContentProvider
797 * the time graph content provider
798 *
799 * @since 3.0
800 */
801 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider) {
802 fTimeGraphViewer.setTimeGraphContentProvider(timeGraphContentProvider);
803 }
804
805 /**
806 * Sets the time graph presentation provider used by this time graph combo.
837a2f8c
PT
807 *
808 * @param timeGraphProvider the time graph provider
809 */
810 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
811 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
812 }
813
814 /**
815 * Sets or clears the input for this time graph combo.
837a2f8c
PT
816 *
817 * @param input the input of this time graph combo, or <code>null</code> if none
4c9c0c87
PT
818 *
819 * @since 3.0
837a2f8c 820 */
4c9c0c87 821 public void setInput(Object input) {
f8840316 822 fFilter.setFiltered(null);
837a2f8c
PT
823 fInhibitTreeSelection = true;
824 fTreeViewer.setInput(input);
825 for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
826 listenerWrapper.selection = null;
827 }
828 fInhibitTreeSelection = false;
837a2f8c
PT
829 fTreeViewer.getTree().getVerticalBar().setEnabled(false);
830 fTreeViewer.getTree().getVerticalBar().setVisible(false);
831 fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
832 fTimeGraphViewer.setInput(input);
588dff10
PT
833 // queue the alignment update because in Linux the item bounds are not
834 // set properly until the tree has been painted at least once
835 fVisibleExpandedItems = null; // invalidate the cache
836 getDisplay().asyncExec(new Runnable() {
837 @Override
838 public void run() {
839 alignTreeItems(true);
840 }});
837a2f8c
PT
841 }
842
4c9c0c87
PT
843 /**
844 * Gets the input for this time graph combo.
845 *
846 * @return The input of this time graph combo, or <code>null</code> if none
847 *
848 * @since 3.0
849 */
850 public Object getInput() {
851 return fTreeViewer.getInput();
852 }
853
bec1f1ac
GB
854 /**
855 * Sets or clears the list of links to display on this combo
856 *
857 * @param links the links to display in this time graph combo
858 * @since 2.1
859 */
860 public void setLinks(List<ILinkEvent> links) {
861 fTimeGraphViewer.setLinks(links);
862 }
863
6ac5a950
AM
864 /**
865 * @param filter The filter object to be attached to the view
866 * @since 2.0
867 */
868 public void addFilter(ViewerFilter filter) {
869 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
870 fTreeViewer.addFilter(wrapper);
871 fTimeGraphViewer.addFilter(wrapper);
872 fViewerFilterMap.put(filter, wrapper);
588dff10 873 alignTreeItems(true);
6ac5a950
AM
874 }
875
876 /**
877 * @param filter The filter object to be removed from the view
878 * @since 2.0
879 */
880 public void removeFilter(ViewerFilter filter) {
881 ViewerFilter wrapper = fViewerFilterMap.get(filter);
882 fTreeViewer.removeFilter(wrapper);
883 fTimeGraphViewer.removeFilter(wrapper);
884 fViewerFilterMap.remove(filter);
588dff10 885 alignTreeItems(true);
6ac5a950
AM
886 }
887
837a2f8c
PT
888 /**
889 * Refreshes this time graph completely with information freshly obtained from its model.
890 */
891 public void refresh() {
892 fInhibitTreeSelection = true;
4c9c0c87
PT
893 Tree tree = fTreeViewer.getTree();
894 tree.setRedraw(false);
837a2f8c 895 fTreeViewer.refresh();
4c9c0c87
PT
896 fTreeViewer.expandAll();
897 tree.setRedraw(true);
837a2f8c 898 fTimeGraphViewer.refresh();
588dff10 899 alignTreeItems(true);
837a2f8c
PT
900 fInhibitTreeSelection = false;
901 }
902
903 /**
904 * Adds a listener for selection changes in this time graph combo.
905 *
906 * @param listener a selection listener
907 */
908 public void addSelectionListener(ITimeGraphSelectionListener listener) {
909 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
910 fTreeViewer.addSelectionChangedListener(listenerWrapper);
911 fSelectionListenerMap.put(listener, listenerWrapper);
912 fTimeGraphViewer.addSelectionListener(listenerWrapper);
913 }
914
915 /**
916 * Removes the given selection listener from this time graph combo.
917 *
918 * @param listener a selection changed listener
919 */
920 public void removeSelectionListener(ITimeGraphSelectionListener listener) {
921 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
922 fTreeViewer.removeSelectionChangedListener(listenerWrapper);
923 fTimeGraphViewer.removeSelectionListener(listenerWrapper);
924 }
925
926 /**
927 * Sets the current selection for this time graph combo.
928 *
929 * @param selection the new selection
930 */
931 public void setSelection(ITimeGraphEntry selection) {
932 fTimeGraphViewer.setSelection(selection);
933 fInhibitTreeSelection = true; // block the tree selection changed listener
934 if (selection != null) {
935 StructuredSelection structuredSelection = new StructuredSelection(selection);
936 fTreeViewer.setSelection(structuredSelection);
937 } else {
938 fTreeViewer.setSelection(new StructuredSelection());
939 }
940 fInhibitTreeSelection = false;
588dff10 941 alignTreeItems(false);
837a2f8c
PT
942 }
943
f4617471
PT
944 /**
945 * Sets the auto-expand level to be used when the input of the viewer is set
946 * using {@link #setInput(Object)}. The value 0 means that there is no
947 * auto-expand; 1 means that top-level elements are expanded, but not their
948 * children; 2 means that top-level elements are expanded, and their
949 * children, but not grand-children; and so on.
950 * <p>
951 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
952 * </p>
953 * @param level
954 * non-negative level, or <code>ALL_LEVELS</code> to expand all
955 * levels of the tree
956 * @since 3.1
957 */
958 public void setAutoExpandLevel(int level) {
959 fTimeGraphViewer.setAutoExpandLevel(level);
960 if (level <= 0) {
961 fTreeViewer.setAutoExpandLevel(level);
962 } else {
963 fTreeViewer.setAutoExpandLevel(level + 1);
964 }
965 }
966
967 /**
968 * Returns the auto-expand level.
969 *
970 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
971 * the tree are expanded automatically
972 * @see #setAutoExpandLevel
973 * @since 3.1
974 */
975 public int getAutoExpandLevel() {
976 return fTimeGraphViewer.getAutoExpandLevel();
977 }
978
837a2f8c
PT
979 /**
980 * Set the expanded state of an entry
981 *
982 * @param entry
983 * The entry to expand/collapse
984 * @param expanded
985 * True for expanded, false for collapsed
986 *
987 * @since 2.0
988 */
989 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
990 fTimeGraphViewer.setExpandedState(entry, expanded);
991 fTreeViewer.setExpandedState(entry, expanded);
588dff10 992 alignTreeItems(true);
837a2f8c
PT
993 }
994
995 /**
996 * Collapses all nodes of the viewer's tree, starting with the root.
997 *
998 * @since 2.0
999 */
1000 public void collapseAll() {
1001 fTimeGraphViewer.collapseAll();
1002 fTreeViewer.collapseAll();
588dff10 1003 alignTreeItems(true);
837a2f8c
PT
1004 }
1005
1006 /**
1007 * Expands all nodes of the viewer's tree, starting with the root.
1008 *
1009 * @since 2.0
1010 */
1011 public void expandAll() {
1012 fTimeGraphViewer.expandAll();
1013 fTreeViewer.expandAll();
588dff10 1014 alignTreeItems(true);
837a2f8c
PT
1015 }
1016
1017 // ------------------------------------------------------------------------
1018 // Internal
1019 // ------------------------------------------------------------------------
1020
588dff10
PT
1021 private List<TreeItem> getVisibleExpandedItems(Tree tree, boolean refresh) {
1022 if (fVisibleExpandedItems == null || refresh) {
1023 ArrayList<TreeItem> items = new ArrayList<>();
1024 for (TreeItem item : tree.getItems()) {
1025 if (item.getData() == FILLER) {
1026 break;
1027 }
1028 items.add(item);
1029 if (item.getExpanded()) {
1030 addVisibleExpandedItems(items, item);
1031 }
837a2f8c 1032 }
588dff10 1033 fVisibleExpandedItems = items;
837a2f8c 1034 }
588dff10 1035 return fVisibleExpandedItems;
837a2f8c
PT
1036 }
1037
588dff10 1038 private void addVisibleExpandedItems(List<TreeItem> items, TreeItem treeItem) {
837a2f8c
PT
1039 for (TreeItem item : treeItem.getItems()) {
1040 items.add(item);
1041 if (item.getExpanded()) {
588dff10 1042 addVisibleExpandedItems(items, item);
837a2f8c
PT
1043 }
1044 }
837a2f8c
PT
1045 }
1046
6ac5a950
AM
1047 /**
1048 * Explores the list of top-level inputs and returns all the inputs
1049 *
1050 * @param inputs The top-level inputs
1051 * @return All the inputs
1052 */
1053 private List<? extends ITimeGraphEntry> listAllInputs(List<? extends ITimeGraphEntry> inputs) {
507b1336 1054 ArrayList<ITimeGraphEntry> items = new ArrayList<>();
6ac5a950
AM
1055 for (ITimeGraphEntry entry : inputs) {
1056 items.add(entry);
1057 if (entry.hasChildren()) {
1058 items.addAll(listAllInputs(entry.getChildren()));
1059 }
1060 }
1061 return items;
1062 }
1063
837a2f8c
PT
1064 private int getItemHeight(final Tree tree) {
1065 /*
1066 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1067 */
1068 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1069 if (fLinuxItemHeight != 0) {
1070 return fLinuxItemHeight;
1071 }
588dff10 1072 List<TreeItem> treeItems = getVisibleExpandedItems(tree, true);
837a2f8c
PT
1073 if (treeItems.size() > 1) {
1074 final TreeItem treeItem0 = treeItems.get(0);
1075 final TreeItem treeItem1 = treeItems.get(1);
1076 PaintListener paintListener = new PaintListener() {
1077 @Override
1078 public void paintControl(PaintEvent e) {
1079 tree.removePaintListener(this);
1080 int y0 = treeItem0.getBounds().y;
1081 int y1 = treeItem1.getBounds().y;
1082 int itemHeight = y1 - y0;
1083 if (itemHeight > 0) {
1084 fLinuxItemHeight = itemHeight;
1085 fTimeGraphViewer.setItemHeight(itemHeight);
1086 }
1087 }
1088 };
1089 tree.addPaintListener(paintListener);
1090 }
1091 } else {
1092 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
1093 }
1094 return tree.getItemHeight();
1095 }
1096
588dff10
PT
1097 private void alignTreeItems(boolean refreshExpandedItems) {
1098 // align the tree top item with the time graph top item
1099 Tree tree = fTreeViewer.getTree();
1100 List<TreeItem> treeItems = getVisibleExpandedItems(tree, refreshExpandedItems);
1101 int topIndex = fTimeGraphViewer.getTopIndex();
1102 if (topIndex >= treeItems.size()) {
1103 return;
1104 }
1105 TreeItem item = treeItems.get(topIndex);
1106 tree.setTopItem(item);
1107
1108 // ensure the time graph item heights are equal to the tree item heights
1109 int treeHeight = fTreeViewer.getTree().getBounds().height;
1110 int index = topIndex;
1111 Rectangle bounds = item.getBounds();
1112 while (index < treeItems.size() - 1) {
1113 if (bounds.y > treeHeight) {
1114 break;
1115 }
1116 /*
1117 * Bug in Linux. The method getBounds doesn't always return the correct height.
1118 * Use the difference of y position between items to calculate the height.
1119 */
1120 TreeItem nextItem = treeItems.get(index + 1);
1121 Rectangle nextBounds = nextItem.getBounds();
1122 Integer itemHeight = nextBounds.y - bounds.y;
1123 if (itemHeight > 0 && !itemHeight.equals(item.getData(ITEM_HEIGHT))) {
1124 ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
1125 if (fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight)) {
1126 item.setData(ITEM_HEIGHT, itemHeight);
1127 }
1128 }
1129 index++;
1130 item = nextItem;
1131 bounds = nextBounds;
1132 }
1133 }
1134
837a2f8c 1135}
This page took 0.109518 seconds and 5 git commands to generate.