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