TMF: Update the target platforms
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / timegraph / TimeGraphCombo.java
CommitLineData
837a2f8c 1/*******************************************************************************
f8840316 2 * Copyright (c) 2012, 2013 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
837a2f8c
PT
13 *******************************************************************************/
14
15package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;
16
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.HashMap;
6ac5a950
AM
20import java.util.List;
21import java.util.Map;
837a2f8c 22
6ac5a950 23import org.eclipse.jface.action.Action;
837a2f8c
PT
24import org.eclipse.jface.viewers.ILabelProviderListener;
25import org.eclipse.jface.viewers.ISelectionChangedListener;
26import org.eclipse.jface.viewers.IStructuredSelection;
27import org.eclipse.jface.viewers.ITableLabelProvider;
28import org.eclipse.jface.viewers.ITreeContentProvider;
29import org.eclipse.jface.viewers.ITreeViewerListener;
30import org.eclipse.jface.viewers.SelectionChangedEvent;
31import org.eclipse.jface.viewers.StructuredSelection;
32import org.eclipse.jface.viewers.TreeExpansionEvent;
33import org.eclipse.jface.viewers.TreeViewer;
34import org.eclipse.jface.viewers.Viewer;
6ac5a950
AM
35import org.eclipse.jface.viewers.ViewerFilter;
36import org.eclipse.linuxtools.internal.tmf.ui.Activator;
37import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants;
38import org.eclipse.linuxtools.internal.tmf.ui.Messages;
39import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
bec1f1ac 40import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ILinkEvent;
837a2f8c
PT
41import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
42import org.eclipse.swt.SWT;
43import org.eclipse.swt.custom.SashForm;
44import org.eclipse.swt.events.ControlAdapter;
45import org.eclipse.swt.events.ControlEvent;
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;
53import org.eclipse.swt.graphics.Image;
54import org.eclipse.swt.graphics.Point;
55import org.eclipse.swt.layout.FillLayout;
56import org.eclipse.swt.widgets.Composite;
57import org.eclipse.swt.widgets.Display;
58import org.eclipse.swt.widgets.Event;
59import org.eclipse.swt.widgets.Listener;
60import org.eclipse.swt.widgets.Slider;
61import org.eclipse.swt.widgets.Tree;
62import org.eclipse.swt.widgets.TreeColumn;
63import org.eclipse.swt.widgets.TreeItem;
64
65/**
66 * Time graph "combo" view (with the list/tree on the left and the gantt chart
67 * on the right)
68 *
69 * @version 1.0
70 * @author Patrick Tasse
71 */
72public class TimeGraphCombo extends Composite {
73
74 // ------------------------------------------------------------------------
75 // Constants
76 // ------------------------------------------------------------------------
77
78 private static final Object FILLER = new Object();
79
c004295c
PT
80 private static final String ITEM_HEIGHT = "$height$"; //$NON-NLS-1$
81
837a2f8c
PT
82 // ------------------------------------------------------------------------
83 // Fields
84 // ------------------------------------------------------------------------
85
4999a196 86 /** The tree viewer */
837a2f8c
PT
87 private TreeViewer fTreeViewer;
88
4999a196 89 /** The time viewer */
837a2f8c
PT
90 private TimeGraphViewer fTimeGraphViewer;
91
4999a196 92 /** The top-level input (children excluded) */
6ac5a950
AM
93 private List<? extends ITimeGraphEntry> fTopInput;
94
4999a196 95 /** The selection listener map */
507b1336 96 private final Map<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<>();
837a2f8c 97
4999a196 98 /** The map of viewer filters */
507b1336 99 private final Map<ViewerFilter, ViewerFilter> fViewerFilterMap = new HashMap<>();
6ac5a950 100
4999a196
GB
101 /**
102 * Flag to block the tree selection changed listener when triggered by the
103 * time graph combo
104 */
837a2f8c
PT
105 private boolean fInhibitTreeSelection = false;
106
4999a196 107 /** Number of filler rows used by the tree content provider */
837a2f8c
PT
108 private int fNumFillerRows;
109
4999a196 110 /** Calculated item height for Linux workaround */
837a2f8c
PT
111 private int fLinuxItemHeight = 0;
112
4999a196 113 /** The button that opens the filter dialog */
6ac5a950
AM
114 private Action showFilterAction;
115
4999a196 116 /** The filter dialog */
6ac5a950
AM
117 private TimeGraphFilterDialog fFilterDialog;
118
4999a196 119 /** The filter generated from the filter dialog */
6ac5a950
AM
120 private RawViewerFilter fFilter;
121
4999a196
GB
122 /** Default weight of each part of the sash */
123 private static final int[] DEFAULT_WEIGHTS = { 1, 1 };
124
837a2f8c
PT
125 // ------------------------------------------------------------------------
126 // Classes
127 // ------------------------------------------------------------------------
128
129 /**
130 * The TreeContentProviderWrapper is used to insert filler items after
131 * the elements of the tree's real content provider.
132 */
133 private class TreeContentProviderWrapper implements ITreeContentProvider {
134 private final ITreeContentProvider contentProvider;
135
136 public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {
137 this.contentProvider = contentProvider;
138 }
139
140 @Override
141 public void dispose() {
142 contentProvider.dispose();
143 }
144
145 @Override
146 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
147 contentProvider.inputChanged(viewer, oldInput, newInput);
148 }
149
150 @Override
151 public Object[] getElements(Object inputElement) {
152 Object[] elements = contentProvider.getElements(inputElement);
153 // add filler elements to ensure alignment with time analysis viewer
f1fae91f 154 Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, Object[].class);
837a2f8c
PT
155 for (int i = 0; i < fNumFillerRows; i++) {
156 oElements[elements.length + i] = FILLER;
157 }
158 return oElements;
159 }
160
161 @Override
162 public Object[] getChildren(Object parentElement) {
163 if (parentElement instanceof ITimeGraphEntry) {
164 return contentProvider.getChildren(parentElement);
165 }
166 return new Object[0];
167 }
168
169 @Override
170 public Object getParent(Object element) {
171 if (element instanceof ITimeGraphEntry) {
172 return contentProvider.getParent(element);
173 }
174 return null;
175 }
176
177 @Override
178 public boolean hasChildren(Object element) {
179 if (element instanceof ITimeGraphEntry) {
180 return contentProvider.hasChildren(element);
181 }
182 return false;
183 }
184 }
185
186 /**
187 * The TreeLabelProviderWrapper is used to intercept the filler items
188 * from the calls to the tree's real label provider.
189 */
190 private class TreeLabelProviderWrapper implements ITableLabelProvider {
191 private final ITableLabelProvider labelProvider;
192
193 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
194 this.labelProvider = labelProvider;
195 }
196
197 @Override
198 public void addListener(ILabelProviderListener listener) {
199 labelProvider.addListener(listener);
200 }
201
202 @Override
203 public void dispose() {
204 labelProvider.dispose();
205 }
206
207 @Override
208 public boolean isLabelProperty(Object element, String property) {
209 if (element instanceof ITimeGraphEntry) {
210 return labelProvider.isLabelProperty(element, property);
211 }
212 return false;
213 }
214
215 @Override
216 public void removeListener(ILabelProviderListener listener) {
217 labelProvider.removeListener(listener);
218 }
219
220 @Override
221 public Image getColumnImage(Object element, int columnIndex) {
222 if (element instanceof ITimeGraphEntry) {
223 return labelProvider.getColumnImage(element, columnIndex);
224 }
225 return null;
226 }
227
228 @Override
229 public String getColumnText(Object element, int columnIndex) {
230 if (element instanceof ITimeGraphEntry) {
231 return labelProvider.getColumnText(element, columnIndex);
232 }
233 return null;
234 }
235
236 }
237
238 /**
239 * The SelectionListenerWrapper is used to intercept the filler items from
240 * the time graph combo's real selection listener, and to prevent double
241 * notifications from being sent when selection changes in both tree and
242 * time graph at the same time.
243 */
244 private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {
245 private final ITimeGraphSelectionListener listener;
246 private ITimeGraphEntry selection = null;
247
248 public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {
249 this.listener = listener;
250 }
251
252 @Override
253 public void selectionChanged(SelectionChangedEvent event) {
254 if (fInhibitTreeSelection) {
255 return;
256 }
257 Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
258 if (element instanceof ITimeGraphEntry) {
259 ITimeGraphEntry entry = (ITimeGraphEntry) element;
260 if (entry != selection) {
261 selection = entry;
262 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
263 }
264 }
265 }
266
267 @Override
268 public void selectionChanged(TimeGraphSelectionEvent event) {
269 ITimeGraphEntry entry = event.getSelection();
270 if (entry != selection) {
271 selection = entry;
272 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
273 }
274 }
275 }
276
6ac5a950
AM
277 /**
278 * The ViewerFilterWrapper is used to intercept the filler items from
279 * the time graph combo's real ViewerFilters. These filler items should
280 * always be visible.
281 */
282 private class ViewerFilterWrapper extends ViewerFilter {
283
f1fae91f 284 private ViewerFilter fWrappedFilter;
6ac5a950
AM
285
286 ViewerFilterWrapper(ViewerFilter filter) {
287 super();
288 this.fWrappedFilter = filter;
289 }
290
291 @Override
292 public boolean select(Viewer viewer, Object parentElement, Object element) {
293 if (element instanceof ITimeGraphEntry) {
294 return fWrappedFilter.select(viewer, parentElement, element);
295 }
296 return true;
297 }
298
299 }
300
301 /**
f8840316
FR
302 * This filter simply keeps a list of elements that should be filtered out.
303 * All the other elements will be shown.
8f28f9d8 304 * By default and when the list is set to null, all elements are shown.
6ac5a950
AM
305 */
306 private class RawViewerFilter extends ViewerFilter {
307
f8840316 308 private List<Object> fFiltered = null;
6ac5a950 309
f8840316
FR
310 public void setFiltered(List<Object> objects) {
311 fFiltered = objects;
6ac5a950
AM
312 }
313
f8840316
FR
314 public List<Object> getFiltered() {
315 return fFiltered;
6ac5a950
AM
316 }
317
318 @Override
319 public boolean select(Viewer viewer, Object parentElement, Object element) {
f8840316 320 if (fFiltered == null) {
8f28f9d8
PT
321 return true;
322 }
f8840316 323 return !fFiltered.contains(element);
6ac5a950
AM
324 }
325 }
326
837a2f8c
PT
327 // ------------------------------------------------------------------------
328 // Constructors
329 // ------------------------------------------------------------------------
330
331 /**
332 * Constructs a new instance of this class given its parent
333 * and a style value describing its behavior and appearance.
334 *
335 * @param parent a widget which will be the parent of the new instance (cannot be null)
336 * @param style the style of widget to construct
337 */
338 public TimeGraphCombo(Composite parent, int style) {
4999a196
GB
339 this(parent, style, DEFAULT_WEIGHTS);
340 }
341
342 /**
343 * Constructs a new instance of this class given its parent and a style
344 * value describing its behavior and appearance.
345 *
346 * @param parent
347 * a widget which will be the parent of the new instance (cannot
348 * be null)
349 * @param style
350 * the style of widget to construct
351 * @param weights
352 * The relative weights of each side of the sash form
353 * @since 2.1
354 */
355 public TimeGraphCombo(Composite parent, int style, int[] weights) {
837a2f8c
PT
356 super(parent, style);
357 setLayout(new FillLayout());
358
359 final SashForm sash = new SashForm(this, SWT.NONE);
360
361 fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);
362 final Tree tree = fTreeViewer.getTree();
363 tree.setHeaderVisible(true);
364 tree.setLinesVisible(true);
365
366 fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);
367 fTimeGraphViewer.setItemHeight(getItemHeight(tree));
368 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
369 fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
370 fTimeGraphViewer.setNameWidthPref(0);
371
6ac5a950
AM
372 fFilter = new RawViewerFilter();
373 addFilter(fFilter);
374
375 fFilterDialog = new TimeGraphFilterDialog(getShell());
376
837a2f8c
PT
377 // Feature in Windows. The tree vertical bar reappears when
378 // the control is resized so we need to hide it again.
379 // Bug in Linux. The tree header height is 0 in constructor,
380 // so we need to reset it later when the control is resized.
381 tree.addControlListener(new ControlAdapter() {
f1fae91f 382 private int depth = 0;
837a2f8c
PT
383 @Override
384 public void controlResized(ControlEvent e) {
385 if (depth == 0) {
386 depth++;
387 tree.getVerticalBar().setEnabled(false);
388 // this can trigger controlResized recursively
389 tree.getVerticalBar().setVisible(false);
390 depth--;
391 }
392 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
393 }
394 });
395
396 // ensure synchronization of expanded items between tree and time graph
397 fTreeViewer.addTreeListener(new ITreeViewerListener() {
398 @Override
399 public void treeCollapsed(TreeExpansionEvent event) {
400 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);
f1fae91f 401 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
402 int topIndex = fTimeGraphViewer.getTopIndex();
403 if (topIndex >= treeItems.size()) {
837a2f8c
PT
404 return;
405 }
d106a486 406 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
407 tree.setTopItem(treeItem);
408 }
409
410 @Override
411 public void treeExpanded(TreeExpansionEvent event) {
e7708b02
PT
412 ITimeGraphEntry entry = (ITimeGraphEntry) event.getElement();
413 fTimeGraphViewer.setExpandedState(entry, true);
414 for (ITimeGraphEntry child : entry.getChildren()) {
415 boolean expanded = fTreeViewer.getExpandedState(child);
416 fTimeGraphViewer.setExpandedState(child, expanded);
417 }
f1fae91f 418 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
419 int topIndex = fTimeGraphViewer.getTopIndex();
420 if (topIndex >= treeItems.size()) {
837a2f8c
PT
421 return;
422 }
d106a486 423 final TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
424 // queue the top item update because the tree can change its top item
425 // autonomously immediately after the listeners have been notified
426 getDisplay().asyncExec(new Runnable() {
427 @Override
428 public void run() {
429 tree.setTopItem(treeItem);
430 }});
431 }
432 });
433
434 // ensure synchronization of expanded items between tree and time graph
435 fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {
436 @Override
437 public void treeCollapsed(TimeGraphTreeExpansionEvent event) {
438 fTreeViewer.setExpandedState(event.getEntry(), false);
439 }
440
441 @Override
442 public void treeExpanded(TimeGraphTreeExpansionEvent event) {
e7708b02
PT
443 ITimeGraphEntry entry = event.getEntry();
444 fTreeViewer.setExpandedState(entry, true);
445 for (ITimeGraphEntry child : entry.getChildren()) {
446 boolean expanded = fTreeViewer.getExpandedState(child);
447 fTimeGraphViewer.setExpandedState(child, expanded);
448 }
837a2f8c
PT
449 }
450 });
451
452 // prevent mouse button from selecting a filler tree item
453 tree.addListener(SWT.MouseDown, new Listener() {
454 @Override
455 public void handleEvent(Event event) {
456 TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
457 if (treeItem == null || treeItem.getData() == FILLER) {
458 event.doit = false;
f1fae91f 459 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
837a2f8c
PT
460 if (treeItems.size() == 0) {
461 fTreeViewer.setSelection(new StructuredSelection());
462 fTimeGraphViewer.setSelection(null);
463 return;
464 }
465 // this prevents from scrolling up when selecting
466 // the partially visible tree item at the bottom
467 tree.select(treeItems.get(treeItems.size() - 1));
468 fTreeViewer.setSelection(new StructuredSelection());
469 fTimeGraphViewer.setSelection(null);
470 }
471 }
472 });
473
474 // prevent mouse wheel from scrolling down into filler tree items
475 tree.addListener(SWT.MouseWheel, new Listener() {
476 @Override
477 public void handleEvent(Event event) {
478 event.doit = false;
479 Slider scrollBar = fTimeGraphViewer.getVerticalBar();
480 fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
f1fae91f 481 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
482 int topIndex = fTimeGraphViewer.getTopIndex();
483 if (topIndex >= treeItems.size()) {
837a2f8c
PT
484 return;
485 }
d106a486 486 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
487 tree.setTopItem(treeItem);
488 }
489 });
490
491 // prevent key stroke from selecting a filler tree item
492 tree.addListener(SWT.KeyDown, new Listener() {
493 @Override
494 public void handleEvent(Event event) {
f1fae91f 495 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
837a2f8c
PT
496 if (treeItems.size() == 0) {
497 fTreeViewer.setSelection(new StructuredSelection());
498 event.doit = false;
499 return;
500 }
501 if (event.keyCode == SWT.ARROW_DOWN) {
502 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
503 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
504 event.doit = false;
505 } else if (event.keyCode == SWT.PAGE_DOWN) {
506 int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
507 int countPerPage = height / getItemHeight(tree);
508 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
509 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
510 event.doit = false;
511 } else if (event.keyCode == SWT.END) {
512 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
513 event.doit = false;
514 }
515 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
516 tree.setTopItem(treeItem);
517 if (fTimeGraphViewer.getSelectionIndex() >= 0) {
518 fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
519 } else {
520 fTreeViewer.setSelection(new StructuredSelection());
521 }
522 }
523 });
524
525 // ensure alignment of top item between tree and time graph
526 fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {
527 @Override
528 public void controlResized(ControlEvent e) {
f1fae91f 529 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
530 int topIndex = fTimeGraphViewer.getTopIndex();
531 if (topIndex >= treeItems.size()) {
837a2f8c
PT
532 return;
533 }
d106a486 534 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
535 tree.setTopItem(treeItem);
536 }
537 });
538
539 // ensure synchronization of selected item between tree and time graph
540 fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
541 @Override
542 public void selectionChanged(SelectionChangedEvent event) {
543 if (fInhibitTreeSelection) {
544 return;
545 }
546 if (event.getSelection() instanceof IStructuredSelection) {
547 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
548 if (selection instanceof ITimeGraphEntry) {
549 fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
550 }
f1fae91f 551 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
552 int topIndex = fTimeGraphViewer.getTopIndex();
553 if (topIndex >= treeItems.size()) {
837a2f8c
PT
554 return;
555 }
d106a486 556 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
557 tree.setTopItem(treeItem);
558 }
559 }
560 });
561
562 // ensure synchronization of selected item between tree and time graph
563 fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {
564 @Override
565 public void selectionChanged(TimeGraphSelectionEvent event) {
566 ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
567 fInhibitTreeSelection = true; // block the tree selection changed listener
568 if (entry != null) {
569 StructuredSelection selection = new StructuredSelection(entry);
570 fTreeViewer.setSelection(selection);
571 } else {
572 fTreeViewer.setSelection(new StructuredSelection());
573 }
574 fInhibitTreeSelection = false;
f1fae91f 575 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
576 int topIndex = fTimeGraphViewer.getTopIndex();
577 if (topIndex >= treeItems.size()) {
837a2f8c
PT
578 return;
579 }
d106a486 580 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
581 tree.setTopItem(treeItem);
582 }
583 });
584
585 // ensure alignment of top item between tree and time graph
586 fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
587 @Override
588 public void widgetSelected(SelectionEvent e) {
f1fae91f 589 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
590 int topIndex = fTimeGraphViewer.getTopIndex();
591 if (topIndex >= treeItems.size()) {
837a2f8c
PT
592 return;
593 }
d106a486 594 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
595 tree.setTopItem(treeItem);
596 }
597 });
598
599 // ensure alignment of top item between tree and time graph
600 fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
601 @Override
602 public void mouseScrolled(MouseEvent e) {
f1fae91f 603 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
d106a486
PT
604 int topIndex = fTimeGraphViewer.getTopIndex();
605 if (topIndex >= treeItems.size()) {
837a2f8c
PT
606 return;
607 }
d106a486 608 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
609 tree.setTopItem(treeItem);
610 }
611 });
612
613 // ensure the tree has focus control when mouse is over it if the time graph had control
614 fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {
615 @Override
616 public void mouseEnter(MouseEvent e) {
617 if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {
618 fTreeViewer.getControl().setFocus();
619 }
620 }
621 });
622
623 // ensure the time graph has focus control when mouse is over it if the tree had control
624 fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
625 @Override
626 public void mouseEnter(MouseEvent e) {
627 if (fTreeViewer.getControl().isFocusControl()) {
628 fTimeGraphViewer.getTimeGraphControl().setFocus();
629 }
630 }
631 });
632 fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
633 @Override
634 public void mouseEnter(MouseEvent e) {
635 if (fTreeViewer.getControl().isFocusControl()) {
636 fTimeGraphViewer.getTimeGraphControl().setFocus();
637 }
638 }
639 });
640
c004295c
PT
641 // ensure the time graph item heights are equal to the tree item heights
642 tree.addPaintListener(new PaintListener() {
643 @Override
644 public void paintControl(PaintEvent e) {
645 List<TreeItem> items = getVisibleExpandedItems(tree);
646 for (int i = 0; i < items.size() - 1; i++) {
647 TreeItem item = items.get(i);
648 /*
649 * Bug in Linux. The method getBounds doesn't always return the correct height.
650 * Use the difference of y position between items to calculate the height.
651 */
652 Integer itemHeight = items.get(i + 1).getBounds().y - item.getBounds().y;
653 if (!itemHeight.equals(item.getData(ITEM_HEIGHT))) {
654 ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
655 if (fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight)) {
656 item.setData(ITEM_HEIGHT, itemHeight);
657 }
658 }
659 }
660 }
661 });
662
837a2f8c
PT
663 // The filler rows are required to ensure alignment when the tree does not have a
664 // visible horizontal scroll bar. The tree does not allow its top item to be set
665 // to a value that would cause blank space to be drawn at the bottom of the tree.
666 fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
667
4999a196 668 sash.setWeights(weights);
837a2f8c
PT
669 }
670
671 // ------------------------------------------------------------------------
672 // Accessors
673 // ------------------------------------------------------------------------
674
675 /**
676 * Returns this time graph combo's tree viewer.
677 *
678 * @return the tree viewer
679 */
680 public TreeViewer getTreeViewer() {
681 return fTreeViewer;
682 }
683
684 /**
685 * Returns this time graph combo's time graph viewer.
686 *
687 * @return the time graph viewer
688 */
689 public TimeGraphViewer getTimeGraphViewer() {
690 return fTimeGraphViewer;
691 }
692
6ac5a950
AM
693 /**
694 * Callback for the show filter action
695 *
696 * @since 2.0
697 */
698 public void showFilterDialog() {
699 if(fTopInput != null) {
8f28f9d8 700 List<? extends ITimeGraphEntry> allElements = listAllInputs(fTopInput);
6ac5a950
AM
701 fFilterDialog.setInput(fTopInput.toArray(new ITimeGraphEntry[0]));
702 fFilterDialog.setTitle(Messages.TmfTimeFilterDialog_WINDOW_TITLE);
703 fFilterDialog.setMessage(Messages.TmfTimeFilterDialog_MESSAGE);
8f28f9d8 704 fFilterDialog.setExpandedElements(allElements.toArray());
f8840316 705 if (fFilter.getFiltered() != null) {
507b1336 706 ArrayList<? extends ITimeGraphEntry> nonFilteredElements = new ArrayList<>(allElements);
f8840316
FR
707 nonFilteredElements.removeAll(fFilter.getFiltered());
708 fFilterDialog.setInitialElementSelections(nonFilteredElements);
8f28f9d8
PT
709 } else {
710 fFilterDialog.setInitialElementSelections(allElements);
711 }
6ac5a950
AM
712 fFilterDialog.create();
713 fFilterDialog.open();
714 // Process selected elements
715 if (fFilterDialog.getResult() != null) {
716 fInhibitTreeSelection = true;
8f28f9d8 717 if (fFilterDialog.getResult().length != allElements.size()) {
f8840316
FR
718 ArrayList<Object> filteredElements = new ArrayList<Object>(allElements);
719 filteredElements.removeAll(Arrays.asList(fFilterDialog.getResult()));
720 fFilter.setFiltered(filteredElements);
8f28f9d8 721 } else {
f8840316 722 fFilter.setFiltered(null);
8f28f9d8 723 }
6ac5a950
AM
724 fTreeViewer.refresh();
725 fTreeViewer.expandAll();
726 fTimeGraphViewer.refresh();
727 fInhibitTreeSelection = false;
728 // Reset selection to first entry
729 if (fFilterDialog.getResult().length > 0) {
730 setSelection((ITimeGraphEntry) fFilterDialog.getResult()[0]);
731 }
732 }
733 }
734 }
735
736 /**
737 * Get the show filter action.
738 *
739 * @return The Action object
740 * @since 2.0
741 */
742 public Action getShowFilterAction() {
743 if (showFilterAction == null) {
744 // showFilter
745 showFilterAction = new Action() {
746 @Override
747 public void run() {
748 showFilterDialog();
749 }
750 };
751 showFilterAction.setText(Messages.TmfTimeGraphCombo_FilterActionNameText);
752 showFilterAction.setToolTipText(Messages.TmfTimeGraphCombo_FilterActionToolTipText);
753 // TODO find a nice, distinctive icon
754 showFilterAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS));
755 }
756
757 return showFilterAction;
758 }
759
837a2f8c
PT
760 // ------------------------------------------------------------------------
761 // Control
762 // ------------------------------------------------------------------------
763
837a2f8c
PT
764 @Override
765 public void redraw() {
766 fTimeGraphViewer.getControl().redraw();
767 super.redraw();
768 }
769
770 // ------------------------------------------------------------------------
771 // Operations
772 // ------------------------------------------------------------------------
773
774 /**
775 * Sets the tree content provider used by this time graph combo.
776 *
777 * @param contentProvider the tree content provider
778 */
779 public void setTreeContentProvider(ITreeContentProvider contentProvider) {
780 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
781 }
782
783 /**
784 * Sets the tree label provider used by this time graph combo.
785 *
786 * @param labelProvider the tree label provider
787 */
788 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
789 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
790 }
791
6ac5a950
AM
792 /**
793 * Sets the tree content provider used by the filter dialog
794 *
795 * @param contentProvider the tree content provider
796 * @since 2.0
797 */
798 public void setFilterContentProvider(ITreeContentProvider contentProvider) {
799 fFilterDialog.setContentProvider(contentProvider);
800 }
801
802 /**
803 * Sets the tree label provider used by the filter dialog
804 *
805 * @param labelProvider the tree label provider
806 * @since 2.0
807 */
808 public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
809 fFilterDialog.setLabelProvider(labelProvider);
810 }
811
837a2f8c
PT
812 /**
813 * Sets the tree columns for this time graph combo.
814 *
815 * @param columnNames the tree column names
816 */
817 public void setTreeColumns(String[] columnNames) {
818 final Tree tree = fTreeViewer.getTree();
819 for (String columnName : columnNames) {
820 TreeColumn column = new TreeColumn(tree, SWT.LEFT);
821 column.setText(columnName);
822 column.pack();
823 }
824 }
825
6ac5a950
AM
826 /**
827 * Sets the tree columns for this time graph combo's filter dialog.
828 *
829 * @param columnNames the tree column names
830 * @since 2.0
831 */
832 public void setFilterColumns(String[] columnNames) {
833 fFilterDialog.setColumnNames(columnNames);
834 }
835
837a2f8c
PT
836 /**
837 * Sets the time graph provider used by this time graph combo.
838 *
839 * @param timeGraphProvider the time graph provider
840 */
841 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
842 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
843 }
844
845 /**
846 * Sets or clears the input for this time graph combo.
847 * The input array should only contain top-level elements.
848 *
849 * @param input the input of this time graph combo, or <code>null</code> if none
850 */
851 public void setInput(ITimeGraphEntry[] input) {
507b1336 852 fTopInput = new ArrayList<>(Arrays.asList(input));
f8840316 853 fFilter.setFiltered(null);
837a2f8c
PT
854 fInhibitTreeSelection = true;
855 fTreeViewer.setInput(input);
856 for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
857 listenerWrapper.selection = null;
858 }
859 fInhibitTreeSelection = false;
860 fTreeViewer.expandAll();
861 fTreeViewer.getTree().getVerticalBar().setEnabled(false);
862 fTreeViewer.getTree().getVerticalBar().setVisible(false);
863 fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
864 fTimeGraphViewer.setInput(input);
865 }
866
bec1f1ac
GB
867 /**
868 * Sets or clears the list of links to display on this combo
869 *
870 * @param links the links to display in this time graph combo
871 * @since 2.1
872 */
873 public void setLinks(List<ILinkEvent> links) {
874 fTimeGraphViewer.setLinks(links);
875 }
876
6ac5a950
AM
877 /**
878 * @param filter The filter object to be attached to the view
879 * @since 2.0
880 */
881 public void addFilter(ViewerFilter filter) {
882 ViewerFilter wrapper = new ViewerFilterWrapper(filter);
883 fTreeViewer.addFilter(wrapper);
884 fTimeGraphViewer.addFilter(wrapper);
885 fViewerFilterMap.put(filter, wrapper);
886 }
887
888 /**
889 * @param filter The filter object to be removed from the view
890 * @since 2.0
891 */
892 public void removeFilter(ViewerFilter filter) {
893 ViewerFilter wrapper = fViewerFilterMap.get(filter);
894 fTreeViewer.removeFilter(wrapper);
895 fTimeGraphViewer.removeFilter(wrapper);
896 fViewerFilterMap.remove(filter);
897 }
898
837a2f8c
PT
899 /**
900 * Refreshes this time graph completely with information freshly obtained from its model.
901 */
902 public void refresh() {
903 fInhibitTreeSelection = true;
904 fTreeViewer.refresh();
905 fTimeGraphViewer.refresh();
906 fInhibitTreeSelection = false;
907 }
908
909 /**
910 * Adds a listener for selection changes in this time graph combo.
911 *
912 * @param listener a selection listener
913 */
914 public void addSelectionListener(ITimeGraphSelectionListener listener) {
915 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
916 fTreeViewer.addSelectionChangedListener(listenerWrapper);
917 fSelectionListenerMap.put(listener, listenerWrapper);
918 fTimeGraphViewer.addSelectionListener(listenerWrapper);
919 }
920
921 /**
922 * Removes the given selection listener from this time graph combo.
923 *
924 * @param listener a selection changed listener
925 */
926 public void removeSelectionListener(ITimeGraphSelectionListener listener) {
927 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
928 fTreeViewer.removeSelectionChangedListener(listenerWrapper);
929 fTimeGraphViewer.removeSelectionListener(listenerWrapper);
930 }
931
932 /**
933 * Sets the current selection for this time graph combo.
934 *
935 * @param selection the new selection
936 */
937 public void setSelection(ITimeGraphEntry selection) {
938 fTimeGraphViewer.setSelection(selection);
939 fInhibitTreeSelection = true; // block the tree selection changed listener
940 if (selection != null) {
941 StructuredSelection structuredSelection = new StructuredSelection(selection);
942 fTreeViewer.setSelection(structuredSelection);
943 } else {
944 fTreeViewer.setSelection(new StructuredSelection());
945 }
946 fInhibitTreeSelection = false;
f1fae91f 947 List<TreeItem> treeItems = getVisibleExpandedItems(fTreeViewer.getTree());
d106a486
PT
948 int topIndex = fTimeGraphViewer.getTopIndex();
949 if (topIndex >= treeItems.size()) {
837a2f8c
PT
950 return;
951 }
d106a486 952 TreeItem treeItem = treeItems.get(topIndex);
837a2f8c
PT
953 fTreeViewer.getTree().setTopItem(treeItem);
954 }
955
956 /**
957 * Set the expanded state of an entry
958 *
959 * @param entry
960 * The entry to expand/collapse
961 * @param expanded
962 * True for expanded, false for collapsed
963 *
964 * @since 2.0
965 */
966 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
967 fTimeGraphViewer.setExpandedState(entry, expanded);
968 fTreeViewer.setExpandedState(entry, expanded);
969 }
970
971 /**
972 * Collapses all nodes of the viewer's tree, starting with the root.
973 *
974 * @since 2.0
975 */
976 public void collapseAll() {
977 fTimeGraphViewer.collapseAll();
978 fTreeViewer.collapseAll();
979 }
980
981 /**
982 * Expands all nodes of the viewer's tree, starting with the root.
983 *
984 * @since 2.0
985 */
986 public void expandAll() {
987 fTimeGraphViewer.expandAll();
988 fTreeViewer.expandAll();
989 }
990
991 // ------------------------------------------------------------------------
992 // Internal
993 // ------------------------------------------------------------------------
994
f1fae91f 995 private List<TreeItem> getVisibleExpandedItems(Tree tree) {
507b1336 996 ArrayList<TreeItem> items = new ArrayList<>();
837a2f8c
PT
997 for (TreeItem item : tree.getItems()) {
998 if (item.getData() == FILLER) {
999 break;
1000 }
1001 items.add(item);
1002 if (item.getExpanded()) {
1003 items.addAll(getVisibleExpandedItems(item));
1004 }
1005 }
1006 return items;
1007 }
1008
f1fae91f 1009 private List<TreeItem> getVisibleExpandedItems(TreeItem treeItem) {
507b1336 1010 ArrayList<TreeItem> items = new ArrayList<>();
837a2f8c
PT
1011 for (TreeItem item : treeItem.getItems()) {
1012 items.add(item);
1013 if (item.getExpanded()) {
1014 items.addAll(getVisibleExpandedItems(item));
1015 }
1016 }
1017 return items;
1018 }
1019
6ac5a950
AM
1020 /**
1021 * Explores the list of top-level inputs and returns all the inputs
1022 *
1023 * @param inputs The top-level inputs
1024 * @return All the inputs
1025 */
1026 private List<? extends ITimeGraphEntry> listAllInputs(List<? extends ITimeGraphEntry> inputs) {
507b1336 1027 ArrayList<ITimeGraphEntry> items = new ArrayList<>();
6ac5a950
AM
1028 for (ITimeGraphEntry entry : inputs) {
1029 items.add(entry);
1030 if (entry.hasChildren()) {
1031 items.addAll(listAllInputs(entry.getChildren()));
1032 }
1033 }
1034 return items;
1035 }
1036
837a2f8c
PT
1037 private int getItemHeight(final Tree tree) {
1038 /*
1039 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1040 */
1041 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1042 if (fLinuxItemHeight != 0) {
1043 return fLinuxItemHeight;
1044 }
f1fae91f 1045 List<TreeItem> treeItems = getVisibleExpandedItems(tree);
837a2f8c
PT
1046 if (treeItems.size() > 1) {
1047 final TreeItem treeItem0 = treeItems.get(0);
1048 final TreeItem treeItem1 = treeItems.get(1);
1049 PaintListener paintListener = new PaintListener() {
1050 @Override
1051 public void paintControl(PaintEvent e) {
1052 tree.removePaintListener(this);
1053 int y0 = treeItem0.getBounds().y;
1054 int y1 = treeItem1.getBounds().y;
1055 int itemHeight = y1 - y0;
1056 if (itemHeight > 0) {
1057 fLinuxItemHeight = itemHeight;
1058 fTimeGraphViewer.setItemHeight(itemHeight);
1059 }
1060 }
1061 };
1062 tree.addPaintListener(paintListener);
1063 }
1064 } else {
1065 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
1066 }
1067 return tree.getItemHeight();
1068 }
1069
1070}
This page took 0.094368 seconds and 5 git commands to generate.