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
.SelectionAdapter
;
36 import org
.eclipse
.swt
.events
.SelectionEvent
;
37 import org
.eclipse
.swt
.graphics
.Image
;
38 import org
.eclipse
.swt
.graphics
.Point
;
39 import org
.eclipse
.swt
.layout
.FillLayout
;
40 import org
.eclipse
.swt
.widgets
.Composite
;
41 import org
.eclipse
.swt
.widgets
.Display
;
42 import org
.eclipse
.swt
.widgets
.Event
;
43 import org
.eclipse
.swt
.widgets
.Listener
;
44 import org
.eclipse
.swt
.widgets
.Slider
;
45 import org
.eclipse
.swt
.widgets
.Tree
;
46 import org
.eclipse
.swt
.widgets
.TreeColumn
;
47 import org
.eclipse
.swt
.widgets
.TreeItem
;
49 public class TimeGraphCombo
extends Composite
{
51 // ------------------------------------------------------------------------
53 // ------------------------------------------------------------------------
55 private static final Object FILLER
= new Object();
57 // ------------------------------------------------------------------------
59 // ------------------------------------------------------------------------
62 private TreeViewer fTreeViewer
;
65 private TimeGraphViewer fTimeGraphViewer
;
67 // The selection listener map
68 private HashMap
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<ITimeGraphSelectionListener
, SelectionListenerWrapper
>();
70 // Flag to block the tree selection changed listener when triggered by the time graph combo
71 private boolean fInhibitTreeSelection
= false;
73 // Number of filler rows used by the tree content provider
74 private static int fNumFillerRows
;
76 // ------------------------------------------------------------------------
78 // ------------------------------------------------------------------------
80 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
81 private ITreeContentProvider contentProvider
;
83 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
84 this.contentProvider
= contentProvider
;
88 public void dispose() {
89 contentProvider
.dispose();
93 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
94 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
98 public Object
[] getElements(Object inputElement
) {
99 Object
[] elements
= contentProvider
.getElements(inputElement
);
100 // add filler elements to ensure alignment with time analysis viewer
101 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, new Object
[0].getClass());
102 for (int i
= 0; i
< fNumFillerRows
; i
++) {
103 oElements
[elements
.length
+ i
] = FILLER
;
109 public Object
[] getChildren(Object parentElement
) {
110 if (parentElement
instanceof ITimeGraphEntry
) {
111 return contentProvider
.getChildren(parentElement
);
113 return new Object
[0];
118 public Object
getParent(Object element
) {
119 if (element
instanceof ITimeGraphEntry
) {
120 return contentProvider
.getParent(element
);
127 public boolean hasChildren(Object element
) {
128 if (element
instanceof ITimeGraphEntry
) {
129 return contentProvider
.hasChildren(element
);
136 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
137 private ITableLabelProvider labelProvider
;
139 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
140 this.labelProvider
= labelProvider
;
144 public void addListener(ILabelProviderListener listener
) {
145 labelProvider
.addListener(listener
);
149 public void dispose() {
150 labelProvider
.dispose();
154 public boolean isLabelProperty(Object element
, String property
) {
155 if (element
instanceof ITimeGraphEntry
) {
156 return labelProvider
.isLabelProperty(element
, property
);
163 public void removeListener(ILabelProviderListener listener
) {
164 labelProvider
.removeListener(listener
);
168 public Image
getColumnImage(Object element
, int columnIndex
) {
169 if (element
instanceof ITimeGraphEntry
) {
170 return labelProvider
.getColumnImage(element
, columnIndex
);
177 public String
getColumnText(Object element
, int columnIndex
) {
178 if (element
instanceof ITimeGraphEntry
) {
179 return labelProvider
.getColumnText(element
, columnIndex
);
187 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
188 private ITimeGraphSelectionListener listener
;
189 private ITimeGraphEntry selection
= null;
191 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
192 this.listener
= listener
;
196 public void selectionChanged(SelectionChangedEvent event
) {
197 if (fInhibitTreeSelection
) {
200 ITimeGraphEntry entry
= (ITimeGraphEntry
) ((IStructuredSelection
) event
.getSelection()).getFirstElement();
201 if (entry
!= selection
) {
203 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
208 public void selectionChanged(TimeGraphSelectionEvent event
) {
209 ITimeGraphEntry entry
= event
.getSelection();
210 if (entry
!= selection
) {
212 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
217 // ------------------------------------------------------------------------
219 // ------------------------------------------------------------------------
221 public TimeGraphCombo(Composite parent
, int style
) {
222 super(parent
, style
);
223 setLayout(new FillLayout());
225 final SashForm sash
= new SashForm(this, SWT
.NONE
);
227 fTreeViewer
= new TreeViewer(sash
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
228 final Tree tree
= fTreeViewer
.getTree();
229 tree
.setHeaderVisible(true);
230 tree
.setLinesVisible(true);
232 fTimeGraphViewer
= new TimeGraphViewer(sash
, SWT
.NONE
);
233 fTimeGraphViewer
.setItemHeight(tree
.getItemHeight() + getTreeItemHeightAdjustement());
234 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
235 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
236 fTimeGraphViewer
.setNameWidthPref(0);
238 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
240 public void treeCollapsed(TreeExpansionEvent event
) {
241 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
245 public void treeExpanded(TreeExpansionEvent event
) {
246 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), true);
250 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
252 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
253 fTreeViewer
.setExpandedState(event
.getEntry(), false);
257 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
258 fTreeViewer
.setExpandedState(event
.getEntry(), true);
262 // prevent mouse button from selecting a filler tree item
263 tree
.addListener(SWT
.MouseDown
, new Listener() {
265 public void handleEvent(Event event
) {
266 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
267 if (treeItem
== null || treeItem
.getData() == FILLER
) {
269 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
270 if (treeItems
.size() == 0) {
273 // this prevents from scrolling up when selecting
274 // the partially visible tree item at the bottom
275 tree
.select(treeItems
.get(treeItems
.size() - 1));
276 fTreeViewer
.setSelection(new StructuredSelection());
277 fTimeGraphViewer
.setSelection(null);
282 tree
.addListener(SWT
.MouseWheel
, new Listener() {
284 public void handleEvent(Event event
) {
286 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
287 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
288 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
289 if (treeItems
.size() == 0) {
292 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
293 tree
.setTopItem(treeItem
);
297 // prevent key stroke from selecting a filler tree item
298 tree
.addListener(SWT
.KeyDown
, new Listener() {
300 public void handleEvent(Event event
) {
301 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
302 if (treeItems
.size() == 0) {
305 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
306 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
307 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
309 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
310 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
311 int countPerPage
= height
/ (tree
.getItemHeight() + getTreeItemHeightAdjustement());
312 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
313 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
315 } else if (event
.keyCode
== SWT
.END
) {
316 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
319 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
320 tree
.setTopItem(treeItem
);
321 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
322 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
324 fTreeViewer
.setSelection(new StructuredSelection());
329 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
331 public void controlResized(ControlEvent e
) {
332 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
333 if (treeItems
.size() == 0) {
336 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
337 tree
.setTopItem(treeItem
);
341 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
343 public void selectionChanged(SelectionChangedEvent event
) {
344 if (fInhibitTreeSelection
) {
347 if (event
.getSelection() instanceof IStructuredSelection
) {
348 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
349 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
350 if (selection
instanceof ITimeGraphEntry
) {
351 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
353 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
354 tree
.setTopItem(treeItem
);
359 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
361 public void selectionChanged(TimeGraphSelectionEvent event
) {
362 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
363 fInhibitTreeSelection
= true; // block the tree selection changed listener
365 StructuredSelection selection
= new StructuredSelection(entry
);
366 fTreeViewer
.setSelection(selection
);
368 fTreeViewer
.setSelection(new StructuredSelection());
370 fInhibitTreeSelection
= false;
371 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
372 if (treeItems
.size() == 0) {
375 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
376 tree
.setTopItem(treeItem
);
380 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
382 public void widgetSelected(SelectionEvent e
) {
383 ArrayList
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
384 if (treeItems
.size() == 0) {
387 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
388 tree
.setTopItem(treeItem
);
392 fNumFillerRows
= Display
.getDefault().getBounds().height
/ (tree
.getItemHeight() + getTreeItemHeightAdjustement());
394 sash
.setWeights(new int[] { 1, 1 });
397 private ArrayList
<TreeItem
> getVisibleExpandedItems(Tree tree
) {
398 ArrayList
<TreeItem
> items
= new ArrayList
<TreeItem
>();
399 for (TreeItem item
: tree
.getItems()) {
400 if (item
.getData() == FILLER
) {
404 if (item
.getExpanded()) {
405 items
.addAll(getVisibleExpandedItems(item
));
411 private ArrayList
<TreeItem
> getVisibleExpandedItems(TreeItem treeItem
) {
412 ArrayList
<TreeItem
> items
= new ArrayList
<TreeItem
>();
413 for (TreeItem item
: treeItem
.getItems()) {
415 if (item
.getExpanded()) {
416 items
.addAll(getVisibleExpandedItems(item
));
422 // ------------------------------------------------------------------------
424 // ------------------------------------------------------------------------
427 * Returns this time graph combo's tree viewer.
429 * @return the tree viewer
431 public TreeViewer
getTreeViewer() {
436 * Returns this time graph combo's time graph viewer.
438 * @return the time graph viewer
440 public TimeGraphViewer
getTimeGraphViewer() {
441 return fTimeGraphViewer
;
444 // ------------------------------------------------------------------------
446 // ------------------------------------------------------------------------
449 * SWT doesn't seem to report correctly the tree item height, at least in
452 * This method provides an adjustment term according to the desktop session.
454 * @return Height adjustment
456 private int getTreeItemHeightAdjustement() {
458 String desktopSession
= System
.getenv("DESKTOP_SESSION"); //$NON-NLS-1$
460 if (desktopSession
!= null) {
461 if (desktopSession
.equals("kde")) { //$NON-NLS-1$
469 // ------------------------------------------------------------------------
471 // ------------------------------------------------------------------------
474 * Sets the tree content provider used by this time graph combo.
476 * @param contentProvider the tree content provider
478 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
479 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
483 * Sets the tree label provider used by this time graph combo.
485 * @param treeLabelProvider the tree label provider
487 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
488 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
492 * Sets the tree columns for this time graph combo.
494 * @param columnNames the tree column names
496 public void setTreeColumns(String
[] columnNames
) {
497 final Tree tree
= fTreeViewer
.getTree();
498 for (String columnName
: columnNames
) {
499 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
500 column
.setText(columnName
);
507 * Sets the time graph provider used by this time graph combo.
509 * @param timeGraphProvider the time graph provider
511 public void setTimeGraphProvider(ITimeGraphProvider timeGraphProvider
) {
512 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
516 * Sets or clears the input for this time graph combo.
518 * @param input the input of this time graph combo, or <code>null</code> if none
520 public void setInput(ITimeGraphEntry
[] input
) {
521 fTreeViewer
.setInput(input
);
522 fTreeViewer
.expandAll();
523 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
524 fTimeGraphViewer
.setInput(input
);
528 * Refreshes this time graph completely with information freshly obtained from its model.
530 public void refresh() {
531 fTreeViewer
.refresh();
532 fTimeGraphViewer
.refresh();
536 * Adds a listener for selection changes in this time graph combo.
538 * @param listener a selection listener
540 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
541 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
542 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
543 fSelectionListenerMap
.put(listener
, listenerWrapper
);
544 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
548 * Removes the given selection listener from this time graph combo.
550 * @param listener a selection changed listener
552 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
553 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
554 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
555 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);