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