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