lttng: Remove capital letter in package name
[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
b97d61f0 13 * Christian Mansky - Add check active / uncheck inactive buttons
837a2f8c
PT
14 *******************************************************************************/
15
2bdf0193 16package org.eclipse.tracecompass.tmf.ui.widgets.timegraph;
837a2f8c
PT
17
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.HashMap;
70e10acc 21import java.util.HashSet;
6ac5a950
AM
22import java.util.List;
23import java.util.Map;
70e10acc 24import java.util.Set;
837a2f8c 25
dfa0ef96 26import org.eclipse.jdt.annotation.NonNull;
6ac5a950 27import org.eclipse.jface.action.Action;
4c9c0c87 28import org.eclipse.jface.viewers.AbstractTreeViewer;
837a2f8c
PT
29import org.eclipse.jface.viewers.ILabelProviderListener;
30import org.eclipse.jface.viewers.ISelectionChangedListener;
31import org.eclipse.jface.viewers.IStructuredSelection;
32import org.eclipse.jface.viewers.ITableLabelProvider;
33import org.eclipse.jface.viewers.ITreeContentProvider;
34import org.eclipse.jface.viewers.ITreeViewerListener;
35import org.eclipse.jface.viewers.SelectionChangedEvent;
36import org.eclipse.jface.viewers.StructuredSelection;
37import org.eclipse.jface.viewers.TreeExpansionEvent;
38import org.eclipse.jface.viewers.TreeViewer;
39import org.eclipse.jface.viewers.Viewer;
6ac5a950 40import org.eclipse.jface.viewers.ViewerFilter;
837a2f8c
PT
41import org.eclipse.swt.SWT;
42import org.eclipse.swt.custom.SashForm;
43import org.eclipse.swt.events.ControlAdapter;
44import org.eclipse.swt.events.ControlEvent;
45import org.eclipse.swt.events.MouseEvent;
46import org.eclipse.swt.events.MouseTrackAdapter;
47import org.eclipse.swt.events.MouseWheelListener;
48import org.eclipse.swt.events.PaintEvent;
49import org.eclipse.swt.events.PaintListener;
50import org.eclipse.swt.events.SelectionAdapter;
51import org.eclipse.swt.events.SelectionEvent;
52import org.eclipse.swt.graphics.Image;
53import org.eclipse.swt.graphics.Point;
588dff10 54import org.eclipse.swt.graphics.Rectangle;
837a2f8c
PT
55import org.eclipse.swt.layout.FillLayout;
56import org.eclipse.swt.widgets.Composite;
57import org.eclipse.swt.widgets.Display;
58import org.eclipse.swt.widgets.Event;
59import org.eclipse.swt.widgets.Listener;
60import org.eclipse.swt.widgets.Slider;
61import org.eclipse.swt.widgets.Tree;
62import org.eclipse.swt.widgets.TreeColumn;
63import org.eclipse.swt.widgets.TreeItem;
2bdf0193
AM
64import org.eclipse.tracecompass.internal.tmf.ui.Activator;
65import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
66import org.eclipse.tracecompass.internal.tmf.ui.Messages;
b97d61f0 67import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ITimeGraphEntryActiveProvider;
2bdf0193
AM
68import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
69import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
70import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
837a2f8c
PT
71
72/**
73 * Time graph "combo" view (with the list/tree on the left and the gantt chart
74 * on the right)
75 *
837a2f8c
PT
76 * @author Patrick Tasse
77 */
78public class TimeGraphCombo extends Composite {
79
80 // ------------------------------------------------------------------------
81 // Constants
82 // ------------------------------------------------------------------------
83
ae09c4ad 84 /** Constant indicating that all levels of the time graph should be expanded */
f4617471
PT
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
4999a196
GB
362 */
363 public TimeGraphCombo(Composite parent, int style, int[] weights) {
837a2f8c
PT
364 super(parent, style);
365 setLayout(new FillLayout());
366
367 final SashForm sash = new SashForm(this, SWT.NONE);
368
369 fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);
4c9c0c87 370 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
837a2f8c
PT
371 final Tree tree = fTreeViewer.getTree();
372 tree.setHeaderVisible(true);
373 tree.setLinesVisible(true);
374
375 fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);
376 fTimeGraphViewer.setItemHeight(getItemHeight(tree));
377 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
378 fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
379 fTimeGraphViewer.setNameWidthPref(0);
380
6ac5a950
AM
381 fFilter = new RawViewerFilter();
382 addFilter(fFilter);
383
384 fFilterDialog = new TimeGraphFilterDialog(getShell());
385
837a2f8c
PT
386 // Feature in Windows. The tree vertical bar reappears when
387 // the control is resized so we need to hide it again.
388 // Bug in Linux. The tree header height is 0 in constructor,
389 // so we need to reset it later when the control is resized.
390 tree.addControlListener(new ControlAdapter() {
f1fae91f 391 private int depth = 0;
837a2f8c
PT
392 @Override
393 public void controlResized(ControlEvent e) {
394 if (depth == 0) {
395 depth++;
396 tree.getVerticalBar().setEnabled(false);
397 // this can trigger controlResized recursively
398 tree.getVerticalBar().setVisible(false);
399 depth--;
400 }
401 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
402 }
403 });
404
405 // ensure synchronization of expanded items between tree and time graph
406 fTreeViewer.addTreeListener(new ITreeViewerListener() {
407 @Override
408 public void treeCollapsed(TreeExpansionEvent event) {
409 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);
588dff10
PT
410 // queue the alignment update because the tree items may only be
411 // actually collapsed after the listeners have been notified
412 fVisibleExpandedItems = null; // invalidate the cache
413 getDisplay().asyncExec(new Runnable() {
414 @Override
415 public void run() {
416 alignTreeItems(true);
417 }});
837a2f8c
PT
418 }
419
420 @Override
421 public void treeExpanded(TreeExpansionEvent event) {
e7708b02
PT
422 ITimeGraphEntry entry = (ITimeGraphEntry) event.getElement();
423 fTimeGraphViewer.setExpandedState(entry, true);
70e10acc 424 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 425 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
426 if (child.hasChildren()) {
427 boolean expanded = expandedElements.contains(child);
428 fTimeGraphViewer.setExpandedState(child, expanded);
429 }
e7708b02 430 }
588dff10
PT
431 // queue the alignment update because the tree items may only be
432 // actually expanded after the listeners have been notified
433 fVisibleExpandedItems = null; // invalidate the cache
837a2f8c
PT
434 getDisplay().asyncExec(new Runnable() {
435 @Override
436 public void run() {
588dff10 437 alignTreeItems(true);
837a2f8c
PT
438 }});
439 }
440 });
441
442 // ensure synchronization of expanded items between tree and time graph
443 fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {
444 @Override
445 public void treeCollapsed(TimeGraphTreeExpansionEvent event) {
446 fTreeViewer.setExpandedState(event.getEntry(), false);
588dff10 447 alignTreeItems(true);
837a2f8c
PT
448 }
449
450 @Override
451 public void treeExpanded(TimeGraphTreeExpansionEvent event) {
e7708b02
PT
452 ITimeGraphEntry entry = event.getEntry();
453 fTreeViewer.setExpandedState(entry, true);
70e10acc 454 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 455 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
456 if (child.hasChildren()) {
457 boolean expanded = expandedElements.contains(child);
458 fTimeGraphViewer.setExpandedState(child, expanded);
459 }
e7708b02 460 }
588dff10 461 alignTreeItems(true);
837a2f8c
PT
462 }
463 });
464
465 // prevent mouse button from selecting a filler tree item
466 tree.addListener(SWT.MouseDown, new Listener() {
467 @Override
468 public void handleEvent(Event event) {
469 TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
470 if (treeItem == null || treeItem.getData() == FILLER) {
471 event.doit = false;
588dff10 472 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
837a2f8c
PT
473 if (treeItems.size() == 0) {
474 fTreeViewer.setSelection(new StructuredSelection());
475 fTimeGraphViewer.setSelection(null);
476 return;
477 }
478 // this prevents from scrolling up when selecting
479 // the partially visible tree item at the bottom
480 tree.select(treeItems.get(treeItems.size() - 1));
481 fTreeViewer.setSelection(new StructuredSelection());
482 fTimeGraphViewer.setSelection(null);
483 }
484 }
485 });
486
487 // prevent mouse wheel from scrolling down into filler tree items
488 tree.addListener(SWT.MouseWheel, new Listener() {
489 @Override
490 public void handleEvent(Event event) {
491 event.doit = false;
492 Slider scrollBar = fTimeGraphViewer.getVerticalBar();
493 fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
588dff10 494 alignTreeItems(false);
837a2f8c
PT
495 }
496 });
497
498 // prevent key stroke from selecting a filler tree item
499 tree.addListener(SWT.KeyDown, new Listener() {
500 @Override
501 public void handleEvent(Event event) {
588dff10 502 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
837a2f8c
PT
503 if (treeItems.size() == 0) {
504 fTreeViewer.setSelection(new StructuredSelection());
505 event.doit = false;
506 return;
507 }
508 if (event.keyCode == SWT.ARROW_DOWN) {
509 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
510 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
511 event.doit = false;
512 } else if (event.keyCode == SWT.PAGE_DOWN) {
513 int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
514 int countPerPage = height / getItemHeight(tree);
515 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
516 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
517 event.doit = false;
518 } else if (event.keyCode == SWT.END) {
519 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
520 event.doit = false;
521 }
837a2f8c
PT
522 if (fTimeGraphViewer.getSelectionIndex() >= 0) {
523 fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
524 } else {
525 fTreeViewer.setSelection(new StructuredSelection());
526 }
588dff10 527 alignTreeItems(false);
837a2f8c
PT
528 }
529 });
530
531 // ensure alignment of top item between tree and time graph
532 fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {
533 @Override
534 public void controlResized(ControlEvent e) {
588dff10 535 alignTreeItems(false);
837a2f8c
PT
536 }
537 });
538
539 // ensure synchronization of selected item between tree and time graph
540 fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
541 @Override
542 public void selectionChanged(SelectionChangedEvent event) {
543 if (fInhibitTreeSelection) {
544 return;
545 }
546 if (event.getSelection() instanceof IStructuredSelection) {
547 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
548 if (selection instanceof ITimeGraphEntry) {
549 fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
550 }
588dff10 551 alignTreeItems(false);
837a2f8c
PT
552 }
553 }
554 });
555
556 // ensure synchronization of selected item between tree and time graph
557 fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {
558 @Override
559 public void selectionChanged(TimeGraphSelectionEvent event) {
560 ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
561 fInhibitTreeSelection = true; // block the tree selection changed listener
562 if (entry != null) {
563 StructuredSelection selection = new StructuredSelection(entry);
564 fTreeViewer.setSelection(selection);
565 } else {
566 fTreeViewer.setSelection(new StructuredSelection());
567 }
568 fInhibitTreeSelection = false;
588dff10 569 alignTreeItems(false);
837a2f8c
PT
570 }
571 });
572
573 // ensure alignment of top item between tree and time graph
574 fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
575 @Override
576 public void widgetSelected(SelectionEvent e) {
588dff10 577 alignTreeItems(false);
837a2f8c
PT
578 }
579 });
580
581 // ensure alignment of top item between tree and time graph
582 fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
583 @Override
584 public void mouseScrolled(MouseEvent e) {
588dff10 585 alignTreeItems(false);
837a2f8c
PT
586 }
587 });
588
589 // ensure the tree has focus control when mouse is over it if the time graph had control
590 fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {
591 @Override
592 public void mouseEnter(MouseEvent e) {
593 if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {
594 fTreeViewer.getControl().setFocus();
595 }
596 }
597 });
598
599 // ensure the time graph has focus control when mouse is over it if the tree had control
600 fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
601 @Override
602 public void mouseEnter(MouseEvent e) {
603 if (fTreeViewer.getControl().isFocusControl()) {
604 fTimeGraphViewer.getTimeGraphControl().setFocus();
605 }
606 }
607 });
608 fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
609 @Override
610 public void mouseEnter(MouseEvent e) {
611 if (fTreeViewer.getControl().isFocusControl()) {
612 fTimeGraphViewer.getTimeGraphControl().setFocus();
613 }
614 }
615 });
616
617 // The filler rows are required to ensure alignment when the tree does not have a
618 // visible horizontal scroll bar. The tree does not allow its top item to be set
619 // to a value that would cause blank space to be drawn at the bottom of the tree.
620 fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
621
4999a196 622 sash.setWeights(weights);
837a2f8c
PT
623 }
624
625 // ------------------------------------------------------------------------
626 // Accessors
627 // ------------------------------------------------------------------------
628
629 /**
630 * Returns this time graph combo's tree viewer.
631 *
632 * @return the tree viewer
633 */
634 public TreeViewer getTreeViewer() {
635 return fTreeViewer;
636 }
637
638 /**
639 * Returns this time graph combo's time graph viewer.
640 *
641 * @return the time graph viewer
642 */
dfa0ef96 643 public @NonNull TimeGraphViewer getTimeGraphViewer() {
837a2f8c
PT
644 return fTimeGraphViewer;
645 }
646
6ac5a950
AM
647 /**
648 * Callback for the show filter action
6ac5a950
AM
649 */
650 public void showFilterDialog() {
4c9c0c87
PT
651 ITimeGraphEntry[] topInput = fTimeGraphViewer.getTimeGraphContentProvider().getElements(fTimeGraphViewer.getInput());
652 if (topInput != null) {
653 List<? extends ITimeGraphEntry> allElements = listAllInputs(Arrays.asList(topInput));
654 fFilterDialog.setInput(fTimeGraphViewer.getInput());
6ac5a950
AM
655 fFilterDialog.setTitle(Messages.TmfTimeFilterDialog_WINDOW_TITLE);
656 fFilterDialog.setMessage(Messages.TmfTimeFilterDialog_MESSAGE);
8f28f9d8 657 fFilterDialog.setExpandedElements(allElements.toArray());
f8840316 658 if (fFilter.getFiltered() != null) {
507b1336 659 ArrayList<? extends ITimeGraphEntry> nonFilteredElements = new ArrayList<>(allElements);
f8840316
FR
660 nonFilteredElements.removeAll(fFilter.getFiltered());
661 fFilterDialog.setInitialElementSelections(nonFilteredElements);
8f28f9d8
PT
662 } else {
663 fFilterDialog.setInitialElementSelections(allElements);
664 }
6ac5a950
AM
665 fFilterDialog.create();
666 fFilterDialog.open();
667 // Process selected elements
668 if (fFilterDialog.getResult() != null) {
669 fInhibitTreeSelection = true;
8f28f9d8 670 if (fFilterDialog.getResult().length != allElements.size()) {
f8840316
FR
671 ArrayList<Object> filteredElements = new ArrayList<Object>(allElements);
672 filteredElements.removeAll(Arrays.asList(fFilterDialog.getResult()));
673 fFilter.setFiltered(filteredElements);
8f28f9d8 674 } else {
f8840316 675 fFilter.setFiltered(null);
8f28f9d8 676 }
6ac5a950
AM
677 fTreeViewer.refresh();
678 fTreeViewer.expandAll();
679 fTimeGraphViewer.refresh();
ece2fc5f 680 fTimeGraphViewer.expandAll();
6ac5a950 681 fInhibitTreeSelection = false;
588dff10 682 alignTreeItems(true);
40b7b614 683 // Reset selection
6ac5a950 684 if (fFilterDialog.getResult().length > 0) {
40b7b614 685 setSelection(null);
6ac5a950
AM
686 }
687 }
688 }
689 }
690
691 /**
692 * Get the show filter action.
693 *
694 * @return The Action object
6ac5a950
AM
695 */
696 public Action getShowFilterAction() {
697 if (showFilterAction == null) {
698 // showFilter
699 showFilterAction = new Action() {
700 @Override
701 public void run() {
702 showFilterDialog();
703 }
704 };
705 showFilterAction.setText(Messages.TmfTimeGraphCombo_FilterActionNameText);
706 showFilterAction.setToolTipText(Messages.TmfTimeGraphCombo_FilterActionToolTipText);
707 // TODO find a nice, distinctive icon
708 showFilterAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS));
709 }
710
711 return showFilterAction;
712 }
713
837a2f8c
PT
714 // ------------------------------------------------------------------------
715 // Control
716 // ------------------------------------------------------------------------
717
837a2f8c
PT
718 @Override
719 public void redraw() {
720 fTimeGraphViewer.getControl().redraw();
721 super.redraw();
722 }
723
724 // ------------------------------------------------------------------------
725 // Operations
726 // ------------------------------------------------------------------------
727
728 /**
729 * Sets the tree content provider used by this time graph combo.
730 *
731 * @param contentProvider the tree content provider
732 */
733 public void setTreeContentProvider(ITreeContentProvider contentProvider) {
734 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
735 }
736
737 /**
738 * Sets the tree label provider used by this time graph combo.
739 *
740 * @param labelProvider the tree label provider
741 */
742 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
743 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
744 }
745
6ac5a950
AM
746 /**
747 * Sets the tree content provider used by the filter dialog
748 *
749 * @param contentProvider the tree content provider
6ac5a950
AM
750 */
751 public void setFilterContentProvider(ITreeContentProvider contentProvider) {
752 fFilterDialog.setContentProvider(contentProvider);
753 }
754
755 /**
756 * Sets the tree label provider used by the filter dialog
757 *
758 * @param labelProvider the tree label provider
6ac5a950
AM
759 */
760 public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
761 fFilterDialog.setLabelProvider(labelProvider);
762 }
763
b97d61f0
CM
764 /**
765 * Adds a "check active" button used by the filter dialog
766 *
767 * @param activeProvider
768 * Additional button info specific to a certain view.
769 * @since 1.0
770 */
771 public void addTimeGraphFilterCheckActiveButton(ITimeGraphEntryActiveProvider activeProvider) {
772 fFilterDialog.addTimeGraphFilterCheckActiveButton(activeProvider);
773 }
774
775 /**
776 * Adds an "uncheck inactive" button used by the filter dialog
777 *
778 * @param inactiveProvider
779 * Additional button info specific to a certain view.
780 * @since 1.0
781 */
782 public void addTimeGraphFilterUncheckInactiveButton(ITimeGraphEntryActiveProvider inactiveProvider) {
783 fFilterDialog.addTimeGraphFilterUncheckInactiveButton(inactiveProvider);
784 }
785
837a2f8c
PT
786 /**
787 * Sets the tree columns for this time graph combo.
788 *
789 * @param columnNames the tree column names
790 */
791 public void setTreeColumns(String[] columnNames) {
792 final Tree tree = fTreeViewer.getTree();
793 for (String columnName : columnNames) {
794 TreeColumn column = new TreeColumn(tree, SWT.LEFT);
795 column.setText(columnName);
796 column.pack();
797 }
798 }
799
6ac5a950
AM
800 /**
801 * Sets the tree columns for this time graph combo's filter dialog.
802 *
803 * @param columnNames the tree column names
6ac5a950
AM
804 */
805 public void setFilterColumns(String[] columnNames) {
806 fFilterDialog.setColumnNames(columnNames);
807 }
808
837a2f8c 809 /**
4c9c0c87
PT
810 * Sets the time graph content provider used by this time graph combo.
811 *
812 * @param timeGraphContentProvider
813 * the time graph content provider
4c9c0c87
PT
814 */
815 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider) {
816 fTimeGraphViewer.setTimeGraphContentProvider(timeGraphContentProvider);
817 }
818
819 /**
820 * Sets the time graph presentation provider used by this time graph combo.
837a2f8c
PT
821 *
822 * @param timeGraphProvider the time graph provider
823 */
824 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
825 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
826 }
827
828 /**
829 * Sets or clears the input for this time graph combo.
837a2f8c
PT
830 *
831 * @param input the input of this time graph combo, or <code>null</code> if none
832 */
4c9c0c87 833 public void setInput(Object input) {
f8840316 834 fFilter.setFiltered(null);
837a2f8c
PT
835 fInhibitTreeSelection = true;
836 fTreeViewer.setInput(input);
837 for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
838 listenerWrapper.selection = null;
839 }
840 fInhibitTreeSelection = false;
837a2f8c
PT
841 fTreeViewer.getTree().getVerticalBar().setEnabled(false);
842 fTreeViewer.getTree().getVerticalBar().setVisible(false);
843 fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
844 fTimeGraphViewer.setInput(input);
588dff10
PT
845 // queue the alignment update because in Linux the item bounds are not
846 // set properly until the tree has been painted at least once
847 fVisibleExpandedItems = null; // invalidate the cache
848 getDisplay().asyncExec(new Runnable() {
849 @Override
850 public void run() {
851 alignTreeItems(true);
852 }});
837a2f8c
PT
853 }
854
4c9c0c87
PT
855 /**
856 * Gets the input for this time graph combo.
857 *
858 * @return The input of this time graph combo, or <code>null</code> if none
4c9c0c87
PT
859 */
860 public Object getInput() {
861 return fTreeViewer.getInput();
862 }
863
bec1f1ac
GB
864 /**
865 * Sets or clears the list of links to display on this combo
866 *
867 * @param links the links to display in this time graph combo
bec1f1ac
GB
868 */
869 public void setLinks(List<ILinkEvent> links) {
870 fTimeGraphViewer.setLinks(links);
871 }
872
6ac5a950
AM
873 /**
874 * @param filter The filter object to be attached to the view
6ac5a950
AM
875 */
876 public void addFilter(ViewerFilter filter) {
877 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
878 fTreeViewer.addFilter(wrapper);
879 fTimeGraphViewer.addFilter(wrapper);
880 fViewerFilterMap.put(filter, wrapper);
588dff10 881 alignTreeItems(true);
6ac5a950
AM
882 }
883
884 /**
885 * @param filter The filter object to be removed from the view
6ac5a950
AM
886 */
887 public void removeFilter(ViewerFilter filter) {
888 ViewerFilter wrapper = fViewerFilterMap.get(filter);
889 fTreeViewer.removeFilter(wrapper);
890 fTimeGraphViewer.removeFilter(wrapper);
891 fViewerFilterMap.remove(filter);
588dff10 892 alignTreeItems(true);
6ac5a950
AM
893 }
894
837a2f8c
PT
895 /**
896 * Refreshes this time graph completely with information freshly obtained from its model.
897 */
898 public void refresh() {
899 fInhibitTreeSelection = true;
4c9c0c87 900 Tree tree = fTreeViewer.getTree();
ece2fc5f
PT
901 try {
902 tree.setRedraw(false);
903 fTreeViewer.refresh();
904 fTreeViewer.expandToLevel(fTreeViewer.getAutoExpandLevel());
905 } finally {
906 tree.setRedraw(true);
907 }
837a2f8c 908 fTimeGraphViewer.refresh();
588dff10 909 alignTreeItems(true);
837a2f8c
PT
910 fInhibitTreeSelection = false;
911 }
912
913 /**
914 * Adds a listener for selection changes in this time graph combo.
915 *
916 * @param listener a selection listener
917 */
918 public void addSelectionListener(ITimeGraphSelectionListener listener) {
919 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
920 fTreeViewer.addSelectionChangedListener(listenerWrapper);
921 fSelectionListenerMap.put(listener, listenerWrapper);
922 fTimeGraphViewer.addSelectionListener(listenerWrapper);
923 }
924
925 /**
926 * Removes the given selection listener from this time graph combo.
927 *
928 * @param listener a selection changed listener
929 */
930 public void removeSelectionListener(ITimeGraphSelectionListener listener) {
931 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
932 fTreeViewer.removeSelectionChangedListener(listenerWrapper);
933 fTimeGraphViewer.removeSelectionListener(listenerWrapper);
934 }
935
936 /**
937 * Sets the current selection for this time graph combo.
938 *
939 * @param selection the new selection
940 */
941 public void setSelection(ITimeGraphEntry selection) {
942 fTimeGraphViewer.setSelection(selection);
943 fInhibitTreeSelection = true; // block the tree selection changed listener
944 if (selection != null) {
945 StructuredSelection structuredSelection = new StructuredSelection(selection);
946 fTreeViewer.setSelection(structuredSelection);
947 } else {
948 fTreeViewer.setSelection(new StructuredSelection());
949 }
950 fInhibitTreeSelection = false;
588dff10 951 alignTreeItems(false);
837a2f8c
PT
952 }
953
f4617471
PT
954 /**
955 * Sets the auto-expand level to be used when the input of the viewer is set
956 * using {@link #setInput(Object)}. The value 0 means that there is no
957 * auto-expand; 1 means that top-level elements are expanded, but not their
958 * children; 2 means that top-level elements are expanded, and their
959 * children, but not grand-children; and so on.
960 * <p>
961 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
962 * </p>
963 * @param level
964 * non-negative level, or <code>ALL_LEVELS</code> to expand all
965 * levels of the tree
f4617471
PT
966 */
967 public void setAutoExpandLevel(int level) {
968 fTimeGraphViewer.setAutoExpandLevel(level);
969 if (level <= 0) {
970 fTreeViewer.setAutoExpandLevel(level);
971 } else {
972 fTreeViewer.setAutoExpandLevel(level + 1);
973 }
974 }
975
976 /**
977 * Returns the auto-expand level.
978 *
979 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
980 * the tree are expanded automatically
981 * @see #setAutoExpandLevel
f4617471
PT
982 */
983 public int getAutoExpandLevel() {
984 return fTimeGraphViewer.getAutoExpandLevel();
985 }
986
837a2f8c
PT
987 /**
988 * Set the expanded state of an entry
989 *
990 * @param entry
991 * The entry to expand/collapse
992 * @param expanded
993 * True for expanded, false for collapsed
837a2f8c
PT
994 */
995 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
996 fTimeGraphViewer.setExpandedState(entry, expanded);
997 fTreeViewer.setExpandedState(entry, expanded);
588dff10 998 alignTreeItems(true);
837a2f8c
PT
999 }
1000
1001 /**
1002 * Collapses all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
1003 */
1004 public void collapseAll() {
1005 fTimeGraphViewer.collapseAll();
1006 fTreeViewer.collapseAll();
588dff10 1007 alignTreeItems(true);
837a2f8c
PT
1008 }
1009
1010 /**
1011 * Expands all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
1012 */
1013 public void expandAll() {
1014 fTimeGraphViewer.expandAll();
1015 fTreeViewer.expandAll();
588dff10 1016 alignTreeItems(true);
837a2f8c
PT
1017 }
1018
1019 // ------------------------------------------------------------------------
1020 // Internal
1021 // ------------------------------------------------------------------------
1022
588dff10
PT
1023 private List<TreeItem> getVisibleExpandedItems(Tree tree, boolean refresh) {
1024 if (fVisibleExpandedItems == null || refresh) {
1025 ArrayList<TreeItem> items = new ArrayList<>();
1026 for (TreeItem item : tree.getItems()) {
1027 if (item.getData() == FILLER) {
1028 break;
1029 }
1030 items.add(item);
1031 if (item.getExpanded()) {
1032 addVisibleExpandedItems(items, item);
1033 }
837a2f8c 1034 }
588dff10 1035 fVisibleExpandedItems = items;
837a2f8c 1036 }
588dff10 1037 return fVisibleExpandedItems;
837a2f8c
PT
1038 }
1039
588dff10 1040 private void addVisibleExpandedItems(List<TreeItem> items, TreeItem treeItem) {
837a2f8c
PT
1041 for (TreeItem item : treeItem.getItems()) {
1042 items.add(item);
1043 if (item.getExpanded()) {
588dff10 1044 addVisibleExpandedItems(items, item);
837a2f8c
PT
1045 }
1046 }
837a2f8c
PT
1047 }
1048
6ac5a950
AM
1049 /**
1050 * Explores the list of top-level inputs and returns all the inputs
1051 *
1052 * @param inputs The top-level inputs
1053 * @return All the inputs
1054 */
1055 private List<? extends ITimeGraphEntry> listAllInputs(List<? extends ITimeGraphEntry> inputs) {
507b1336 1056 ArrayList<ITimeGraphEntry> items = new ArrayList<>();
6ac5a950
AM
1057 for (ITimeGraphEntry entry : inputs) {
1058 items.add(entry);
1059 if (entry.hasChildren()) {
1060 items.addAll(listAllInputs(entry.getChildren()));
1061 }
1062 }
1063 return items;
1064 }
1065
837a2f8c
PT
1066 private int getItemHeight(final Tree tree) {
1067 /*
1068 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1069 */
1070 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1071 if (fLinuxItemHeight != 0) {
1072 return fLinuxItemHeight;
1073 }
588dff10 1074 List<TreeItem> treeItems = getVisibleExpandedItems(tree, true);
837a2f8c
PT
1075 if (treeItems.size() > 1) {
1076 final TreeItem treeItem0 = treeItems.get(0);
1077 final TreeItem treeItem1 = treeItems.get(1);
1078 PaintListener paintListener = new PaintListener() {
1079 @Override
1080 public void paintControl(PaintEvent e) {
1081 tree.removePaintListener(this);
1082 int y0 = treeItem0.getBounds().y;
1083 int y1 = treeItem1.getBounds().y;
1084 int itemHeight = y1 - y0;
1085 if (itemHeight > 0) {
1086 fLinuxItemHeight = itemHeight;
1087 fTimeGraphViewer.setItemHeight(itemHeight);
1088 }
1089 }
1090 };
1091 tree.addPaintListener(paintListener);
1092 }
1093 } else {
1094 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
1095 }
1096 return tree.getItemHeight();
1097 }
1098
588dff10
PT
1099 private void alignTreeItems(boolean refreshExpandedItems) {
1100 // align the tree top item with the time graph top item
1101 Tree tree = fTreeViewer.getTree();
1102 List<TreeItem> treeItems = getVisibleExpandedItems(tree, refreshExpandedItems);
1103 int topIndex = fTimeGraphViewer.getTopIndex();
1104 if (topIndex >= treeItems.size()) {
1105 return;
1106 }
1107 TreeItem item = treeItems.get(topIndex);
1108 tree.setTopItem(item);
1109
1110 // ensure the time graph item heights are equal to the tree item heights
1111 int treeHeight = fTreeViewer.getTree().getBounds().height;
1112 int index = topIndex;
1113 Rectangle bounds = item.getBounds();
1114 while (index < treeItems.size() - 1) {
1115 if (bounds.y > treeHeight) {
1116 break;
1117 }
1118 /*
1119 * Bug in Linux. The method getBounds doesn't always return the correct height.
1120 * Use the difference of y position between items to calculate the height.
1121 */
1122 TreeItem nextItem = treeItems.get(index + 1);
1123 Rectangle nextBounds = nextItem.getBounds();
1124 Integer itemHeight = nextBounds.y - bounds.y;
1125 if (itemHeight > 0 && !itemHeight.equals(item.getData(ITEM_HEIGHT))) {
1126 ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
1127 if (fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight)) {
1128 item.setData(ITEM_HEIGHT, itemHeight);
1129 }
1130 }
1131 index++;
1132 item = nextItem;
1133 bounds = nextBounds;
1134 }
1135 }
1136
837a2f8c 1137}
This page took 0.154489 seconds and 5 git commands to generate.