Bug 378401: Implementation of time graph widget.
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / timegraph / TimeGraphCombo.java
CommitLineData
fb5cad3d
PT
1/*******************************************************************************\r
2 * Copyright (c) 2012 Ericsson\r
3 * \r
4 * All rights reserved. This program and the accompanying materials are\r
5 * made available under the terms of the Eclipse Public License v1.0 which\r
6 * accompanies this distribution, and is available at\r
7 * http://www.eclipse.org/legal/epl-v10.html\r
8 * \r
9 * Contributors:\r
10 * Patrick Tasse - Initial API and implementation\r
11 *******************************************************************************/\r
12\r
13package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;\r
14\r
15import java.util.ArrayList;\r
16import java.util.Arrays;\r
17import java.util.HashMap;\r
18\r
19import org.eclipse.jface.viewers.ILabelProviderListener;\r
20import org.eclipse.jface.viewers.ISelectionChangedListener;\r
21import org.eclipse.jface.viewers.IStructuredSelection;\r
22import org.eclipse.jface.viewers.ITableLabelProvider;\r
23import org.eclipse.jface.viewers.ITreeContentProvider;\r
24import org.eclipse.jface.viewers.ITreeViewerListener;\r
25import org.eclipse.jface.viewers.SelectionChangedEvent;\r
26import org.eclipse.jface.viewers.StructuredSelection;\r
27import org.eclipse.jface.viewers.TreeExpansionEvent;\r
28import org.eclipse.jface.viewers.TreeViewer;\r
29import org.eclipse.jface.viewers.Viewer;\r
30import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;\r
31import org.eclipse.swt.SWT;\r
32import org.eclipse.swt.custom.SashForm;\r
33import org.eclipse.swt.events.ControlAdapter;\r
34import org.eclipse.swt.events.ControlEvent;\r
35import org.eclipse.swt.events.SelectionAdapter;\r
36import org.eclipse.swt.events.SelectionEvent;\r
37import org.eclipse.swt.graphics.Image;\r
38import org.eclipse.swt.graphics.Point;\r
39import org.eclipse.swt.layout.FillLayout;\r
40import org.eclipse.swt.widgets.Composite;\r
41import org.eclipse.swt.widgets.Display;\r
42import org.eclipse.swt.widgets.Event;\r
43import org.eclipse.swt.widgets.Listener;\r
44import org.eclipse.swt.widgets.Slider;\r
45import org.eclipse.swt.widgets.Tree;\r
46import org.eclipse.swt.widgets.TreeColumn;\r
47import org.eclipse.swt.widgets.TreeItem;\r
48\r
49public class TimeGraphCombo extends Composite {\r
50\r
51 // ------------------------------------------------------------------------\r
52 // Constants\r
53 // ------------------------------------------------------------------------\r
54\r
55 private static final Object FILLER = new Object();\r
56\r
57 // ------------------------------------------------------------------------\r
58 // Fields\r
59 // ------------------------------------------------------------------------\r
60\r
61 // The tree viewer\r
62 private TreeViewer fTreeViewer;\r
63\r
64 // The time viewer\r
65 private TimeGraphViewer fTimeGraphViewer;\r
66\r
67 // The selection listener map\r
68 private HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>();\r
69\r
70 // Flag to block the tree selection changed listener when triggered by the time graph combo\r
71 private boolean fInhibitTreeSelection = false;\r
72\r
73 // Number of filler rows used by the tree content provider\r
74 private static int fNumFillerRows;\r
75\r
76 // ------------------------------------------------------------------------\r
77 // Classes\r
78 // ------------------------------------------------------------------------\r
79\r
80 private class TreeContentProviderWrapper implements ITreeContentProvider {\r
81 private ITreeContentProvider contentProvider;\r
82\r
83 public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {\r
84 this.contentProvider = contentProvider;\r
85 }\r
86\r
87 @Override\r
88 public void dispose() {\r
89 contentProvider.dispose();\r
90 }\r
91\r
92 @Override\r
93 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
94 contentProvider.inputChanged(viewer, oldInput, newInput);\r
95 }\r
96\r
97 @Override\r
98 public Object[] getElements(Object inputElement) {\r
99 Object[] elements = contentProvider.getElements(inputElement);\r
100 // add filler elements to ensure alignment with time analysis viewer\r
101 Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass());\r
102 for (int i = 0; i < fNumFillerRows; i++) {\r
103 oElements[elements.length + i] = FILLER;\r
104 }\r
105 return oElements;\r
106 }\r
107\r
108 @Override\r
109 public Object[] getChildren(Object parentElement) {\r
110 if (parentElement instanceof ITimeGraphEntry) {\r
111 return contentProvider.getChildren(parentElement);\r
112 } else {\r
113 return new Object[0];\r
114 }\r
115 }\r
116\r
117 @Override\r
118 public Object getParent(Object element) {\r
119 if (element instanceof ITimeGraphEntry) {\r
120 return contentProvider.getParent(element);\r
121 } else {\r
122 return null;\r
123 }\r
124 }\r
125\r
126 @Override\r
127 public boolean hasChildren(Object element) {\r
128 if (element instanceof ITimeGraphEntry) {\r
129 return contentProvider.hasChildren(element);\r
130 } else {\r
131 return false;\r
132 }\r
133 }\r
134 }\r
135\r
136 private class TreeLabelProviderWrapper implements ITableLabelProvider {\r
137 private ITableLabelProvider labelProvider;\r
138\r
139 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {\r
140 this.labelProvider = labelProvider;\r
141 }\r
142\r
143 @Override\r
144 public void addListener(ILabelProviderListener listener) {\r
145 labelProvider.addListener(listener);\r
146 }\r
147\r
148 @Override\r
149 public void dispose() {\r
150 labelProvider.dispose();\r
151 }\r
152\r
153 @Override\r
154 public boolean isLabelProperty(Object element, String property) {\r
155 if (element instanceof ITimeGraphEntry) {\r
156 return labelProvider.isLabelProperty(element, property);\r
157 } else {\r
158 return false;\r
159 }\r
160 }\r
161\r
162 @Override\r
163 public void removeListener(ILabelProviderListener listener) {\r
164 labelProvider.removeListener(listener);\r
165 }\r
166\r
167 @Override\r
168 public Image getColumnImage(Object element, int columnIndex) {\r
169 if (element instanceof ITimeGraphEntry) {\r
170 return labelProvider.getColumnImage(element, columnIndex);\r
171 } else {\r
172 return null;\r
173 }\r
174 }\r
175\r
176 @Override\r
177 public String getColumnText(Object element, int columnIndex) {\r
178 if (element instanceof ITimeGraphEntry) {\r
179 return labelProvider.getColumnText(element, columnIndex);\r
180 } else {\r
181 return null;\r
182 }\r
183 }\r
184\r
185 }\r
186\r
187 private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {\r
188 private ITimeGraphSelectionListener listener;\r
189 private ITimeGraphEntry selection = null;\r
190\r
191 public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {\r
192 this.listener = listener;\r
193 }\r
194\r
195 @Override\r
196 public void selectionChanged(SelectionChangedEvent event) {\r
197 if (fInhibitTreeSelection) {\r
198 return;\r
199 }\r
200 ITimeGraphEntry entry = (ITimeGraphEntry) ((IStructuredSelection) event.getSelection()).getFirstElement();\r
201 if (entry != selection) {\r
202 selection = entry;\r
203 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r
204 }\r
205 }\r
206\r
207 @Override\r
208 public void selectionChanged(TimeGraphSelectionEvent event) {\r
209 ITimeGraphEntry entry = event.getSelection();\r
210 if (entry != selection) {\r
211 selection = entry;\r
212 listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r
213 }\r
214 }\r
215 }\r
216\r
217 // ------------------------------------------------------------------------\r
218 // Constructors\r
219 // ------------------------------------------------------------------------\r
220\r
221 public TimeGraphCombo(Composite parent, int style) {\r
222 super(parent, style);\r
223 setLayout(new FillLayout());\r
224\r
225 final SashForm sash = new SashForm(this, SWT.NONE);\r
226\r
227 fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);\r
228 final Tree tree = fTreeViewer.getTree();\r
229 tree.setHeaderVisible(true);\r
230 tree.setLinesVisible(true);\r
231\r
232 fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);\r
233 fTimeGraphViewer.setItemHeight(tree.getItemHeight() + getTreeItemHeightAdjustement());\r
234 fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());\r
235 fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());\r
236 fTimeGraphViewer.setNameWidthPref(0);\r
237\r
238 fTreeViewer.addTreeListener(new ITreeViewerListener() {\r
239 @Override\r
240 public void treeCollapsed(TreeExpansionEvent event) {\r
241 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);\r
242 }\r
243\r
244 @Override\r
245 public void treeExpanded(TreeExpansionEvent event) {\r
246 fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true);\r
247 }\r
248 });\r
249\r
250 fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {\r
251 @Override\r
252 public void treeCollapsed(TimeGraphTreeExpansionEvent event) {\r
253 fTreeViewer.setExpandedState(event.getEntry(), false);\r
254 }\r
255\r
256 @Override\r
257 public void treeExpanded(TimeGraphTreeExpansionEvent event) {\r
258 fTreeViewer.setExpandedState(event.getEntry(), true);\r
259 }\r
260 });\r
261\r
262 // prevent mouse button from selecting a filler tree item\r
263 tree.addListener(SWT.MouseDown, new Listener() {\r
264 @Override\r
265 public void handleEvent(Event event) {\r
266 TreeItem treeItem = tree.getItem(new Point(event.x, event.y));\r
267 if (treeItem == null || treeItem.getData() == FILLER) {\r
268 event.doit = false;\r
269 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
270 if (treeItems.size() == 0) {\r
271 return;\r
272 }\r
273 // this prevents from scrolling up when selecting\r
274 // the partially visible tree item at the bottom\r
275 tree.select(treeItems.get(treeItems.size() - 1));\r
276 fTreeViewer.setSelection(new StructuredSelection());\r
277 fTimeGraphViewer.setSelection(null);\r
278 }\r
279 }\r
280 });\r
281\r
282 tree.addListener(SWT.MouseWheel, new Listener() {\r
283 @Override\r
284 public void handleEvent(Event event) {\r
285 event.doit = false;\r
286 Slider scrollBar = fTimeGraphViewer.getVerticalBar();\r
287 fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);\r
288 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
289 if (treeItems.size() == 0) {\r
290 return;\r
291 }\r
292 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
293 tree.setTopItem(treeItem);\r
294 }\r
295 });\r
296\r
297 // prevent key stroke from selecting a filler tree item\r
298 tree.addListener(SWT.KeyDown, new Listener() {\r
299 @Override\r
300 public void handleEvent(Event event) {\r
301 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
302 if (treeItems.size() == 0) {\r
303 return;\r
304 }\r
305 if (event.keyCode == SWT.ARROW_DOWN) {\r
306 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);\r
307 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r
308 event.doit = false;\r
309 } else if (event.keyCode == SWT.PAGE_DOWN) {\r
310 int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;\r
311 int countPerPage = height / (tree.getItemHeight() + getTreeItemHeightAdjustement());\r
312 int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);\r
313 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r
314 event.doit = false;\r
315 } else if (event.keyCode == SWT.END) {\r
316 fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());\r
317 event.doit = false;\r
318 }\r
319 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
320 tree.setTopItem(treeItem);\r
321 if (fTimeGraphViewer.getSelectionIndex() >= 0) {\r
322 fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));\r
323 } else {\r
324 fTreeViewer.setSelection(new StructuredSelection());\r
325 }\r
326 }\r
327 });\r
328\r
329 fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {\r
330 @Override\r
331 public void controlResized(ControlEvent e) {\r
332 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
333 if (treeItems.size() == 0) {\r
334 return;\r
335 }\r
336 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
337 tree.setTopItem(treeItem);\r
338 }\r
339 });\r
340\r
341 fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {\r
342 @Override\r
343 public void selectionChanged(SelectionChangedEvent event) {\r
344 if (fInhibitTreeSelection) {\r
345 return;\r
346 }\r
347 if (event.getSelection() instanceof IStructuredSelection) {\r
348 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();\r
349 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
350 if (selection instanceof ITimeGraphEntry) {\r
351 fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);\r
352 }\r
353 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
354 tree.setTopItem(treeItem);\r
355 }\r
356 }\r
357 });\r
358\r
359 fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {\r
360 @Override\r
361 public void selectionChanged(TimeGraphSelectionEvent event) {\r
362 ITimeGraphEntry entry = fTimeGraphViewer.getSelection();\r
363 fInhibitTreeSelection = true; // block the tree selection changed listener\r
364 if (entry != null) {\r
365 StructuredSelection selection = new StructuredSelection(entry);\r
366 fTreeViewer.setSelection(selection);\r
367 } else {\r
368 fTreeViewer.setSelection(new StructuredSelection());\r
369 }\r
370 fInhibitTreeSelection = false;\r
371 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
372 if (treeItems.size() == 0) {\r
373 return;\r
374 }\r
375 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
376 tree.setTopItem(treeItem);\r
377 }\r
378 });\r
379\r
380 fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {\r
381 @Override\r
382 public void widgetSelected(SelectionEvent e) {\r
383 ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
384 if (treeItems.size() == 0) {\r
385 return;\r
386 }\r
387 TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
388 tree.setTopItem(treeItem);\r
389 }\r
390 });\r
391\r
392 fNumFillerRows = Display.getDefault().getBounds().height / (tree.getItemHeight() + getTreeItemHeightAdjustement());\r
393\r
394 sash.setWeights(new int[] { 1, 1 });\r
395 }\r
396\r
397 private ArrayList<TreeItem> getVisibleExpandedItems(Tree tree) {\r
398 ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r
399 for (TreeItem item : tree.getItems()) {\r
400 if (item.getData() == FILLER) {\r
401 break;\r
402 }\r
403 items.add(item);\r
404 if (item.getExpanded()) {\r
405 items.addAll(getVisibleExpandedItems(item));\r
406 }\r
407 }\r
408 return items;\r
409 }\r
410\r
411 private ArrayList<TreeItem> getVisibleExpandedItems(TreeItem treeItem) {\r
412 ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r
413 for (TreeItem item : treeItem.getItems()) {\r
414 items.add(item);\r
415 if (item.getExpanded()) {\r
416 items.addAll(getVisibleExpandedItems(item));\r
417 }\r
418 }\r
419 return items;\r
420 }\r
421\r
422 // ------------------------------------------------------------------------\r
423 // Accessors\r
424 // ------------------------------------------------------------------------\r
425\r
426 /**\r
427 * Returns this time graph combo's tree viewer.\r
428 * \r
429 * @return the tree viewer\r
430 */\r
431 public TreeViewer getTreeViewer() {\r
432 return fTreeViewer;\r
433 }\r
434\r
435 /**\r
436 * Returns this time graph combo's time graph viewer.\r
437 * \r
438 * @return the time graph viewer\r
439 */\r
440 public TimeGraphViewer getTimeGraphViewer() {\r
441 return fTimeGraphViewer;\r
442 }\r
443\r
444 // ------------------------------------------------------------------------\r
445 // Internal\r
446 // ------------------------------------------------------------------------\r
447\r
448 /*\r
449 * SWT doesn't seem to report correctly the tree item height, at least in\r
450 * the case of KDE.\r
451 * \r
452 * This method provides an adjustment term according to the desktop session.\r
453 * \r
454 * @return Height adjustment \r
455 */\r
456 private int getTreeItemHeightAdjustement() {\r
457 int ajustement = 0;\r
458 String desktopSession = System.getenv("DESKTOP_SESSION"); //$NON-NLS-1$\r
459\r
460 if (desktopSession != null) {\r
461 if (desktopSession.equals("kde")) { //$NON-NLS-1$\r
462 ajustement = 2;\r
463 }\r
464 }\r
465\r
466 return ajustement;\r
467 }\r
468\r
469 // ------------------------------------------------------------------------\r
470 // Operations\r
471 // ------------------------------------------------------------------------\r
472\r
473 /**\r
474 * Sets the tree content provider used by this time graph combo.\r
475 * \r
476 * @param contentProvider the tree content provider\r
477 */\r
478 public void setTreeContentProvider(ITreeContentProvider contentProvider) {\r
479 fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));\r
480 }\r
481\r
482 /**\r
483 * Sets the tree label provider used by this time graph combo.\r
484 * \r
485 * @param treeLabelProvider the tree label provider\r
486 */\r
487 public void setTreeLabelProvider(ITableLabelProvider labelProvider) {\r
488 fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));\r
489 }\r
490\r
491 /**\r
492 * Sets the tree columns for this time graph combo.\r
493 * \r
494 * @param columnNames the tree column names\r
495 */\r
496 public void setTreeColumns(String[] columnNames) {\r
497 final Tree tree = fTreeViewer.getTree();\r
498 for (String columnName : columnNames) {\r
499 TreeColumn column = new TreeColumn(tree, SWT.LEFT);\r
500 column.setText(columnName);\r
501 column.pack();\r
502 }\r
503 }\r
504\r
505\r
506 /**\r
507 * Sets the time graph provider used by this time graph combo.\r
508 * \r
509 * @param timeGraphProvider the time graph provider\r
510 */\r
511 public void setTimeGraphProvider(ITimeGraphProvider timeGraphProvider) {\r
512 fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);\r
513 }\r
514\r
515 /**\r
516 * Sets or clears the input for this time graph combo.\r
517 *\r
518 * @param input the input of this time graph combo, or <code>null</code> if none\r
519 */\r
520 public void setInput(ITimeGraphEntry[] input) {\r
521 fTreeViewer.setInput(input);\r
522 fTreeViewer.expandAll();\r
523 fTreeViewer.getTree().getVerticalBar().setEnabled(false);\r
524 fTimeGraphViewer.setInput(input);\r
525 }\r
526\r
527 /**\r
528 * Refreshes this time graph completely with information freshly obtained from its model.\r
529 */\r
530 public void refresh() {\r
531 fTreeViewer.refresh();\r
532 fTimeGraphViewer.refresh();\r
533 }\r
534\r
535 /**\r
536 * Adds a listener for selection changes in this time graph combo.\r
537 *\r
538 * @param listener a selection listener\r
539 */\r
540 public void addSelectionListener(ITimeGraphSelectionListener listener) {\r
541 SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);\r
542 fTreeViewer.addSelectionChangedListener(listenerWrapper);\r
543 fSelectionListenerMap.put(listener, listenerWrapper);\r
544 fTimeGraphViewer.addSelectionListener(listenerWrapper);\r
545 }\r
546\r
547 /**\r
548 * Removes the given selection listener from this time graph combo.\r
549 *\r
550 * @param listener a selection changed listener\r
551 */\r
552 public void removeSelectionListener(ITimeGraphSelectionListener listener) {\r
553 SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);\r
554 fTreeViewer.removeSelectionChangedListener(listenerWrapper);\r
555 fTimeGraphViewer.removeSelectionListener(listenerWrapper);\r
556 }\r
557}\r
This page took 0.045559 seconds and 5 git commands to generate.