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