Move alltests plugin to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / linuxtools / 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
15package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;
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
6ac5a950 25import org.eclipse.jface.action.Action;
4c9c0c87 26import org.eclipse.jface.viewers.AbstractTreeViewer;
837a2f8c
PT
27import org.eclipse.jface.viewers.ILabelProviderListener;
28import org.eclipse.jface.viewers.ISelectionChangedListener;
29import org.eclipse.jface.viewers.IStructuredSelection;
30import org.eclipse.jface.viewers.ITableLabelProvider;
31import org.eclipse.jface.viewers.ITreeContentProvider;
32import org.eclipse.jface.viewers.ITreeViewerListener;
33import org.eclipse.jface.viewers.SelectionChangedEvent;
34import org.eclipse.jface.viewers.StructuredSelection;
35import org.eclipse.jface.viewers.TreeExpansionEvent;
36import org.eclipse.jface.viewers.TreeViewer;
37import org.eclipse.jface.viewers.Viewer;
6ac5a950
AM
38import org.eclipse.jface.viewers.ViewerFilter;
39import org.eclipse.linuxtools.internal.tmf.ui.Activator;
40import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants;
41import org.eclipse.linuxtools.internal.tmf.ui.Messages;
42import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
bec1f1ac 43import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ILinkEvent;
837a2f8c
PT
44import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
45import org.eclipse.swt.SWT;
46import org.eclipse.swt.custom.SashForm;
47import org.eclipse.swt.events.ControlAdapter;
48import org.eclipse.swt.events.ControlEvent;
49import org.eclipse.swt.events.MouseEvent;
50import org.eclipse.swt.events.MouseTrackAdapter;
51import org.eclipse.swt.events.MouseWheelListener;
52import org.eclipse.swt.events.PaintEvent;
53import org.eclipse.swt.events.PaintListener;
54import org.eclipse.swt.events.SelectionAdapter;
55import org.eclipse.swt.events.SelectionEvent;
56import org.eclipse.swt.graphics.Image;
57import org.eclipse.swt.graphics.Point;
588dff10 58import org.eclipse.swt.graphics.Rectangle;
837a2f8c
PT
59import org.eclipse.swt.layout.FillLayout;
60import org.eclipse.swt.widgets.Composite;
61import org.eclipse.swt.widgets.Display;
62import org.eclipse.swt.widgets.Event;
63import org.eclipse.swt.widgets.Listener;
64import org.eclipse.swt.widgets.Slider;
65import org.eclipse.swt.widgets.Tree;
66import org.eclipse.swt.widgets.TreeColumn;
67import org.eclipse.swt.widgets.TreeItem;
68
69/**
70 * Time graph "combo" view (with the list/tree on the left and the gantt chart
71 * on the right)
72 *
73 * @version 1.0
74 * @author Patrick Tasse
75 */
76public class TimeGraphCombo extends Composite {
77
78 // ------------------------------------------------------------------------
79 // Constants
80 // ------------------------------------------------------------------------
81
f4617471
PT
82 /** Constant indicating that all levels of the time graph should be expanded
83 * @since 3.1 */
84 public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
85
837a2f8c
PT
86 private static final Object FILLER = new Object();
87
c004295c
PT
88 private static final String ITEM_HEIGHT = "$height$"; //$NON-NLS-1$
89
837a2f8c
PT
90 // ------------------------------------------------------------------------
91 // Fields
92 // ------------------------------------------------------------------------
93
4999a196 94 /** The tree viewer */
837a2f8c
PT
95 private TreeViewer fTreeViewer;
96
4999a196 97 /** The time viewer */
837a2f8c
PT
98 private TimeGraphViewer fTimeGraphViewer;
99
4999a196 100 /** The selection listener map */
507b1336 101 private final Map<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<>();
837a2f8c 102
4999a196 103 /** The map of viewer filters */
507b1336 104 private final Map<ViewerFilter, ViewerFilter> fViewerFilterMap = new HashMap<>();
6ac5a950 105
4999a196
GB
106 /**
107 * Flag to block the tree selection changed listener when triggered by the
108 * time graph combo
109 */
837a2f8c
PT
110 private boolean fInhibitTreeSelection = false;
111
4999a196 112 /** Number of filler rows used by the tree content provider */
837a2f8c
PT
113 private int fNumFillerRows;
114
4999a196 115 /** Calculated item height for Linux workaround */
837a2f8c
PT
116 private int fLinuxItemHeight = 0;
117
4999a196 118 /** The button that opens the filter dialog */
6ac5a950
AM
119 private Action showFilterAction;
120
4999a196 121 /** The filter dialog */
6ac5a950
AM
122 private TimeGraphFilterDialog fFilterDialog;
123
4999a196 124 /** The filter generated from the filter dialog */
6ac5a950
AM
125 private RawViewerFilter fFilter;
126
4999a196
GB
127 /** Default weight of each part of the sash */
128 private static final int[] DEFAULT_WEIGHTS = { 1, 1 };
129
588dff10
PT
130 /** List of all expanded items whose parents are also expanded */
131 private List<TreeItem> fVisibleExpandedItems = null;
132
837a2f8c
PT
133 // ------------------------------------------------------------------------
134 // Classes
135 // ------------------------------------------------------------------------
136
137 /**
138 * The TreeContentProviderWrapper is used to insert filler items after
139 * the elements of the tree's real content provider.
140 */
141 private class TreeContentProviderWrapper implements ITreeContentProvider {
142 private final ITreeContentProvider contentProvider;
143
144 public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {
145 this.contentProvider = contentProvider;
146 }
147
148 @Override
149 public void dispose() {
150 contentProvider.dispose();
151 }
152
153 @Override
154 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
155 contentProvider.inputChanged(viewer, oldInput, newInput);
156 }
157
158 @Override
159 public Object[] getElements(Object inputElement) {
160 Object[] elements = contentProvider.getElements(inputElement);
161 // add filler elements to ensure alignment with time analysis viewer
f1fae91f 162 Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, Object[].class);
837a2f8c
PT
163 for (int i = 0; i < fNumFillerRows; i++) {
164 oElements[elements.length + i] = FILLER;
165 }
166 return oElements;
167 }
168
169 @Override
170 public Object[] getChildren(Object parentElement) {
171 if (parentElement instanceof ITimeGraphEntry) {
172 return contentProvider.getChildren(parentElement);
173 }
174 return new Object[0];
175 }
176
177 @Override
178 public Object getParent(Object element) {
179 if (element instanceof ITimeGraphEntry) {
180 return contentProvider.getParent(element);
181 }
182 return null;
183 }
184
185 @Override
186 public boolean hasChildren(Object element) {
187 if (element instanceof ITimeGraphEntry) {
188 return contentProvider.hasChildren(element);
189 }
190 return false;
191 }
192 }
193
194 /**
195 * The TreeLabelProviderWrapper is used to intercept the filler items
196 * from the calls to the tree's real label provider.
197 */
198 private class TreeLabelProviderWrapper implements ITableLabelProvider {
199 private final ITableLabelProvider labelProvider;
200
201 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
202 this.labelProvider = labelProvider;
203 }
204
205 @Override
206 public void addListener(ILabelProviderListener listener) {
207 labelProvider.addListener(listener);
208 }
209
210 @Override
211 public void dispose() {
212 labelProvider.dispose();
213 }
214
215 @Override
216 public boolean isLabelProperty(Object element, String property) {
217 if (element instanceof ITimeGraphEntry) {
218 return labelProvider.isLabelProperty(element, property);
219 }
220 return false;
221 }
222
223 @Override
224 public void removeListener(ILabelProviderListener listener) {
225 labelProvider.removeListener(listener);
226 }
227
228 @Override
229 public Image getColumnImage(Object element, int columnIndex) {
230 if (element instanceof ITimeGraphEntry) {
231 return labelProvider.getColumnImage(element, columnIndex);
232 }
233 return null;
234 }
235
236 @Override
237 public String getColumnText(Object element, int columnIndex) {
238 if (element instanceof ITimeGraphEntry) {
239 return labelProvider.getColumnText(element, columnIndex);
240 }
241 return null;
242 }
243
244 }
245
246 /**
247 * The SelectionListenerWrapper is used to intercept the filler items from
248 * the time graph combo's real selection listener, and to prevent double
249 * notifications from being sent when selection changes in both tree and
250 * time graph at the same time.
251 */
252 private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {
253 private final ITimeGraphSelectionListener listener;
254 private ITimeGraphEntry selection = null;
255
256 public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {
257 this.listener = listener;
258 }
259
260 @Override
261 public void selectionChanged(SelectionChangedEvent event) {
262 if (fInhibitTreeSelection) {
263 return;
264 }
265 Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
266 if (element instanceof ITimeGraphEntry) {
267 ITimeGraphEntry entry = (ITimeGraphEntry) element;
268 if (entry != selection) {
269 selection = entry;
270 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
271 }
272 }
273 }
274
275 @Override
276 public void selectionChanged(TimeGraphSelectionEvent event) {
277 ITimeGraphEntry entry = event.getSelection();
278 if (entry != selection) {
279 selection = entry;
280 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
281 }
282 }
283 }
284
6ac5a950
AM
285 /**
286 * The ViewerFilterWrapper is used to intercept the filler items from
287 * the time graph combo's real ViewerFilters. These filler items should
288 * always be visible.
289 */
290 private class ViewerFilterWrapper extends ViewerFilter {
291
f1fae91f 292 private ViewerFilter fWrappedFilter;
6ac5a950
AM
293
294 ViewerFilterWrapper(ViewerFilter filter) {
295 super();
296 this.fWrappedFilter = filter;
297 }
298
299 @Override
300 public boolean select(Viewer viewer, Object parentElement, Object element) {
301 if (element instanceof ITimeGraphEntry) {
302 return fWrappedFilter.select(viewer, parentElement, element);
303 }
304 return true;
305 }
306
307 }
308
309 /**
f8840316
FR
310 * This filter simply keeps a list of elements that should be filtered out.
311 * All the other elements will be shown.
8f28f9d8 312 * By default and when the list is set to null, all elements are shown.
6ac5a950
AM
313 */
314 private class RawViewerFilter extends ViewerFilter {
315
f8840316 316 private List<Object> fFiltered = null;
6ac5a950 317
f8840316
FR
318 public void setFiltered(List<Object> objects) {
319 fFiltered = objects;
6ac5a950
AM
320 }
321
f8840316
FR
322 public List<Object> getFiltered() {
323 return fFiltered;
6ac5a950
AM
324 }
325
326 @Override
327 public boolean select(Viewer viewer, Object parentElement, Object element) {
f8840316 328 if (fFiltered == null) {
8f28f9d8
PT
329 return true;
330 }
f8840316 331 return !fFiltered.contains(element);
6ac5a950
AM
332 }
333 }
334
837a2f8c
PT
335 // ------------------------------------------------------------------------
336 // Constructors
337 // ------------------------------------------------------------------------
338
339 /**
340 * Constructs a new instance of this class given its parent
341 * and a style value describing its behavior and appearance.
342 *
343 * @param parent a widget which will be the parent of the new instance (cannot be null)
344 * @param style the style of widget to construct
345 */
346 public TimeGraphCombo(Composite parent, int style) {
4999a196
GB
347 this(parent, style, DEFAULT_WEIGHTS);
348 }
349
350 /**
351 * Constructs a new instance of this class given its parent and a style
352 * value describing its behavior and appearance.
353 *
354 * @param parent
355 * a widget which will be the parent of the new instance (cannot
356 * be null)
357 * @param style
358 * the style of widget to construct
359 * @param weights
360 * The relative weights of each side of the sash form
361 * @since 2.1
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 */
643 public TimeGraphViewer getTimeGraphViewer() {
644 return fTimeGraphViewer;
645 }
646
6ac5a950
AM
647 /**
648 * Callback for the show filter action
649 *
650 * @since 2.0
651 */
652 public void showFilterDialog() {
4c9c0c87
PT
653 ITimeGraphEntry[] topInput = fTimeGraphViewer.getTimeGraphContentProvider().getElements(fTimeGraphViewer.getInput());
654 if (topInput != null) {
655 List<? extends ITimeGraphEntry> allElements = listAllInputs(Arrays.asList(topInput));
656 fFilterDialog.setInput(fTimeGraphViewer.getInput());
6ac5a950
AM
657 fFilterDialog.setTitle(Messages.TmfTimeFilterDialog_WINDOW_TITLE);
658 fFilterDialog.setMessage(Messages.TmfTimeFilterDialog_MESSAGE);
8f28f9d8 659 fFilterDialog.setExpandedElements(allElements.toArray());
f8840316 660 if (fFilter.getFiltered() != null) {
507b1336 661 ArrayList<? extends ITimeGraphEntry> nonFilteredElements = new ArrayList<>(allElements);
f8840316
FR
662 nonFilteredElements.removeAll(fFilter.getFiltered());
663 fFilterDialog.setInitialElementSelections(nonFilteredElements);
8f28f9d8
PT
664 } else {
665 fFilterDialog.setInitialElementSelections(allElements);
666 }
6ac5a950
AM
667 fFilterDialog.create();
668 fFilterDialog.open();
669 // Process selected elements
670 if (fFilterDialog.getResult() != null) {
671 fInhibitTreeSelection = true;
8f28f9d8 672 if (fFilterDialog.getResult().length != allElements.size()) {
f8840316
FR
673 ArrayList<Object> filteredElements = new ArrayList<Object>(allElements);
674 filteredElements.removeAll(Arrays.asList(fFilterDialog.getResult()));
675 fFilter.setFiltered(filteredElements);
8f28f9d8 676 } else {
f8840316 677 fFilter.setFiltered(null);
8f28f9d8 678 }
6ac5a950
AM
679 fTreeViewer.refresh();
680 fTreeViewer.expandAll();
681 fTimeGraphViewer.refresh();
682 fInhibitTreeSelection = false;
588dff10 683 alignTreeItems(true);
40b7b614 684 // Reset selection
6ac5a950 685 if (fFilterDialog.getResult().length > 0) {
40b7b614 686 setSelection(null);
6ac5a950
AM
687 }
688 }
689 }
690 }
691
692 /**
693 * Get the show filter action.
694 *
695 * @return The Action object
696 * @since 2.0
697 */
698 public Action getShowFilterAction() {
699 if (showFilterAction == null) {
700 // showFilter
701 showFilterAction = new Action() {
702 @Override
703 public void run() {
704 showFilterDialog();
705 }
706 };
707 showFilterAction.setText(Messages.TmfTimeGraphCombo_FilterActionNameText);
708 showFilterAction.setToolTipText(Messages.TmfTimeGraphCombo_FilterActionToolTipText);
709 // TODO find a nice, distinctive icon
710 showFilterAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS));
711 }
712
713 return showFilterAction;
714 }
715
837a2f8c
PT
716 // ------------------------------------------------------------------------
717 // Control
718 // ------------------------------------------------------------------------
719
837a2f8c
PT
720 @Override
721 public void redraw() {
722 fTimeGraphViewer.getControl().redraw();
723 super.redraw();
724 }
725
726 // ------------------------------------------------------------------------
727 // Operations
728 // ------------------------------------------------------------------------
729
730 /**
731 * Sets the tree content provider used by this time graph combo.
732 *
733 * @param contentProvider the tree content provider
734 */
735 public void setTreeContentProvider(ITreeContentProvider contentProvider) {
736 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
737 }
738
739 /**
740 * Sets the tree label provider used by this time graph combo.
741 *
742 * @param labelProvider the tree label provider
743 */
744 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
745 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
746 }
747
6ac5a950
AM
748 /**
749 * Sets the tree content provider used by the filter dialog
750 *
751 * @param contentProvider the tree content provider
752 * @since 2.0
753 */
754 public void setFilterContentProvider(ITreeContentProvider contentProvider) {
755 fFilterDialog.setContentProvider(contentProvider);
756 }
757
758 /**
759 * Sets the tree label provider used by the filter dialog
760 *
761 * @param labelProvider the tree label provider
762 * @since 2.0
763 */
764 public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
765 fFilterDialog.setLabelProvider(labelProvider);
766 }
767
837a2f8c
PT
768 /**
769 * Sets the tree columns for this time graph combo.
770 *
771 * @param columnNames the tree column names
772 */
773 public void setTreeColumns(String[] columnNames) {
774 final Tree tree = fTreeViewer.getTree();
775 for (String columnName : columnNames) {
776 TreeColumn column = new TreeColumn(tree, SWT.LEFT);
777 column.setText(columnName);
778 column.pack();
779 }
780 }
781
6ac5a950
AM
782 /**
783 * Sets the tree columns for this time graph combo's filter dialog.
784 *
785 * @param columnNames the tree column names
786 * @since 2.0
787 */
788 public void setFilterColumns(String[] columnNames) {
789 fFilterDialog.setColumnNames(columnNames);
790 }
791
837a2f8c 792 /**
4c9c0c87
PT
793 * Sets the time graph content provider used by this time graph combo.
794 *
795 * @param timeGraphContentProvider
796 * the time graph content provider
797 *
798 * @since 3.0
799 */
800 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider) {
801 fTimeGraphViewer.setTimeGraphContentProvider(timeGraphContentProvider);
802 }
803
804 /**
805 * Sets the time graph presentation provider used by this time graph combo.
837a2f8c
PT
806 *
807 * @param timeGraphProvider the time graph provider
808 */
809 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
810 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
811 }
812
813 /**
814 * Sets or clears the input for this time graph combo.
837a2f8c
PT
815 *
816 * @param input the input of this time graph combo, or <code>null</code> if none
4c9c0c87
PT
817 *
818 * @since 3.0
837a2f8c 819 */
4c9c0c87 820 public void setInput(Object input) {
f8840316 821 fFilter.setFiltered(null);
837a2f8c
PT
822 fInhibitTreeSelection = true;
823 fTreeViewer.setInput(input);
824 for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
825 listenerWrapper.selection = null;
826 }
827 fInhibitTreeSelection = false;
837a2f8c
PT
828 fTreeViewer.getTree().getVerticalBar().setEnabled(false);
829 fTreeViewer.getTree().getVerticalBar().setVisible(false);
830 fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
831 fTimeGraphViewer.setInput(input);
588dff10
PT
832 // queue the alignment update because in Linux the item bounds are not
833 // set properly until the tree has been painted at least once
834 fVisibleExpandedItems = null; // invalidate the cache
835 getDisplay().asyncExec(new Runnable() {
836 @Override
837 public void run() {
838 alignTreeItems(true);
839 }});
837a2f8c
PT
840 }
841
4c9c0c87
PT
842 /**
843 * Gets the input for this time graph combo.
844 *
845 * @return The input of this time graph combo, or <code>null</code> if none
846 *
847 * @since 3.0
848 */
849 public Object getInput() {
850 return fTreeViewer.getInput();
851 }
852
bec1f1ac
GB
853 /**
854 * Sets or clears the list of links to display on this combo
855 *
856 * @param links the links to display in this time graph combo
857 * @since 2.1
858 */
859 public void setLinks(List<ILinkEvent> links) {
860 fTimeGraphViewer.setLinks(links);
861 }
862
6ac5a950
AM
863 /**
864 * @param filter The filter object to be attached to the view
865 * @since 2.0
866 */
867 public void addFilter(ViewerFilter filter) {
868 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
869 fTreeViewer.addFilter(wrapper);
870 fTimeGraphViewer.addFilter(wrapper);
871 fViewerFilterMap.put(filter, wrapper);
588dff10 872 alignTreeItems(true);
6ac5a950
AM
873 }
874
875 /**
876 * @param filter The filter object to be removed from the view
877 * @since 2.0
878 */
879 public void removeFilter(ViewerFilter filter) {
880 ViewerFilter wrapper = fViewerFilterMap.get(filter);
881 fTreeViewer.removeFilter(wrapper);
882 fTimeGraphViewer.removeFilter(wrapper);
883 fViewerFilterMap.remove(filter);
588dff10 884 alignTreeItems(true);
6ac5a950
AM
885 }
886
837a2f8c
PT
887 /**
888 * Refreshes this time graph completely with information freshly obtained from its model.
889 */
890 public void refresh() {
891 fInhibitTreeSelection = true;
4c9c0c87
PT
892 Tree tree = fTreeViewer.getTree();
893 tree.setRedraw(false);
837a2f8c 894 fTreeViewer.refresh();
4c9c0c87
PT
895 fTreeViewer.expandAll();
896 tree.setRedraw(true);
837a2f8c 897 fTimeGraphViewer.refresh();
588dff10 898 alignTreeItems(true);
837a2f8c
PT
899 fInhibitTreeSelection = false;
900 }
901
902 /**
903 * Adds a listener for selection changes in this time graph combo.
904 *
905 * @param listener a selection listener
906 */
907 public void addSelectionListener(ITimeGraphSelectionListener listener) {
908 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
909 fTreeViewer.addSelectionChangedListener(listenerWrapper);
910 fSelectionListenerMap.put(listener, listenerWrapper);
911 fTimeGraphViewer.addSelectionListener(listenerWrapper);
912 }
913
914 /**
915 * Removes the given selection listener from this time graph combo.
916 *
917 * @param listener a selection changed listener
918 */
919 public void removeSelectionListener(ITimeGraphSelectionListener listener) {
920 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
921 fTreeViewer.removeSelectionChangedListener(listenerWrapper);
922 fTimeGraphViewer.removeSelectionListener(listenerWrapper);
923 }
924
925 /**
926 * Sets the current selection for this time graph combo.
927 *
928 * @param selection the new selection
929 */
930 public void setSelection(ITimeGraphEntry selection) {
931 fTimeGraphViewer.setSelection(selection);
932 fInhibitTreeSelection = true; // block the tree selection changed listener
933 if (selection != null) {
934 StructuredSelection structuredSelection = new StructuredSelection(selection);
935 fTreeViewer.setSelection(structuredSelection);
936 } else {
937 fTreeViewer.setSelection(new StructuredSelection());
938 }
939 fInhibitTreeSelection = false;
588dff10 940 alignTreeItems(false);
837a2f8c
PT
941 }
942
f4617471
PT
943 /**
944 * Sets the auto-expand level to be used when the input of the viewer is set
945 * using {@link #setInput(Object)}. The value 0 means that there is no
946 * auto-expand; 1 means that top-level elements are expanded, but not their
947 * children; 2 means that top-level elements are expanded, and their
948 * children, but not grand-children; and so on.
949 * <p>
950 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
951 * </p>
952 * @param level
953 * non-negative level, or <code>ALL_LEVELS</code> to expand all
954 * levels of the tree
955 * @since 3.1
956 */
957 public void setAutoExpandLevel(int level) {
958 fTimeGraphViewer.setAutoExpandLevel(level);
959 if (level <= 0) {
960 fTreeViewer.setAutoExpandLevel(level);
961 } else {
962 fTreeViewer.setAutoExpandLevel(level + 1);
963 }
964 }
965
966 /**
967 * Returns the auto-expand level.
968 *
969 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
970 * the tree are expanded automatically
971 * @see #setAutoExpandLevel
972 * @since 3.1
973 */
974 public int getAutoExpandLevel() {
975 return fTimeGraphViewer.getAutoExpandLevel();
976 }
977
837a2f8c
PT
978 /**
979 * Set the expanded state of an entry
980 *
981 * @param entry
982 * The entry to expand/collapse
983 * @param expanded
984 * True for expanded, false for collapsed
985 *
986 * @since 2.0
987 */
988 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
989 fTimeGraphViewer.setExpandedState(entry, expanded);
990 fTreeViewer.setExpandedState(entry, expanded);
588dff10 991 alignTreeItems(true);
837a2f8c
PT
992 }
993
994 /**
995 * Collapses all nodes of the viewer's tree, starting with the root.
996 *
997 * @since 2.0
998 */
999 public void collapseAll() {
1000 fTimeGraphViewer.collapseAll();
1001 fTreeViewer.collapseAll();
588dff10 1002 alignTreeItems(true);
837a2f8c
PT
1003 }
1004
1005 /**
1006 * Expands all nodes of the viewer's tree, starting with the root.
1007 *
1008 * @since 2.0
1009 */
1010 public void expandAll() {
1011 fTimeGraphViewer.expandAll();
1012 fTreeViewer.expandAll();
588dff10 1013 alignTreeItems(true);
837a2f8c
PT
1014 }
1015
1016 // ------------------------------------------------------------------------
1017 // Internal
1018 // ------------------------------------------------------------------------
1019
588dff10
PT
1020 private List<TreeItem> getVisibleExpandedItems(Tree tree, boolean refresh) {
1021 if (fVisibleExpandedItems == null || refresh) {
1022 ArrayList<TreeItem> items = new ArrayList<>();
1023 for (TreeItem item : tree.getItems()) {
1024 if (item.getData() == FILLER) {
1025 break;
1026 }
1027 items.add(item);
1028 if (item.getExpanded()) {
1029 addVisibleExpandedItems(items, item);
1030 }
837a2f8c 1031 }
588dff10 1032 fVisibleExpandedItems = items;
837a2f8c 1033 }
588dff10 1034 return fVisibleExpandedItems;
837a2f8c
PT
1035 }
1036
588dff10 1037 private void addVisibleExpandedItems(List<TreeItem> items, TreeItem treeItem) {
837a2f8c
PT
1038 for (TreeItem item : treeItem.getItems()) {
1039 items.add(item);
1040 if (item.getExpanded()) {
588dff10 1041 addVisibleExpandedItems(items, item);
837a2f8c
PT
1042 }
1043 }
837a2f8c
PT
1044 }
1045
6ac5a950
AM
1046 /**
1047 * Explores the list of top-level inputs and returns all the inputs
1048 *
1049 * @param inputs The top-level inputs
1050 * @return All the inputs
1051 */
1052 private List<? extends ITimeGraphEntry> listAllInputs(List<? extends ITimeGraphEntry> inputs) {
507b1336 1053 ArrayList<ITimeGraphEntry> items = new ArrayList<>();
6ac5a950
AM
1054 for (ITimeGraphEntry entry : inputs) {
1055 items.add(entry);
1056 if (entry.hasChildren()) {
1057 items.addAll(listAllInputs(entry.getChildren()));
1058 }
1059 }
1060 return items;
1061 }
1062
837a2f8c
PT
1063 private int getItemHeight(final Tree tree) {
1064 /*
1065 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1066 */
1067 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1068 if (fLinuxItemHeight != 0) {
1069 return fLinuxItemHeight;
1070 }
588dff10 1071 List<TreeItem> treeItems = getVisibleExpandedItems(tree, true);
837a2f8c
PT
1072 if (treeItems.size() > 1) {
1073 final TreeItem treeItem0 = treeItems.get(0);
1074 final TreeItem treeItem1 = treeItems.get(1);
1075 PaintListener paintListener = new PaintListener() {
1076 @Override
1077 public void paintControl(PaintEvent e) {
1078 tree.removePaintListener(this);
1079 int y0 = treeItem0.getBounds().y;
1080 int y1 = treeItem1.getBounds().y;
1081 int itemHeight = y1 - y0;
1082 if (itemHeight > 0) {
1083 fLinuxItemHeight = itemHeight;
1084 fTimeGraphViewer.setItemHeight(itemHeight);
1085 }
1086 }
1087 };
1088 tree.addPaintListener(paintListener);
1089 }
1090 } else {
1091 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
1092 }
1093 return tree.getItemHeight();
1094 }
1095
588dff10
PT
1096 private void alignTreeItems(boolean refreshExpandedItems) {
1097 // align the tree top item with the time graph top item
1098 Tree tree = fTreeViewer.getTree();
1099 List<TreeItem> treeItems = getVisibleExpandedItems(tree, refreshExpandedItems);
1100 int topIndex = fTimeGraphViewer.getTopIndex();
1101 if (topIndex >= treeItems.size()) {
1102 return;
1103 }
1104 TreeItem item = treeItems.get(topIndex);
1105 tree.setTopItem(item);
1106
1107 // ensure the time graph item heights are equal to the tree item heights
1108 int treeHeight = fTreeViewer.getTree().getBounds().height;
1109 int index = topIndex;
1110 Rectangle bounds = item.getBounds();
1111 while (index < treeItems.size() - 1) {
1112 if (bounds.y > treeHeight) {
1113 break;
1114 }
1115 /*
1116 * Bug in Linux. The method getBounds doesn't always return the correct height.
1117 * Use the difference of y position between items to calculate the height.
1118 */
1119 TreeItem nextItem = treeItems.get(index + 1);
1120 Rectangle nextBounds = nextItem.getBounds();
1121 Integer itemHeight = nextBounds.y - bounds.y;
1122 if (itemHeight > 0 && !itemHeight.equals(item.getData(ITEM_HEIGHT))) {
1123 ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
1124 if (fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight)) {
1125 item.setData(ITEM_HEIGHT, itemHeight);
1126 }
1127 }
1128 index++;
1129 item = nextItem;
1130 bounds = nextBounds;
1131 }
1132 }
1133
837a2f8c 1134}
This page took 0.100282 seconds and 5 git commands to generate.