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