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