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