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