tmf.ui: Add Lambdas to TimeGraphCombo
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / TimeGraphCombo.java
CommitLineData
837a2f8c 1/*******************************************************************************
d2e4afa7 2 * Copyright (c) 2012, 2015 Ericsson, others
837a2f8c
PT
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Patrick Tasse - Initial API and implementation
f8840316 11 * François Rajotte - Filter implementation
bec1f1ac 12 * Geneviève Bastien - Add event links between entries
b97d61f0 13 * Christian Mansky - Add check active / uncheck inactive buttons
837a2f8c
PT
14 *******************************************************************************/
15
2bdf0193 16package org.eclipse.tracecompass.tmf.ui.widgets.timegraph;
837a2f8c
PT
17
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.HashMap;
70e10acc 21import java.util.HashSet;
6ac5a950
AM
22import java.util.List;
23import java.util.Map;
70e10acc 24import java.util.Set;
837a2f8c 25
dfa0ef96 26import org.eclipse.jdt.annotation.NonNull;
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;
48072ae3 44import org.eclipse.swt.events.KeyEvent;
f0a9cee1 45import org.eclipse.swt.events.MouseAdapter;
837a2f8c
PT
46import org.eclipse.swt.events.MouseEvent;
47import org.eclipse.swt.events.MouseTrackAdapter;
837a2f8c
PT
48import org.eclipse.swt.events.PaintEvent;
49import org.eclipse.swt.events.PaintListener;
50import org.eclipse.swt.events.SelectionAdapter;
51import org.eclipse.swt.events.SelectionEvent;
3bd20aa6
PT
52import org.eclipse.swt.graphics.Font;
53import org.eclipse.swt.graphics.FontData;
dc4fa715 54import org.eclipse.swt.graphics.GC;
837a2f8c
PT
55import org.eclipse.swt.graphics.Image;
56import org.eclipse.swt.graphics.Point;
588dff10 57import org.eclipse.swt.graphics.Rectangle;
837a2f8c 58import org.eclipse.swt.layout.FillLayout;
d2e4afa7 59import org.eclipse.swt.layout.GridLayout;
837a2f8c 60import org.eclipse.swt.widgets.Composite;
d2e4afa7 61import org.eclipse.swt.widgets.Control;
837a2f8c
PT
62import org.eclipse.swt.widgets.Display;
63import org.eclipse.swt.widgets.Event;
64import org.eclipse.swt.widgets.Listener;
d2e4afa7 65import org.eclipse.swt.widgets.Sash;
837a2f8c
PT
66import org.eclipse.swt.widgets.Slider;
67import org.eclipse.swt.widgets.Tree;
68import org.eclipse.swt.widgets.TreeColumn;
69import org.eclipse.swt.widgets.TreeItem;
d2e4afa7
MAL
70import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
71import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
72import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
73import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
b97d61f0 74import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ITimeGraphEntryActiveProvider;
cfcfd964 75import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ShowFilterDialogAction;
2bdf0193
AM
76import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
77import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
dc4fa715 78import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;
3bd20aa6
PT
79import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
80import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
dc4fa715 81import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphMarkerAxis;
837a2f8c 82
4923d7b9
PT
83import com.google.common.collect.Iterables;
84
837a2f8c
PT
85/**
86 * Time graph "combo" view (with the list/tree on the left and the gantt chart
87 * on the right)
88 *
837a2f8c
PT
89 * @author Patrick Tasse
90 */
91public class TimeGraphCombo extends Composite {
92
93 // ------------------------------------------------------------------------
94 // Constants
95 // ------------------------------------------------------------------------
96
ae09c4ad 97 /** Constant indicating that all levels of the time graph should be expanded */
f4617471
PT
98 public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
99
837a2f8c
PT
100 private static final Object FILLER = new Object();
101
102 // ------------------------------------------------------------------------
103 // Fields
104 // ------------------------------------------------------------------------
105
4999a196 106 /** The tree viewer */
837a2f8c
PT
107 private TreeViewer fTreeViewer;
108
4999a196 109 /** The time viewer */
dfa0ef96 110 private @NonNull TimeGraphViewer fTimeGraphViewer;
837a2f8c 111
4999a196 112 /** The selection listener map */
507b1336 113 private final Map<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<>();
837a2f8c 114
4923d7b9 115 /** The map of viewer filters to viewer filter wrappers */
367e2932 116 private final Map<@NonNull ViewerFilter, @NonNull ViewerFilter> fViewerFilterMap = new HashMap<>();
6ac5a950 117
4999a196
GB
118 /**
119 * Flag to block the tree selection changed listener when triggered by the
120 * time graph combo
121 */
837a2f8c
PT
122 private boolean fInhibitTreeSelection = false;
123
4999a196 124 /** Number of filler rows used by the tree content provider */
837a2f8c
PT
125 private int fNumFillerRows;
126
4999a196 127 /** Calculated item height for Linux workaround */
837a2f8c
PT
128 private int fLinuxItemHeight = 0;
129
cfcfd964
PT
130 /** The action that opens the filter dialog */
131 private ShowFilterDialogAction fShowFilterDialogAction;
6ac5a950 132
4999a196
GB
133 /** Default weight of each part of the sash */
134 private static final int[] DEFAULT_WEIGHTS = { 1, 1 };
135
588dff10
PT
136 /** List of all expanded items whose parents are also expanded */
137 private List<TreeItem> fVisibleExpandedItems = null;
138
d2e4afa7
MAL
139 private Listener fSashDragListener;
140 private SashForm fSashForm;
141
f6de5bef
MAL
142 private final boolean fScrollBarsInTreeWorkaround;
143
3bd20aa6
PT
144 private Font fTreeFont;
145
837a2f8c
PT
146 // ------------------------------------------------------------------------
147 // Classes
148 // ------------------------------------------------------------------------
149
3bd20aa6
PT
150 /**
151 * The TimeGraphViewerExtension is used to set appropriate values and to
152 * override methods that could be called directly by the user and that must
153 * be handled by the time graph combo.
154 */
155 private class TimeGraphViewerExtension extends TimeGraphViewer {
156
157 private TimeGraphViewerExtension(Composite parent, int style, Tree tree) {
158 super(parent, style);
159 setItemHeight(TimeGraphCombo.this.getItemHeight(tree, true));
160 setHeaderHeight(tree.getHeaderHeight());
161 setBorderWidth(tree.getBorderWidth());
162 setNameWidthPref(0);
163 }
164
165 @Override
166 public ShowFilterDialogAction getShowFilterDialogAction() {
167 return TimeGraphCombo.this.getShowFilterDialogAction();
168 }
169
170 @Override
171 protected TimeGraphControl createTimeGraphControl(Composite composite, TimeGraphColorScheme colors) {
172 return new TimeGraphControl(composite, colors) {
173 @Override
83d0971d
PT
174 public void verticalZoom(boolean zoomIn) {
175 TimeGraphCombo.this.verticalZoom(zoomIn);
3bd20aa6
PT
176 }
177
178 @Override
83d0971d 179 public void resetVerticalZoom() {
3bd20aa6 180 TimeGraphCombo.this.resetVerticalZoom();
3bd20aa6 181 }
48072ae3
PT
182
183 @Override
184 public void setElementPosition(ITimeGraphEntry entry, int y) {
185 /*
186 * Queue the update to make sure the time graph combo has finished
187 * updating the item heights.
188 */
189 getDisplay().asyncExec(() -> {
190 super.setElementPosition(entry, y);
191 alignTreeItems(false);
192 });
193 }
3bd20aa6
PT
194 };
195 }
dc4fa715
PT
196
197 private class TimeGraphMarkerAxisExtension extends TimeGraphMarkerAxis {
198 private int fMargin = 0;
199
200 public TimeGraphMarkerAxisExtension(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
201 super(parent, colorScheme, timeProvider);
202 }
203
204 @Override
205 public Point computeSize(int wHint, int hHint, boolean changed) {
206 Point size = super.computeSize(wHint, hHint, changed);
207 if (size.y > 0) {
208 size.y += fMargin;
209 }
210 return size;
211 }
212
213 @Override
214 public void redraw() {
215 super.redraw();
216 fTreeViewer.getControl().redraw();
217 }
218
219 @Override
220 protected void drawMarkerAxis(Rectangle bounds, int nameSpace, GC gc) {
221 super.drawMarkerAxis(bounds, nameSpace, gc);
222 }
223
224 private Rectangle getAxisBounds() {
225 Tree tree = fTreeViewer.getTree();
226 Rectangle axisBounds = getBounds();
227 Rectangle treeClientArea = tree.getClientArea();
228 if (axisBounds.isEmpty()) {
229 treeClientArea.y += treeClientArea.height;
230 treeClientArea.height = 0;
231 return treeClientArea;
232 }
233 Composite axisParent = getParent();
234 Point axisDisplayCoordinates = axisParent.toDisplay(axisBounds.x, axisBounds.y);
235 Point axisTreeCoordinates = tree.toControl(axisDisplayCoordinates);
236 int height = treeClientArea.y + treeClientArea.height - axisTreeCoordinates.y;
237 int margin = Math.max(0, axisBounds.height - height);
238 if (fMargin != margin) {
239 fMargin = margin;
240 getParent().layout();
241 redraw();
242 axisTreeCoordinates.y -= margin;
243 height += margin;
244 }
245 return new Rectangle(treeClientArea.x, axisTreeCoordinates.y, treeClientArea.width, height);
246 }
247 }
248
249 @Override
250 protected TimeGraphMarkerAxis createTimeGraphMarkerAxis(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
251 TimeGraphMarkerAxisExtension timeGraphMarkerAxis = new TimeGraphMarkerAxisExtension(parent, colorScheme, timeProvider);
252 Tree tree = fTreeViewer.getTree();
72bf2314
MK
253 tree.addPaintListener(e -> {
254 /*
255 * Draw the marker axis over the tree. Only the name area
256 * will be drawn, since the time area has zero width.
257 */
258 Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
259 e.gc.setBackground(timeGraphMarkerAxis.getBackground());
260 timeGraphMarkerAxis.drawMarkerAxis(bounds, bounds.width, e.gc);
dc4fa715 261 });
f0a9cee1
PT
262 tree.addMouseListener(new MouseAdapter() {
263 @Override
264 public void mouseDown(MouseEvent e) {
265 Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
266 if (bounds.contains(e.x, e.y)) {
267 timeGraphMarkerAxis.mouseDown(e, bounds, bounds.width);
268 }
269 }
270 });
dc4fa715
PT
271 tree.getHorizontalBar().addSelectionListener(new SelectionAdapter() {
272 @Override
273 public void widgetSelected(SelectionEvent e) {
274 tree.redraw();
275 }
276 });
277 return timeGraphMarkerAxis;
278 }
3bd20aa6
PT
279 }
280
837a2f8c
PT
281 /**
282 * The TreeContentProviderWrapper is used to insert filler items after
283 * the elements of the tree's real content provider.
284 */
285 private class TreeContentProviderWrapper implements ITreeContentProvider {
286 private final ITreeContentProvider contentProvider;
287
288 public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {
289 this.contentProvider = contentProvider;
290 }
291
292 @Override
293 public void dispose() {
294 contentProvider.dispose();
295 }
296
297 @Override
298 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
299 contentProvider.inputChanged(viewer, oldInput, newInput);
300 }
301
302 @Override
303 public Object[] getElements(Object inputElement) {
304 Object[] elements = contentProvider.getElements(inputElement);
305 // add filler elements to ensure alignment with time analysis viewer
f1fae91f 306 Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, Object[].class);
837a2f8c
PT
307 for (int i = 0; i < fNumFillerRows; i++) {
308 oElements[elements.length + i] = FILLER;
309 }
310 return oElements;
311 }
312
313 @Override
314 public Object[] getChildren(Object parentElement) {
315 if (parentElement instanceof ITimeGraphEntry) {
316 return contentProvider.getChildren(parentElement);
317 }
318 return new Object[0];
319 }
320
321 @Override
322 public Object getParent(Object element) {
323 if (element instanceof ITimeGraphEntry) {
324 return contentProvider.getParent(element);
325 }
326 return null;
327 }
328
329 @Override
330 public boolean hasChildren(Object element) {
331 if (element instanceof ITimeGraphEntry) {
332 return contentProvider.hasChildren(element);
333 }
334 return false;
335 }
336 }
337
338 /**
339 * The TreeLabelProviderWrapper is used to intercept the filler items
340 * from the calls to the tree's real label provider.
341 */
658e0268 342 private static class TreeLabelProviderWrapper implements ITableLabelProvider {
837a2f8c
PT
343 private final ITableLabelProvider labelProvider;
344
345 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
346 this.labelProvider = labelProvider;
347 }
348
349 @Override
350 public void addListener(ILabelProviderListener listener) {
351 labelProvider.addListener(listener);
352 }
353
354 @Override
355 public void dispose() {
356 labelProvider.dispose();
357 }
358
359 @Override
360 public boolean isLabelProperty(Object element, String property) {
361 if (element instanceof ITimeGraphEntry) {
362 return labelProvider.isLabelProperty(element, property);
363 }
364 return false;
365 }
366
367 @Override
368 public void removeListener(ILabelProviderListener listener) {
369 labelProvider.removeListener(listener);
370 }
371
372 @Override
373 public Image getColumnImage(Object element, int columnIndex) {
374 if (element instanceof ITimeGraphEntry) {
375 return labelProvider.getColumnImage(element, columnIndex);
376 }
377 return null;
378 }
379
380 @Override
381 public String getColumnText(Object element, int columnIndex) {
382 if (element instanceof ITimeGraphEntry) {
383 return labelProvider.getColumnText(element, columnIndex);
384 }
385 return null;
386 }
387
388 }
389
390 /**
391 * The SelectionListenerWrapper is used to intercept the filler items from
392 * the time graph combo's real selection listener, and to prevent double
393 * notifications from being sent when selection changes in both tree and
394 * time graph at the same time.
395 */
396 private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {
397 private final ITimeGraphSelectionListener listener;
398 private ITimeGraphEntry selection = null;
399
400 public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {
401 this.listener = listener;
402 }
403
404 @Override
405 public void selectionChanged(SelectionChangedEvent event) {
406 if (fInhibitTreeSelection) {
407 return;
408 }
409 Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
410 if (element instanceof ITimeGraphEntry) {
411 ITimeGraphEntry entry = (ITimeGraphEntry) element;
412 if (entry != selection) {
413 selection = entry;
414 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
415 }
416 }
417 }
418
419 @Override
420 public void selectionChanged(TimeGraphSelectionEvent event) {
421 ITimeGraphEntry entry = event.getSelection();
422 if (entry != selection) {
423 selection = entry;
424 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
425 }
426 }
427 }
428
6ac5a950
AM
429 /**
430 * The ViewerFilterWrapper is used to intercept the filler items from
431 * the time graph combo's real ViewerFilters. These filler items should
432 * always be visible.
433 */
658e0268 434 private static class ViewerFilterWrapper extends ViewerFilter {
6ac5a950 435
f1fae91f 436 private ViewerFilter fWrappedFilter;
6ac5a950
AM
437
438 ViewerFilterWrapper(ViewerFilter filter) {
439 super();
440 this.fWrappedFilter = filter;
441 }
442
443 @Override
444 public boolean select(Viewer viewer, Object parentElement, Object element) {
445 if (element instanceof ITimeGraphEntry) {
446 return fWrappedFilter.select(viewer, parentElement, element);
447 }
448 return true;
449 }
450
451 }
452
837a2f8c
PT
453 // ------------------------------------------------------------------------
454 // Constructors
455 // ------------------------------------------------------------------------
456
457 /**
458 * Constructs a new instance of this class given its parent
459 * and a style value describing its behavior and appearance.
460 *
461 * @param parent a widget which will be the parent of the new instance (cannot be null)
462 * @param style the style of widget to construct
463 */
464 public TimeGraphCombo(Composite parent, int style) {
4999a196
GB
465 this(parent, style, DEFAULT_WEIGHTS);
466 }
467
468 /**
469 * Constructs a new instance of this class given its parent and a style
470 * value describing its behavior and appearance.
471 *
472 * @param parent
473 * a widget which will be the parent of the new instance (cannot
474 * be null)
475 * @param style
476 * the style of widget to construct
477 * @param weights
d2e4afa7 478 * The array (length 2) of relative weights of each side of the sash form
4999a196
GB
479 */
480 public TimeGraphCombo(Composite parent, int style, int[] weights) {
837a2f8c
PT
481 super(parent, style);
482 setLayout(new FillLayout());
483
d2e4afa7 484 fSashForm = new SashForm(this, SWT.NONE);
837a2f8c 485
f6de5bef
MAL
486 /*
487 * In Windows, SWT.H_SCROLL | SWT.NO_SCROLL is not properly supported,
488 * both scroll bars are always created. See Tree.checkStyle: "Even when
489 * WS_HSCROLL or WS_VSCROLL is not specified, Windows creates trees and
490 * tables with scroll bars."
491 */
492 fScrollBarsInTreeWorkaround = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$
493
494 int scrollBarStyle = fScrollBarsInTreeWorkaround ? SWT.H_SCROLL : SWT.H_SCROLL | SWT.NO_SCROLL;
495
496 fTreeViewer = new TreeViewer(fSashForm, SWT.FULL_SELECTION | scrollBarStyle);
4c9c0c87 497 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
837a2f8c
PT
498 final Tree tree = fTreeViewer.getTree();
499 tree.setHeaderVisible(true);
500 tree.setLinesVisible(true);
501
3bd20aa6 502 fTimeGraphViewer = new TimeGraphViewerExtension(fSashForm, SWT.NONE, tree);
837a2f8c 503
f6de5bef
MAL
504 if (fScrollBarsInTreeWorkaround) {
505 // Feature in Windows. The tree vertical bar reappears when
506 // the control is resized so we need to hide it again.
507 tree.addControlListener(new ControlAdapter() {
508 private int depth = 0;
509
510 @Override
511 public void controlResized(ControlEvent e) {
512 if (depth == 0) {
513 depth++;
514 tree.getVerticalBar().setEnabled(false);
515 // this can trigger controlResized recursively
516 tree.getVerticalBar().setVisible(false);
517 depth--;
518 }
837a2f8c 519 }
f6de5bef
MAL
520 });
521 }
3573a01c
MAL
522 // Bug in Linux. The tree header height is 0 in constructor,
523 // so we need to reset it later when the control is painted.
524 // This work around used to be done on control resized but the header
525 // height was not initialized on the initial resize on GTK3.
526 tree.addPaintListener(new PaintListener() {
527 @Override
528 public void paintControl(PaintEvent e) {
529 int headerHeight = tree.getHeaderHeight();
530 if (headerHeight > 0) {
531 fTimeGraphViewer.setHeaderHeight(headerHeight);
532 tree.removePaintListener(this);
533 }
837a2f8c
PT
534 }
535 });
536
72bf2314
MK
537 tree.addDisposeListener(e -> {
538 if (fTreeFont != null) {
539 fTreeFont.dispose();
3bd20aa6
PT
540 }
541 });
542
837a2f8c
PT
543 // ensure synchronization of expanded items between tree and time graph
544 fTreeViewer.addTreeListener(new ITreeViewerListener() {
545 @Override
546 public void treeCollapsed(TreeExpansionEvent event) {
547 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);
588dff10
PT
548 // queue the alignment update because the tree items may only be
549 // actually collapsed after the listeners have been notified
550 fVisibleExpandedItems = null; // invalidate the cache
551 getDisplay().asyncExec(new Runnable() {
552 @Override
553 public void run() {
554 alignTreeItems(true);
555 }});
837a2f8c
PT
556 }
557
558 @Override
559 public void treeExpanded(TreeExpansionEvent event) {
e7708b02
PT
560 ITimeGraphEntry entry = (ITimeGraphEntry) event.getElement();
561 fTimeGraphViewer.setExpandedState(entry, true);
70e10acc 562 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 563 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
564 if (child.hasChildren()) {
565 boolean expanded = expandedElements.contains(child);
566 fTimeGraphViewer.setExpandedState(child, expanded);
567 }
e7708b02 568 }
588dff10
PT
569 // queue the alignment update because the tree items may only be
570 // actually expanded after the listeners have been notified
571 fVisibleExpandedItems = null; // invalidate the cache
837a2f8c
PT
572 getDisplay().asyncExec(new Runnable() {
573 @Override
574 public void run() {
588dff10 575 alignTreeItems(true);
837a2f8c
PT
576 }});
577 }
578 });
579
580 // ensure synchronization of expanded items between tree and time graph
581 fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {
582 @Override
583 public void treeCollapsed(TimeGraphTreeExpansionEvent event) {
584 fTreeViewer.setExpandedState(event.getEntry(), false);
588dff10 585 alignTreeItems(true);
837a2f8c
PT
586 }
587
588 @Override
589 public void treeExpanded(TimeGraphTreeExpansionEvent event) {
e7708b02
PT
590 ITimeGraphEntry entry = event.getEntry();
591 fTreeViewer.setExpandedState(entry, true);
70e10acc 592 Set<Object> expandedElements = new HashSet<>(Arrays.asList(fTreeViewer.getExpandedElements()));
e7708b02 593 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc
PT
594 if (child.hasChildren()) {
595 boolean expanded = expandedElements.contains(child);
596 fTimeGraphViewer.setExpandedState(child, expanded);
597 }
e7708b02 598 }
588dff10 599 alignTreeItems(true);
837a2f8c
PT
600 }
601 });
602
603 // prevent mouse button from selecting a filler tree item
72bf2314
MK
604 tree.addListener(SWT.MouseDown, event -> {
605 TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
606 if (treeItem == null || treeItem.getData() == FILLER) {
607 event.doit = false;
608 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
609 if (treeItems.size() == 0) {
837a2f8c
PT
610 fTreeViewer.setSelection(new StructuredSelection());
611 fTimeGraphViewer.setSelection(null);
72bf2314 612 return;
837a2f8c 613 }
72bf2314
MK
614 // this prevents from scrolling up when selecting
615 // the partially visible tree item at the bottom
616 tree.select(treeItems.get(treeItems.size() - 1));
617 fTreeViewer.setSelection(new StructuredSelection());
618 fTimeGraphViewer.setSelection(null);
837a2f8c
PT
619 }
620 });
621
622 // prevent mouse wheel from scrolling down into filler tree items
72bf2314
MK
623 tree.addListener(SWT.MouseWheel, event -> {
624 event.doit = false;
625 if (event.count == 0) {
626 return;
837a2f8c 627 }
72bf2314
MK
628 Slider scrollBar = fTimeGraphViewer.getVerticalBar();
629 fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
630 alignTreeItems(false);
837a2f8c
PT
631 });
632
633 // prevent key stroke from selecting a filler tree item
72bf2314
MK
634 tree.addListener(SWT.KeyDown, event -> {
635 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
636 if (treeItems.size() == 0) {
637 fTreeViewer.setSelection(new StructuredSelection());
638 event.doit = false;
639 return;
837a2f8c 640 }
72bf2314
MK
641 if (event.keyCode == SWT.ARROW_DOWN) {
642 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
643 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
644 event.doit = false;
645 } else if (event.keyCode == SWT.PAGE_DOWN) {
646 int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
647 int countPerPage = height / getItemHeight(tree, false);
648 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
649 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
650 event.doit = false;
651 } else if (event.keyCode == SWT.END) {
652 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
653 event.doit = false;
654 } else if ((event.character == '+' || event.character == '=') && ((event.stateMask & SWT.CTRL) != 0)) {
655 fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
656 return;
657 } else if (event.character == '-' && ((event.stateMask & SWT.CTRL) != 0)) {
658 fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
659 return;
660 } else if (event.character == '0' && ((event.stateMask & SWT.CTRL) != 0)) {
661 fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
662 return;
663 } else {
664 return;
665 }
666 if (fTimeGraphViewer.getSelectionIndex() >= 0) {
667 fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
668 } else {
669 fTreeViewer.setSelection(new StructuredSelection());
670 }
671 alignTreeItems(false);
837a2f8c
PT
672 });
673
674 // ensure alignment of top item between tree and time graph
675 fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {
676 @Override
677 public void controlResized(ControlEvent e) {
588dff10 678 alignTreeItems(false);
837a2f8c
PT
679 }
680 });
681
682 // ensure synchronization of selected item between tree and time graph
72bf2314
MK
683 fTreeViewer.addSelectionChangedListener(event -> {
684 if (fInhibitTreeSelection) {
685 return;
686 }
687 if (event.getSelection() instanceof IStructuredSelection) {
688 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
689 if (selection instanceof ITimeGraphEntry) {
690 fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
837a2f8c 691 }
72bf2314 692 alignTreeItems(false);
837a2f8c
PT
693 }
694 });
695
696 // ensure synchronization of selected item between tree and time graph
72bf2314
MK
697 fTimeGraphViewer.addSelectionListener(event -> {
698 ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
699 fInhibitTreeSelection = true; // block the tree selection changed listener
700 if (entry != null) {
701 StructuredSelection selection = new StructuredSelection(entry);
702 fTreeViewer.setSelection(selection);
703 } else {
704 fTreeViewer.setSelection(new StructuredSelection());
837a2f8c 705 }
72bf2314
MK
706 fInhibitTreeSelection = false;
707 alignTreeItems(false);
837a2f8c
PT
708 });
709
710 // ensure alignment of top item between tree and time graph
711 fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
712 @Override
713 public void widgetSelected(SelectionEvent e) {
588dff10 714 alignTreeItems(false);
837a2f8c
PT
715 }
716 });
717
718 // ensure alignment of top item between tree and time graph
72bf2314
MK
719 fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(e -> {
720 if (e.count == 0) {
721 return;
837a2f8c 722 }
72bf2314 723 alignTreeItems(false);
837a2f8c
PT
724 });
725
726 // ensure the tree has focus control when mouse is over it if the time graph had control
727 fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {
728 @Override
729 public void mouseEnter(MouseEvent e) {
730 if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {
731 fTreeViewer.getControl().setFocus();
732 }
733 }
734 });
735
736 // ensure the time graph has focus control when mouse is over it if the tree had control
737 fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
738 @Override
739 public void mouseEnter(MouseEvent e) {
740 if (fTreeViewer.getControl().isFocusControl()) {
741 fTimeGraphViewer.getTimeGraphControl().setFocus();
742 }
743 }
744 });
745 fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
746 @Override
747 public void mouseEnter(MouseEvent e) {
748 if (fTreeViewer.getControl().isFocusControl()) {
749 fTimeGraphViewer.getTimeGraphControl().setFocus();
750 }
751 }
752 });
753
754 // The filler rows are required to ensure alignment when the tree does not have a
755 // visible horizontal scroll bar. The tree does not allow its top item to be set
756 // to a value that would cause blank space to be drawn at the bottom of the tree.
3bd20aa6 757 fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree, false);
837a2f8c 758
d2e4afa7
MAL
759 fSashForm.setWeights(weights);
760
761 fTimeGraphViewer.getTimeGraphControl().addPaintListener(new PaintListener() {
762 @Override
763 public void paintControl(PaintEvent e) {
764 // Sashes in a SashForm are being created on layout so add the
765 // drag listener here
766 if (fSashDragListener == null) {
767 for (Control control : fSashForm.getChildren()) {
768 if (control instanceof Sash) {
769 fSashDragListener = new Listener() {
770
771 @Override
772 public void handleEvent(Event event) {
773 sendTimeViewAlignmentChanged();
774
775 }
776 };
777 control.removePaintListener(this);
778 control.addListener(SWT.Selection, fSashDragListener);
779 // There should be only one sash
780 break;
781 }
782 }
783 }
784 }
785 });
786 }
787
83d0971d 788 private void verticalZoom(boolean zoomIn) {
3bd20aa6
PT
789 Tree tree = fTreeViewer.getTree();
790 FontData fontData = tree.getFont().getFontData()[0];
791 int height = fontData.getHeight() + (zoomIn ? 1 : -1);
792 if (height <= 0) {
83d0971d 793 return;
3bd20aa6
PT
794 }
795 fontData.setHeight(height);
796 if (fTreeFont != null) {
797 fTreeFont.dispose();
798 }
799 fTreeFont = new Font(tree.getDisplay(), fontData);
800 tree.setFont(fTreeFont);
801 redraw();
802 update();
803 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
804 fTimeGraphViewer.setItemHeight(getItemHeight(tree, true));
805 alignTreeItems(false);
3bd20aa6
PT
806 }
807
808 private void resetVerticalZoom() {
809 Tree tree = fTreeViewer.getTree();
810 if (fTreeFont != null) {
811 fTreeFont.dispose();
812 fTreeFont = null;
813 }
814 tree.setFont(null);
815 redraw();
816 update();
817 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
818 fTimeGraphViewer.setItemHeight(getItemHeight(tree, true));
819 alignTreeItems(false);
3bd20aa6
PT
820 }
821
d2e4afa7
MAL
822 private void sendTimeViewAlignmentChanged() {
823 TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm, getTimeViewAlignmentInfo()));
837a2f8c
PT
824 }
825
826 // ------------------------------------------------------------------------
827 // Accessors
828 // ------------------------------------------------------------------------
829
830 /**
831 * Returns this time graph combo's tree viewer.
832 *
833 * @return the tree viewer
834 */
835 public TreeViewer getTreeViewer() {
836 return fTreeViewer;
837 }
838
839 /**
840 * Returns this time graph combo's time graph viewer.
841 *
842 * @return the time graph viewer
843 */
dfa0ef96 844 public @NonNull TimeGraphViewer getTimeGraphViewer() {
837a2f8c
PT
845 return fTimeGraphViewer;
846 }
847
6ac5a950 848 /**
cfcfd964
PT
849 * Get the show filter dialog action.
850 *
851 * @return The Action object
852 * @since 2.0
6ac5a950 853 */
cfcfd964
PT
854 public ShowFilterDialogAction getShowFilterDialogAction() {
855 if (fShowFilterDialogAction == null) {
856 fShowFilterDialogAction = new ShowFilterDialogAction(fTimeGraphViewer) {
857 @Override
858 protected void addFilter(ViewerFilter filter) {
859 /* add filter to the combo instead of the viewer */
860 TimeGraphCombo.this.addFilter(filter);
8f28f9d8 861 }
cfcfd964
PT
862
863 @Override
864 protected void removeFilter(ViewerFilter filter) {
865 /* remove filter from the combo instead of the viewer */
866 TimeGraphCombo.this.removeFilter(filter);
6ac5a950 867 }
6ac5a950 868
6ac5a950 869 @Override
cfcfd964
PT
870 protected void refresh() {
871 /* refresh the combo instead of the viewer */
872 TimeGraphCombo.this.refresh();
6ac5a950
AM
873 }
874 };
6ac5a950 875 }
cfcfd964 876 return fShowFilterDialogAction;
6ac5a950
AM
877 }
878
837a2f8c
PT
879 // ------------------------------------------------------------------------
880 // Control
881 // ------------------------------------------------------------------------
882
837a2f8c
PT
883 @Override
884 public void redraw() {
885 fTimeGraphViewer.getControl().redraw();
886 super.redraw();
887 }
888
3bd20aa6
PT
889 @Override
890 public void update() {
891 fTimeGraphViewer.getControl().update();
892 super.update();
893 }
894
837a2f8c
PT
895 // ------------------------------------------------------------------------
896 // Operations
897 // ------------------------------------------------------------------------
898
899 /**
900 * Sets the tree content provider used by this time graph combo.
901 *
902 * @param contentProvider the tree content provider
903 */
904 public void setTreeContentProvider(ITreeContentProvider contentProvider) {
905 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
906 }
907
908 /**
909 * Sets the tree label provider used by this time graph combo.
910 *
911 * @param labelProvider the tree label provider
912 */
913 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
914 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
915 }
916
6ac5a950
AM
917 /**
918 * Sets the tree content provider used by the filter dialog
919 *
920 * @param contentProvider the tree content provider
6ac5a950
AM
921 */
922 public void setFilterContentProvider(ITreeContentProvider contentProvider) {
cfcfd964 923 getShowFilterDialogAction().getFilterDialog().setContentProvider(contentProvider);
6ac5a950
AM
924 }
925
926 /**
927 * Sets the tree label provider used by the filter dialog
928 *
929 * @param labelProvider the tree label provider
6ac5a950
AM
930 */
931 public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
cfcfd964 932 getShowFilterDialogAction().getFilterDialog().setLabelProvider(labelProvider);
6ac5a950
AM
933 }
934
b97d61f0
CM
935 /**
936 * Adds a "check active" button used by the filter dialog
937 *
938 * @param activeProvider
939 * Additional button info specific to a certain view.
940 * @since 1.0
941 */
942 public void addTimeGraphFilterCheckActiveButton(ITimeGraphEntryActiveProvider activeProvider) {
cfcfd964 943 getShowFilterDialogAction().getFilterDialog().addTimeGraphFilterCheckActiveButton(activeProvider);
b97d61f0
CM
944 }
945
946 /**
947 * Adds an "uncheck inactive" button used by the filter dialog
948 *
949 * @param inactiveProvider
950 * Additional button info specific to a certain view.
951 * @since 1.0
952 */
953 public void addTimeGraphFilterUncheckInactiveButton(ITimeGraphEntryActiveProvider inactiveProvider) {
cfcfd964 954 getShowFilterDialogAction().getFilterDialog().addTimeGraphFilterUncheckInactiveButton(inactiveProvider);
b97d61f0
CM
955 }
956
837a2f8c
PT
957 /**
958 * Sets the tree columns for this time graph combo.
959 *
960 * @param columnNames the tree column names
961 */
962 public void setTreeColumns(String[] columnNames) {
963 final Tree tree = fTreeViewer.getTree();
964 for (String columnName : columnNames) {
965 TreeColumn column = new TreeColumn(tree, SWT.LEFT);
bfb2be9d 966 column.setMoveable(true);
837a2f8c
PT
967 column.setText(columnName);
968 column.pack();
969 }
970 }
971
6ac5a950
AM
972 /**
973 * Sets the tree columns for this time graph combo's filter dialog.
974 *
975 * @param columnNames the tree column names
6ac5a950
AM
976 */
977 public void setFilterColumns(String[] columnNames) {
cfcfd964 978 getShowFilterDialogAction().getFilterDialog().setColumnNames(columnNames);
6ac5a950
AM
979 }
980
837a2f8c 981 /**
4c9c0c87
PT
982 * Sets the time graph content provider used by this time graph combo.
983 *
984 * @param timeGraphContentProvider
985 * the time graph content provider
4c9c0c87
PT
986 */
987 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider) {
988 fTimeGraphViewer.setTimeGraphContentProvider(timeGraphContentProvider);
989 }
990
991 /**
992 * Sets the time graph presentation provider used by this time graph combo.
837a2f8c
PT
993 *
994 * @param timeGraphProvider the time graph provider
995 */
996 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
997 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
998 }
999
1000 /**
1001 * Sets or clears the input for this time graph combo.
837a2f8c
PT
1002 *
1003 * @param input the input of this time graph combo, or <code>null</code> if none
1004 */
4c9c0c87 1005 public void setInput(Object input) {
837a2f8c
PT
1006 fInhibitTreeSelection = true;
1007 fTreeViewer.setInput(input);
1008 for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
1009 listenerWrapper.selection = null;
1010 }
1011 fInhibitTreeSelection = false;
f6de5bef
MAL
1012 if (fScrollBarsInTreeWorkaround) {
1013 fTreeViewer.getTree().getVerticalBar().setEnabled(false);
1014 fTreeViewer.getTree().getVerticalBar().setVisible(false);
1015 }
837a2f8c 1016 fTimeGraphViewer.setInput(input);
3bd20aa6 1017 fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree(), false));
588dff10
PT
1018 // queue the alignment update because in Linux the item bounds are not
1019 // set properly until the tree has been painted at least once
1020 fVisibleExpandedItems = null; // invalidate the cache
1021 getDisplay().asyncExec(new Runnable() {
1022 @Override
1023 public void run() {
1024 alignTreeItems(true);
1025 }});
837a2f8c
PT
1026 }
1027
4c9c0c87
PT
1028 /**
1029 * Gets the input for this time graph combo.
1030 *
1031 * @return The input of this time graph combo, or <code>null</code> if none
4c9c0c87
PT
1032 */
1033 public Object getInput() {
1034 return fTreeViewer.getInput();
1035 }
1036
bec1f1ac
GB
1037 /**
1038 * Sets or clears the list of links to display on this combo
1039 *
1040 * @param links the links to display in this time graph combo
bec1f1ac
GB
1041 */
1042 public void setLinks(List<ILinkEvent> links) {
1043 fTimeGraphViewer.setLinks(links);
1044 }
1045
6ac5a950
AM
1046 /**
1047 * @param filter The filter object to be attached to the view
6ac5a950 1048 */
367e2932 1049 public void addFilter(@NonNull ViewerFilter filter) {
cfcfd964 1050 fInhibitTreeSelection = true;
6ac5a950
AM
1051 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
1052 fTreeViewer.addFilter(wrapper);
4923d7b9 1053 fTimeGraphViewer.addFilter(filter);
6ac5a950 1054 fViewerFilterMap.put(filter, wrapper);
588dff10 1055 alignTreeItems(true);
cfcfd964 1056 fInhibitTreeSelection = false;
6ac5a950
AM
1057 }
1058
1059 /**
1060 * @param filter The filter object to be removed from the view
6ac5a950 1061 */
367e2932 1062 public void removeFilter(@NonNull ViewerFilter filter) {
cfcfd964 1063 fInhibitTreeSelection = true;
6ac5a950
AM
1064 ViewerFilter wrapper = fViewerFilterMap.get(filter);
1065 fTreeViewer.removeFilter(wrapper);
4923d7b9 1066 fTimeGraphViewer.removeFilter(filter);
6ac5a950 1067 fViewerFilterMap.remove(filter);
588dff10 1068 alignTreeItems(true);
cfcfd964 1069 fInhibitTreeSelection = false;
6ac5a950
AM
1070 }
1071
4923d7b9
PT
1072 /**
1073 * Returns this viewer's filters.
1074 *
1075 * @return an array of viewer filters
1076 * @since 2.0
1077 */
367e2932 1078 public @NonNull ViewerFilter[] getFilters() {
4923d7b9
PT
1079 return fTimeGraphViewer.getFilters();
1080 }
1081
1082 /**
1083 * Sets the filters, replacing any previous filters, and triggers
1084 * refiltering of the elements.
1085 *
1086 * @param filters
1087 * an array of viewer filters, or null
1088 * @since 2.0
1089 */
367e2932 1090 public void setFilters(@NonNull ViewerFilter[] filters) {
cfcfd964 1091 fInhibitTreeSelection = true;
4923d7b9
PT
1092 fViewerFilterMap.clear();
1093 if (filters == null) {
1094 fTreeViewer.resetFilters();
1095 } else {
1096 for (ViewerFilter filter : filters) {
1097 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
1098 fViewerFilterMap.put(filter, wrapper);
1099 }
1100 ViewerFilter[] wrappers = Iterables.toArray(fViewerFilterMap.values(), ViewerFilter.class);
1101 fTreeViewer.setFilters(wrappers);
1102 }
1103 fTimeGraphViewer.setFilters(filters);
1104 alignTreeItems(true);
cfcfd964 1105 fInhibitTreeSelection = false;
4923d7b9
PT
1106 }
1107
837a2f8c
PT
1108 /**
1109 * Refreshes this time graph completely with information freshly obtained from its model.
1110 */
1111 public void refresh() {
1112 fInhibitTreeSelection = true;
4c9c0c87 1113 Tree tree = fTreeViewer.getTree();
ece2fc5f
PT
1114 try {
1115 tree.setRedraw(false);
1116 fTreeViewer.refresh();
ece2fc5f
PT
1117 } finally {
1118 tree.setRedraw(true);
1119 }
837a2f8c 1120 fTimeGraphViewer.refresh();
588dff10 1121 alignTreeItems(true);
837a2f8c
PT
1122 fInhibitTreeSelection = false;
1123 }
1124
1125 /**
1126 * Adds a listener for selection changes in this time graph combo.
1127 *
1128 * @param listener a selection listener
1129 */
1130 public void addSelectionListener(ITimeGraphSelectionListener listener) {
1131 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
1132 fTreeViewer.addSelectionChangedListener(listenerWrapper);
1133 fSelectionListenerMap.put(listener, listenerWrapper);
1134 fTimeGraphViewer.addSelectionListener(listenerWrapper);
1135 }
1136
1137 /**
1138 * Removes the given selection listener from this time graph combo.
1139 *
1140 * @param listener a selection changed listener
1141 */
1142 public void removeSelectionListener(ITimeGraphSelectionListener listener) {
1143 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
1144 fTreeViewer.removeSelectionChangedListener(listenerWrapper);
1145 fTimeGraphViewer.removeSelectionListener(listenerWrapper);
1146 }
1147
1148 /**
1149 * Sets the current selection for this time graph combo.
1150 *
1151 * @param selection the new selection
1152 */
1153 public void setSelection(ITimeGraphEntry selection) {
1154 fTimeGraphViewer.setSelection(selection);
1155 fInhibitTreeSelection = true; // block the tree selection changed listener
1156 if (selection != null) {
1157 StructuredSelection structuredSelection = new StructuredSelection(selection);
1158 fTreeViewer.setSelection(structuredSelection);
1159 } else {
1160 fTreeViewer.setSelection(new StructuredSelection());
1161 }
1162 fInhibitTreeSelection = false;
588dff10 1163 alignTreeItems(false);
837a2f8c
PT
1164 }
1165
f4617471 1166 /**
df0e3d5f
PT
1167 * Sets the auto-expand level to be used for new entries discovered when
1168 * calling {@link #setInput(Object)} or {@link #refresh()}. The value 0
1169 * means that there is no auto-expand; 1 means that top-level entries are
1170 * expanded, but not their children; 2 means that top-level entries are
1171 * expanded, and their children, but not grand-children; and so on.
f4617471
PT
1172 * <p>
1173 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
1174 * </p>
df0e3d5f 1175 *
f4617471
PT
1176 * @param level
1177 * non-negative level, or <code>ALL_LEVELS</code> to expand all
1178 * levels of the tree
f4617471
PT
1179 */
1180 public void setAutoExpandLevel(int level) {
1181 fTimeGraphViewer.setAutoExpandLevel(level);
1182 if (level <= 0) {
1183 fTreeViewer.setAutoExpandLevel(level);
1184 } else {
1185 fTreeViewer.setAutoExpandLevel(level + 1);
1186 }
1187 }
1188
1189 /**
1190 * Returns the auto-expand level.
1191 *
1192 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
1193 * the tree are expanded automatically
1194 * @see #setAutoExpandLevel
f4617471
PT
1195 */
1196 public int getAutoExpandLevel() {
1197 return fTimeGraphViewer.getAutoExpandLevel();
1198 }
1199
82467e47
BH
1200 /**
1201 * Get the expanded state of an entry.
1202 *
1203 * @param entry
1204 * The entry
1205 * @return true if the entry is expanded, false if collapsed
1206 * @since 2.0
1207 */
1208 public boolean getExpandedState(ITimeGraphEntry entry) {
1209 return fTimeGraphViewer.getExpandedState(entry);
1210 }
1211
837a2f8c
PT
1212 /**
1213 * Set the expanded state of an entry
1214 *
1215 * @param entry
1216 * The entry to expand/collapse
1217 * @param expanded
1218 * True for expanded, false for collapsed
837a2f8c
PT
1219 */
1220 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
1221 fTimeGraphViewer.setExpandedState(entry, expanded);
1222 fTreeViewer.setExpandedState(entry, expanded);
588dff10 1223 alignTreeItems(true);
837a2f8c
PT
1224 }
1225
1226 /**
1227 * Collapses all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
1228 */
1229 public void collapseAll() {
1230 fTimeGraphViewer.collapseAll();
1231 fTreeViewer.collapseAll();
588dff10 1232 alignTreeItems(true);
837a2f8c
PT
1233 }
1234
1235 /**
1236 * Expands all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
1237 */
1238 public void expandAll() {
1239 fTimeGraphViewer.expandAll();
1240 fTreeViewer.expandAll();
588dff10 1241 alignTreeItems(true);
837a2f8c
PT
1242 }
1243
1244 // ------------------------------------------------------------------------
1245 // Internal
1246 // ------------------------------------------------------------------------
1247
588dff10
PT
1248 private List<TreeItem> getVisibleExpandedItems(Tree tree, boolean refresh) {
1249 if (fVisibleExpandedItems == null || refresh) {
df0e3d5f
PT
1250 List<TreeItem> visibleExpandedItems = new ArrayList<>();
1251 addVisibleExpandedItems(visibleExpandedItems, tree.getItems());
1252 fVisibleExpandedItems = visibleExpandedItems;
837a2f8c 1253 }
588dff10 1254 return fVisibleExpandedItems;
837a2f8c
PT
1255 }
1256
df0e3d5f
PT
1257 private void addVisibleExpandedItems(List<TreeItem> visibleExpandedItems, TreeItem[] items) {
1258 for (TreeItem item : items) {
1259 Object data = item.getData();
1260 if (data == FILLER) {
1261 break;
1262 }
1263 visibleExpandedItems.add(item);
1264 boolean expandedState = fTimeGraphViewer.getExpandedState((ITimeGraphEntry) data);
1265 if (item.getExpanded() != expandedState) {
1266 /* synchronize the expanded state of both viewers */
1267 fTreeViewer.setExpandedState(data, expandedState);
1268 }
1269 if (expandedState) {
1270 addVisibleExpandedItems(visibleExpandedItems, item.getItems());
837a2f8c
PT
1271 }
1272 }
837a2f8c
PT
1273 }
1274
3bd20aa6 1275 private int getItemHeight(final Tree tree, boolean force) {
837a2f8c
PT
1276 /*
1277 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1278 */
1279 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
3bd20aa6 1280 if (fLinuxItemHeight != 0 && !force) {
837a2f8c
PT
1281 return fLinuxItemHeight;
1282 }
ff2990ed
BH
1283
1284 if (getVisibleExpandedItems(tree, true).size() > 1) {
837a2f8c
PT
1285 PaintListener paintListener = new PaintListener() {
1286 @Override
1287 public void paintControl(PaintEvent e) {
ff2990ed
BH
1288 // get the treeItems here to have all items
1289 List<TreeItem> treeItems = getVisibleExpandedItems(tree, true);
1290 if (treeItems.size() < 2) {
1291 return;
1292 }
1293 final TreeItem treeItem0 = treeItems.get(0);
1294 final TreeItem treeItem1 = treeItems.get(1);
837a2f8c
PT
1295 tree.removePaintListener(this);
1296 int y0 = treeItem0.getBounds().y;
1297 int y1 = treeItem1.getBounds().y;
1298 int itemHeight = y1 - y0;
1299 if (itemHeight > 0) {
1300 fLinuxItemHeight = itemHeight;
1301 fTimeGraphViewer.setItemHeight(itemHeight);
1302 }
1303 }
1304 };
1305 tree.addPaintListener(paintListener);
1306 }
1307 } else {
1308 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
1309 }
1310 return tree.getItemHeight();
1311 }
1312
588dff10 1313 private void alignTreeItems(boolean refreshExpandedItems) {
1e5c4376 1314
588dff10
PT
1315 // align the tree top item with the time graph top item
1316 Tree tree = fTreeViewer.getTree();
1317 List<TreeItem> treeItems = getVisibleExpandedItems(tree, refreshExpandedItems);
1318 int topIndex = fTimeGraphViewer.getTopIndex();
1319 if (topIndex >= treeItems.size()) {
1320 return;
1321 }
1322 TreeItem item = treeItems.get(topIndex);
1323 tree.setTopItem(item);
4e1ebbca
PT
1324 /*
1325 * In GTK3, the bounds of the tree items are only sure to be correct
1326 * after the tree has been painted.
1327 */
1328 tree.addPaintListener(new PaintListener() {
1329 @Override
1330 public void paintControl(PaintEvent e) {
1331 tree.removePaintListener(this);
1332 doAlignTreeItems();
1333 redraw();
179d1473
PT
1334 /*
1335 * Bug in GTK. Calling setTopItem() can scroll to the wrong item
1336 * when the 'tree view' is dirty. Set it again once it is clean.
1337 */
1338 if (SWT.getPlatform().equals("gtk")) { //$NON-NLS-1$
179d1473 1339 tree.getDisplay().asyncExec(() -> {
1e5c4376
BH
1340 TreeItem topItem = tree.getTopItem();
1341 if (!tree.isDisposed() && topItem != null && !topItem.isDisposed()) {
359af2ed
PT
1342 tree.setTopItem(topItem);
1343 }
179d1473
PT
1344 });
1345 }
4e1ebbca
PT
1346 }
1347 });
1348 /* Make sure the paint event is triggered. */
1349 tree.redraw();
1350 }
1351
1352 private void doAlignTreeItems() {
1353 Tree tree = fTreeViewer.getTree();
1354 List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
1355 int topIndex = fTimeGraphViewer.getTopIndex();
1356 if (topIndex >= treeItems.size()) {
1357 return;
1358 }
1359 TreeItem item = treeItems.get(topIndex);
1360
3bd20aa6
PT
1361 // get the first filler item so we can calculate the last item's height
1362 TreeItem fillerItem = null;
1363 for (TreeItem treeItem : fTreeViewer.getTree().getItems()) {
1364 if (treeItem.getData() == FILLER) {
1365 fillerItem = treeItem;
1366 break;
1367 }
1368 }
1369
588dff10
PT
1370 // ensure the time graph item heights are equal to the tree item heights
1371 int treeHeight = fTreeViewer.getTree().getBounds().height;
1372 int index = topIndex;
1373 Rectangle bounds = item.getBounds();
3bd20aa6 1374 while (index < treeItems.size()) {
588dff10
PT
1375 if (bounds.y > treeHeight) {
1376 break;
1377 }
3bd20aa6
PT
1378 TreeItem nextItem = (index + 1 == treeItems.size()) ? fillerItem : treeItems.get(index + 1);
1379 Rectangle nextBounds = alignTreeItem(item, bounds, nextItem);
588dff10
PT
1380 index++;
1381 item = nextItem;
1382 bounds = nextBounds;
1383 }
3bd20aa6
PT
1384
1385 /*
1386 * When an item's height in the time graph changes, it is possible that
1387 * the time graph readjusts its top index to fill empty space at the
1388 * bottom of the viewer. Calling method setTopIndex() triggers this
1389 * adjustment, if needed. In that case, we need to make sure that the
1390 * newly visible items at the top of the viewer are also aligned.
1391 */
1392 fTimeGraphViewer.setTopIndex(topIndex);
4e1ebbca
PT
1393 if (fTimeGraphViewer.getTopIndex() != topIndex) {
1394 alignTreeItems(false);
3bd20aa6
PT
1395 }
1396 }
1397
1398 private Rectangle alignTreeItem(TreeItem item, Rectangle bounds, TreeItem nextItem) {
1399 /*
1400 * Bug in Linux. The method getBounds doesn't always return the correct height.
1401 * Use the difference of y position between items to calculate the height.
1402 */
1403 Rectangle nextBounds = nextItem.getBounds();
1404 Integer itemHeight = nextBounds.y - bounds.y;
1405 if (itemHeight > 0) {
1406 ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
1407 fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight);
1408 }
1409 return nextBounds;
588dff10
PT
1410 }
1411
d2e4afa7
MAL
1412 /**
1413 * Return the time alignment information
1414 *
1415 * @return the time alignment information
1416 *
1417 * @see ITmfTimeAligned
1418 *
1419 * @since 1.0
1420 */
1421 public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
d2e4afa7 1422 Point location = fSashForm.toDisplay(0, 0);
2e23a015
PT
1423 int timeAxisOffset = fTreeViewer.getControl().getSize().x + fSashForm.getSashWidth();
1424 return new TmfTimeViewAlignmentInfo(fSashForm.getShell(), location, timeAxisOffset);
d2e4afa7
MAL
1425 }
1426
1427 /**
1428 * Return the available width for the time-axis.
1429 *
1430 * @see ITmfTimeAligned
1431 *
1432 * @param requestedOffset
1433 * the requested offset
1434 * @return the available width for the time-axis
1435 *
1436 * @since 1.0
1437 */
1438 public int getAvailableWidth(int requestedOffset) {
89316ebe 1439 int vBarWidth = ((fTimeGraphViewer.getVerticalBar() != null) && (fTimeGraphViewer.getVerticalBar().isVisible())) ? fTimeGraphViewer.getVerticalBar().getSize().x : 0;
d2e4afa7 1440 int totalWidth = fSashForm.getBounds().width;
89316ebe 1441 return Math.min(totalWidth, Math.max(0, totalWidth - requestedOffset - vBarWidth));
d2e4afa7
MAL
1442 }
1443
1444 /**
1445 * Perform the alignment operation.
1446 *
1447 * @param offset
1448 * the alignment offset
1449 * @param width
1450 * the alignment width
1451 *
1452 * @see ITmfTimeAligned
1453 *
1454 * @since 1.0
1455 */
1456 public void performAlign(int offset, int width) {
1457 int total = fSashForm.getBounds().width;
1458 int timeAxisOffset = Math.min(offset, total);
c34ab48a
PT
1459 int width1 = Math.max(0, timeAxisOffset - fSashForm.getSashWidth());
1460 int width2 = total - timeAxisOffset;
40f88571
MAL
1461 if (width1 >= 0 && width2 > 0 || width1 > 0 && width2 >= 0) {
1462 fSashForm.setWeights(new int[] { width1, width2 });
1463 fSashForm.layout();
1464 }
d2e4afa7
MAL
1465
1466 Composite composite = fTimeGraphViewer.getTimeAlignedComposite();
1467 GridLayout layout = (GridLayout) composite.getLayout();
1468 int timeBasedControlsWidth = composite.getSize().x;
1469 int marginSize = timeBasedControlsWidth - width;
1470 layout.marginRight = Math.max(0, marginSize);
1471 composite.layout();
1472 }
837a2f8c 1473}
This page took 0.161273 seconds and 5 git commands to generate.