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