1 /*******************************************************************************
2 * Copyright (c) 2012 Ericsson
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
10 * Patrick Tasse - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Arrays
;
17 import java
.util
.HashMap
;
19 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
20 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
21 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
22 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
23 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
24 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
25 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
26 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
27 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
28 import org
.eclipse
.jface
.viewers
.TreeViewer
;
29 import org
.eclipse
.jface
.viewers
.Viewer
;
30 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
31 import org
.eclipse
.swt
.SWT
;
32 import org
.eclipse
.swt
.custom
.SashForm
;
33 import org
.eclipse
.swt
.events
.ControlAdapter
;
34 import org
.eclipse
.swt
.events
.ControlEvent
;
35 import org
.eclipse
.swt
.events
.PaintEvent
;
36 import org
.eclipse
.swt
.events
.PaintListener
;
37 import org
.eclipse
.swt
.events
.SelectionAdapter
;
38 import org
.eclipse
.swt
.events
.SelectionEvent
;
39 import org
.eclipse
.swt
.graphics
.Image
;
40 import org
.eclipse
.swt
.graphics
.Point
;
41 import org
.eclipse
.swt
.layout
.FillLayout
;
42 import org
.eclipse
.swt
.widgets
.Composite
;
43 import org
.eclipse
.swt
.widgets
.Display
;
44 import org
.eclipse
.swt
.widgets
.Event
;
45 import org
.eclipse
.swt
.widgets
.Listener
;
46 import org
.eclipse
.swt
.widgets
.Slider
;
47 import org
.eclipse
.swt
.widgets
.Tree
;
48 import org
.eclipse
.swt
.widgets
.TreeColumn
;
49 import org
.eclipse
.swt
.widgets
.TreeItem
;
51 public class TimeGraphCombo
extends Composite
{
53 // ------------------------------------------------------------------------
55 // ------------------------------------------------------------------------
57 private static final Object FILLER
= new Object();
59 // ------------------------------------------------------------------------
61 // ------------------------------------------------------------------------
64 private TreeViewer fTreeViewer
;
67 private TimeGraphViewer fTimeGraphViewer
;
69 // The selection listener map
70 private HashMap
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<ITimeGraphSelectionListener
, SelectionListenerWrapper
>();
72 // Flag to block the tree selection changed listener when triggered by the time graph combo
73 private boolean fInhibitTreeSelection
= false;
75 // Number of filler rows used by the tree content provider
76 private static int fNumFillerRows
;
78 // Calculated item height for Linux workaround
79 private int fLinuxItemHeight
= 0;
81 // ------------------------------------------------------------------------
83 // ------------------------------------------------------------------------
85 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
86 private ITreeContentProvider contentProvider
;
88 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
89 this.contentProvider
= contentProvider
;
93 public void dispose() {
94 contentProvider
.dispose();
98 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
99 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
103 public Object
[] getElements(Object inputElement
) {
104 Object
[] elements
= contentProvider
.getElements(inputElement
);
105 // add filler elements to ensure alignment with time analysis viewer
106 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, new Object
[0].getClass());
107 for (int i
= 0; i
< fNumFillerRows
; i
++) {
108 oElements
[elements
.length
+ i
] = FILLER
;
114 public Object
[] getChildren(Object parentElement
) {
115 if (parentElement
instanceof ITimeGraphEntry
) {
116 return contentProvider
.getChildren(parentElement
);
118 return new Object
[0];
123 public Object
getParent(Object element
) {
124 if (element
instanceof ITimeGraphEntry
) {
125 return contentProvider
.getParent(element
);
132 public boolean hasChildren(Object element
) {
133 if (element
instanceof ITimeGraphEntry
) {
134 return contentProvider
.hasChildren(element
);
141 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
142 private ITableLabelProvider labelProvider
;
144 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
145 this.labelProvider
= labelProvider
;
149 public void addListener(ILabelProviderListener listener
) {
150 labelProvider
.addListener(listener
);
154 public void dispose() {
155 labelProvider
.dispose();
159 public boolean isLabelProperty(Object element
, String property
) {
160 if (element
instanceof ITimeGraphEntry
) {
161 return labelProvider
.isLabelProperty(element
, property
);
168 public void removeListener(ILabelProviderListener listener
) {
169 labelProvider
.removeListener(listener
);
173 public Image
getColumnImage(Object element
, int columnIndex
) {
174 if (element
instanceof ITimeGraphEntry
) {
175 return labelProvider
.getColumnImage(element
, columnIndex
);
182 public String
getColumnText(Object element
, int columnIndex
) {
183 if (element
instanceof ITimeGraphEntry
) {
184 return labelProvider
.getColumnText(element
, columnIndex
);
192 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
193 private ITimeGraphSelectionListener listener
;
194 private ITimeGraphEntry selection
= null;
196 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
197 this.listener
= listener
;
201 public void selectionChanged(SelectionChangedEvent event
) {
202 if (fInhibitTreeSelection
) {
205 ITimeGraphEntry entry
= (ITimeGraphEntry
) ((IStructuredSelection
) event
.getSelection()).getFirstElement();
206 if (entry
!= selection
) {
208 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
213 public void selectionChanged(TimeGraphSelectionEvent event
) {
214 ITimeGraphEntry entry
= event
.getSelection();
215 if (entry
!= selection
) {
217 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
222 // ------------------------------------------------------------------------
224 // ------------------------------------------------------------------------
226 public TimeGraphCombo(Composite parent
, int style
) {
227 super(parent
, style
);
228 setLayout(new FillLayout());
230 final SashForm sash
= new SashForm(this, SWT
.NONE
);
232 fTreeViewer
= new TreeViewer(sash
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
233 final Tree tree
= fTreeViewer
.getTree();
234 tree
.setHeaderVisible(true);
235 tree
.setLinesVisible(true);
237 fTimeGraphViewer
= new TimeGraphViewer(sash
, SWT
.NONE
);
238 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
239 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
240 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
241 fTimeGraphViewer
.setNameWidthPref(0);
243 // Bug in Linux. The tree header height is 0 in constructor,
244 // so we need to reset it later when the control is resized.
245 tree
.addControlListener(new ControlAdapter() {
247 public void controlResized(ControlEvent e
) {
248 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
252 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
254 public void treeCollapsed(TreeExpansionEvent event
) {
255 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
259 public void treeExpanded(TreeExpansionEvent event
) {
260 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), true);
264 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
266 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
267 fTreeViewer
.setExpandedState(event
.getEntry(), false);
271 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
272 fTreeViewer
.setExpandedState(event
.getEntry(), true);
276 // prevent mouse button from selecting a filler tree item
277 tree
.addListener(SWT
.MouseDown
, new Listener() {
279 public void handleEvent(Event event
) {
280 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
281 if (treeItem
== null || treeItem
.getData() == FILLER
) {
283 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
284 if (treeItems
.size() == 0) {
287 // this prevents from scrolling up when selecting
288 // the partially visible tree item at the bottom
289 tree
.select(treeItems
.get(treeItems
.size() - 1));
290 fTreeViewer
.setSelection(new StructuredSelection());
291 fTimeGraphViewer
.setSelection(null);
296 tree
.addListener(SWT
.MouseWheel
, new Listener() {
298 public void handleEvent(Event event
) {
300 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
301 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
302 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
303 if (treeItems
.size() == 0) {
306 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
307 tree
.setTopItem(treeItem
);
311 // prevent key stroke from selecting a filler tree item
312 tree
.addListener(SWT
.KeyDown
, new Listener() {
314 public void handleEvent(Event event
) {
315 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
316 if (treeItems
.size() == 0) {
319 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
320 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
321 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
323 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
324 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
325 int countPerPage
= height
/ getItemHeight(tree
);
326 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
327 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
329 } else if (event
.keyCode
== SWT
.END
) {
330 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
333 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
334 tree
.setTopItem(treeItem
);
335 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
336 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
338 fTreeViewer
.setSelection(new StructuredSelection());
343 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
345 public void controlResized(ControlEvent e
) {
346 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
347 if (treeItems
.size() == 0) {
350 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
351 tree
.setTopItem(treeItem
);
355 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
357 public void selectionChanged(SelectionChangedEvent event
) {
358 if (fInhibitTreeSelection
) {
361 if (event
.getSelection() instanceof IStructuredSelection
) {
362 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
363 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
364 if (selection
instanceof ITimeGraphEntry
) {
365 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
367 if (treeItems
.size() == 0) {
370 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
371 tree
.setTopItem(treeItem
);
376 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
378 public void selectionChanged(TimeGraphSelectionEvent event
) {
379 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
380 fInhibitTreeSelection
= true; // block the tree selection changed listener
382 StructuredSelection selection
= new StructuredSelection(entry
);
383 fTreeViewer
.setSelection(selection
);
385 fTreeViewer
.setSelection(new StructuredSelection());
387 fInhibitTreeSelection
= false;
388 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
389 if (treeItems
.size() == 0) {
392 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
393 tree
.setTopItem(treeItem
);
397 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
399 public void widgetSelected(SelectionEvent e
) {
400 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
401 if (treeItems
.size() == 0) {
404 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
405 tree
.setTopItem(treeItem
);
409 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
411 sash
.setWeights(new int[] { 1, 1 });
414 private ArrayList
<TreeItem
> getVisibleExpandedItems(Tree tree
) {
415 ArrayList
<TreeItem
> items
= new ArrayList
<TreeItem
>();
416 for (TreeItem item
: tree
.getItems()) {
417 if (item
.getData() == FILLER
) {
421 if (item
.getExpanded()) {
422 items
.addAll(getVisibleExpandedItems(item
));
428 private ArrayList
<TreeItem
> getVisibleExpandedItems(TreeItem treeItem
) {
429 ArrayList
<TreeItem
> items
= new ArrayList
<TreeItem
>();
430 for (TreeItem item
: treeItem
.getItems()) {
432 if (item
.getExpanded()) {
433 items
.addAll(getVisibleExpandedItems(item
));
439 // ------------------------------------------------------------------------
441 // ------------------------------------------------------------------------
444 * Returns this time graph combo's tree viewer.
446 * @return the tree viewer
448 public TreeViewer
getTreeViewer() {
453 * Returns this time graph combo's time graph viewer.
455 * @return the time graph viewer
457 public TimeGraphViewer
getTimeGraphViewer() {
458 return fTimeGraphViewer
;
461 // ------------------------------------------------------------------------
463 // ------------------------------------------------------------------------
465 public int getItemHeight(final Tree tree
) {
467 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
469 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
470 if (fLinuxItemHeight
!= 0) {
471 return fLinuxItemHeight
;
473 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
474 if (treeItems
.size() > 1) {
475 final TreeItem treeItem0
= treeItems
.get(0);
476 final TreeItem treeItem1
= treeItems
.get(1);
477 PaintListener paintListener
= new PaintListener() {
479 public void paintControl(PaintEvent e
) {
480 tree
.removePaintListener(this);
481 int y0
= treeItem0
.getBounds().y
;
482 int y1
= treeItem1
.getBounds().y
;
483 int itemHeight
= y1
- y0
;
484 if (itemHeight
> 0) {
485 fLinuxItemHeight
= itemHeight
;
486 fTimeGraphViewer
.setItemHeight(itemHeight
);
490 tree
.addPaintListener(paintListener
);
493 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
495 return tree
.getItemHeight();
498 // ------------------------------------------------------------------------
500 // ------------------------------------------------------------------------
503 * Sets the tree content provider used by this time graph combo.
505 * @param contentProvider the tree content provider
507 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
508 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
512 * Sets the tree label provider used by this time graph combo.
514 * @param treeLabelProvider the tree label provider
516 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
517 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
521 * Sets the tree columns for this time graph combo.
523 * @param columnNames the tree column names
525 public void setTreeColumns(String
[] columnNames
) {
526 final Tree tree
= fTreeViewer
.getTree();
527 for (String columnName
: columnNames
) {
528 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
529 column
.setText(columnName
);
536 * Sets the time graph provider used by this time graph combo.
538 * @param timeGraphProvider the time graph provider
540 public void setTimeGraphProvider(ITimeGraphProvider timeGraphProvider
) {
541 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
545 * Sets or clears the input for this time graph combo.
547 * @param input the input of this time graph combo, or <code>null</code> if none
549 public void setInput(ITimeGraphEntry
[] input
) {
550 fTreeViewer
.setInput(input
);
551 fTreeViewer
.expandAll();
552 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
553 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
554 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
555 fTimeGraphViewer
.setInput(input
);
559 * Refreshes this time graph completely with information freshly obtained from its model.
561 public void refresh() {
562 fTreeViewer
.refresh();
563 fTimeGraphViewer
.refresh();
567 * Adds a listener for selection changes in this time graph combo.
569 * @param listener a selection listener
571 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
572 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
573 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
574 fSelectionListenerMap
.put(listener
, listenerWrapper
);
575 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
579 * Removes the given selection listener from this time graph combo.
581 * @param listener a selection changed listener
583 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
584 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
585 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
586 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);