Commit | Line | Data |
---|---|---|
fb5cad3d PT |
1 | /*******************************************************************************\r |
2 | * Copyright (c) 2012 Ericsson\r | |
013a5f1c | 3 | *\r |
fb5cad3d PT |
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 | |
013a5f1c | 8 | *\r |
fb5cad3d PT |
9 | * Contributors:\r |
10 | * Patrick Tasse - Initial API and implementation\r | |
11 | *******************************************************************************/\r | |
12 | \r | |
13 | package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;\r | |
14 | \r | |
15 | import java.util.ArrayList;\r | |
16 | import java.util.Arrays;\r | |
17 | import java.util.HashMap;\r | |
18 | \r | |
19 | import org.eclipse.jface.viewers.ILabelProviderListener;\r | |
20 | import org.eclipse.jface.viewers.ISelectionChangedListener;\r | |
21 | import org.eclipse.jface.viewers.IStructuredSelection;\r | |
22 | import org.eclipse.jface.viewers.ITableLabelProvider;\r | |
23 | import org.eclipse.jface.viewers.ITreeContentProvider;\r | |
24 | import org.eclipse.jface.viewers.ITreeViewerListener;\r | |
25 | import org.eclipse.jface.viewers.SelectionChangedEvent;\r | |
26 | import org.eclipse.jface.viewers.StructuredSelection;\r | |
27 | import org.eclipse.jface.viewers.TreeExpansionEvent;\r | |
28 | import org.eclipse.jface.viewers.TreeViewer;\r | |
29 | import org.eclipse.jface.viewers.Viewer;\r | |
30 | import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;\r | |
31 | import org.eclipse.swt.SWT;\r | |
32 | import org.eclipse.swt.custom.SashForm;\r | |
33 | import org.eclipse.swt.events.ControlAdapter;\r | |
34 | import org.eclipse.swt.events.ControlEvent;\r | |
b83af2c3 | 35 | import org.eclipse.swt.events.MouseEvent;\r |
e9f70626 | 36 | import org.eclipse.swt.events.MouseTrackAdapter;\r |
b83af2c3 | 37 | import org.eclipse.swt.events.MouseWheelListener;\r |
519ea816 PT |
38 | import org.eclipse.swt.events.PaintEvent;\r |
39 | import org.eclipse.swt.events.PaintListener;\r | |
fb5cad3d PT |
40 | import org.eclipse.swt.events.SelectionAdapter;\r |
41 | import org.eclipse.swt.events.SelectionEvent;\r | |
42 | import org.eclipse.swt.graphics.Image;\r | |
43 | import org.eclipse.swt.graphics.Point;\r | |
44 | import org.eclipse.swt.layout.FillLayout;\r | |
45 | import org.eclipse.swt.widgets.Composite;\r | |
46 | import org.eclipse.swt.widgets.Display;\r | |
47 | import org.eclipse.swt.widgets.Event;\r | |
48 | import org.eclipse.swt.widgets.Listener;\r | |
49 | import org.eclipse.swt.widgets.Slider;\r | |
50 | import org.eclipse.swt.widgets.Tree;\r | |
51 | import org.eclipse.swt.widgets.TreeColumn;\r | |
52 | import org.eclipse.swt.widgets.TreeItem;\r | |
53 | \r | |
013a5f1c AM |
54 | /**\r |
55 | * Time graph "combo" view (with the list/tree on the left and the gantt chart\r | |
56 | * on the right)\r | |
57 | *\r | |
58 | * @version 1.0\r | |
59 | * @author Patrick Tasse\r | |
60 | */\r | |
fb5cad3d PT |
61 | public class TimeGraphCombo extends Composite {\r |
62 | \r | |
63 | // ------------------------------------------------------------------------\r | |
64 | // Constants\r | |
65 | // ------------------------------------------------------------------------\r | |
66 | \r | |
67 | private static final Object FILLER = new Object();\r | |
68 | \r | |
69 | // ------------------------------------------------------------------------\r | |
70 | // Fields\r | |
71 | // ------------------------------------------------------------------------\r | |
72 | \r | |
73 | // The tree viewer\r | |
74 | private TreeViewer fTreeViewer;\r | |
75 | \r | |
76 | // The time viewer\r | |
77 | private TimeGraphViewer fTimeGraphViewer;\r | |
78 | \r | |
79 | // The selection listener map\r | |
013a5f1c | 80 | private final HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>();\r |
fb5cad3d PT |
81 | \r |
82 | // Flag to block the tree selection changed listener when triggered by the time graph combo\r | |
83 | private boolean fInhibitTreeSelection = false;\r | |
84 | \r | |
85 | // Number of filler rows used by the tree content provider\r | |
549f3c43 | 86 | private int fNumFillerRows;\r |
fb5cad3d | 87 | \r |
519ea816 PT |
88 | // Calculated item height for Linux workaround\r |
89 | private int fLinuxItemHeight = 0;\r | |
90 | \r | |
fb5cad3d PT |
91 | // ------------------------------------------------------------------------\r |
92 | // Classes\r | |
93 | // ------------------------------------------------------------------------\r | |
94 | \r | |
549f3c43 PT |
95 | /**\r |
96 | * The TreeContentProviderWrapper is used to insert filler items after\r | |
97 | * the elements of the tree's real content provider.\r | |
98 | */\r | |
fb5cad3d | 99 | private class TreeContentProviderWrapper implements ITreeContentProvider {\r |
013a5f1c | 100 | private final ITreeContentProvider contentProvider;\r |
fb5cad3d PT |
101 | \r |
102 | public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {\r | |
103 | this.contentProvider = contentProvider;\r | |
104 | }\r | |
105 | \r | |
106 | @Override\r | |
107 | public void dispose() {\r | |
108 | contentProvider.dispose();\r | |
109 | }\r | |
110 | \r | |
111 | @Override\r | |
112 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r | |
113 | contentProvider.inputChanged(viewer, oldInput, newInput);\r | |
114 | }\r | |
115 | \r | |
116 | @Override\r | |
117 | public Object[] getElements(Object inputElement) {\r | |
118 | Object[] elements = contentProvider.getElements(inputElement);\r | |
119 | // add filler elements to ensure alignment with time analysis viewer\r | |
120 | Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass());\r | |
121 | for (int i = 0; i < fNumFillerRows; i++) {\r | |
122 | oElements[elements.length + i] = FILLER;\r | |
123 | }\r | |
124 | return oElements;\r | |
125 | }\r | |
126 | \r | |
127 | @Override\r | |
128 | public Object[] getChildren(Object parentElement) {\r | |
129 | if (parentElement instanceof ITimeGraphEntry) {\r | |
130 | return contentProvider.getChildren(parentElement);\r | |
131 | } else {\r | |
132 | return new Object[0];\r | |
133 | }\r | |
134 | }\r | |
135 | \r | |
136 | @Override\r | |
137 | public Object getParent(Object element) {\r | |
138 | if (element instanceof ITimeGraphEntry) {\r | |
139 | return contentProvider.getParent(element);\r | |
140 | } else {\r | |
141 | return null;\r | |
142 | }\r | |
143 | }\r | |
144 | \r | |
145 | @Override\r | |
146 | public boolean hasChildren(Object element) {\r | |
147 | if (element instanceof ITimeGraphEntry) {\r | |
148 | return contentProvider.hasChildren(element);\r | |
149 | } else {\r | |
150 | return false;\r | |
151 | }\r | |
152 | }\r | |
153 | }\r | |
154 | \r | |
549f3c43 PT |
155 | /**\r |
156 | * The TreeLabelProviderWrapper is used to intercept the filler items\r | |
157 | * from the calls to the tree's real label provider.\r | |
158 | */\r | |
fb5cad3d | 159 | private class TreeLabelProviderWrapper implements ITableLabelProvider {\r |
013a5f1c | 160 | private final ITableLabelProvider labelProvider;\r |
fb5cad3d PT |
161 | \r |
162 | public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {\r | |
163 | this.labelProvider = labelProvider;\r | |
164 | }\r | |
165 | \r | |
166 | @Override\r | |
167 | public void addListener(ILabelProviderListener listener) {\r | |
168 | labelProvider.addListener(listener);\r | |
169 | }\r | |
170 | \r | |
171 | @Override\r | |
172 | public void dispose() {\r | |
173 | labelProvider.dispose();\r | |
174 | }\r | |
175 | \r | |
176 | @Override\r | |
177 | public boolean isLabelProperty(Object element, String property) {\r | |
178 | if (element instanceof ITimeGraphEntry) {\r | |
179 | return labelProvider.isLabelProperty(element, property);\r | |
180 | } else {\r | |
181 | return false;\r | |
182 | }\r | |
183 | }\r | |
184 | \r | |
185 | @Override\r | |
186 | public void removeListener(ILabelProviderListener listener) {\r | |
187 | labelProvider.removeListener(listener);\r | |
188 | }\r | |
189 | \r | |
190 | @Override\r | |
191 | public Image getColumnImage(Object element, int columnIndex) {\r | |
192 | if (element instanceof ITimeGraphEntry) {\r | |
193 | return labelProvider.getColumnImage(element, columnIndex);\r | |
194 | } else {\r | |
195 | return null;\r | |
196 | }\r | |
197 | }\r | |
198 | \r | |
199 | @Override\r | |
200 | public String getColumnText(Object element, int columnIndex) {\r | |
201 | if (element instanceof ITimeGraphEntry) {\r | |
202 | return labelProvider.getColumnText(element, columnIndex);\r | |
203 | } else {\r | |
204 | return null;\r | |
205 | }\r | |
206 | }\r | |
207 | \r | |
208 | }\r | |
209 | \r | |
549f3c43 PT |
210 | /**\r |
211 | * The SelectionListenerWrapper is used to intercept the filler items from\r | |
212 | * the time graph combo's real selection listener, and to prevent double\r | |
213 | * notifications from being sent when selection changes in both tree and\r | |
214 | * time graph at the same time.\r | |
215 | */\r | |
fb5cad3d | 216 | private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {\r |
013a5f1c | 217 | private final ITimeGraphSelectionListener listener;\r |
fb5cad3d PT |
218 | private ITimeGraphEntry selection = null;\r |
219 | \r | |
220 | public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {\r | |
221 | this.listener = listener;\r | |
222 | }\r | |
223 | \r | |
224 | @Override\r | |
225 | public void selectionChanged(SelectionChangedEvent event) {\r | |
226 | if (fInhibitTreeSelection) {\r | |
227 | return;\r | |
228 | }\r | |
b83af2c3 PT |
229 | Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();\r |
230 | if (element instanceof ITimeGraphEntry) {\r | |
231 | ITimeGraphEntry entry = (ITimeGraphEntry) element;\r | |
232 | if (entry != selection) {\r | |
233 | selection = entry;\r | |
234 | listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r | |
235 | }\r | |
fb5cad3d PT |
236 | }\r |
237 | }\r | |
238 | \r | |
239 | @Override\r | |
240 | public void selectionChanged(TimeGraphSelectionEvent event) {\r | |
241 | ITimeGraphEntry entry = event.getSelection();\r | |
242 | if (entry != selection) {\r | |
243 | selection = entry;\r | |
244 | listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r | |
245 | }\r | |
246 | }\r | |
247 | }\r | |
248 | \r | |
249 | // ------------------------------------------------------------------------\r | |
250 | // Constructors\r | |
251 | // ------------------------------------------------------------------------\r | |
252 | \r | |
549f3c43 PT |
253 | /**\r |
254 | * Constructs a new instance of this class given its parent\r | |
255 | * and a style value describing its behavior and appearance.\r | |
013a5f1c | 256 | *\r |
549f3c43 PT |
257 | * @param parent a widget which will be the parent of the new instance (cannot be null)\r |
258 | * @param style the style of widget to construct\r | |
259 | */\r | |
fb5cad3d PT |
260 | public TimeGraphCombo(Composite parent, int style) {\r |
261 | super(parent, style);\r | |
262 | setLayout(new FillLayout());\r | |
263 | \r | |
264 | final SashForm sash = new SashForm(this, SWT.NONE);\r | |
265 | \r | |
266 | fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);\r | |
267 | final Tree tree = fTreeViewer.getTree();\r | |
268 | tree.setHeaderVisible(true);\r | |
269 | tree.setLinesVisible(true);\r | |
270 | \r | |
271 | fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);\r | |
519ea816 | 272 | fTimeGraphViewer.setItemHeight(getItemHeight(tree));\r |
fb5cad3d PT |
273 | fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());\r |
274 | fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());\r | |
275 | fTimeGraphViewer.setNameWidthPref(0);\r | |
276 | \r | |
549f3c43 | 277 | // Feature in Windows. The tree vertical bar reappears when\r |
013a5f1c | 278 | // the control is resized so we need to hide it again.\r |
d269c2fa PT |
279 | // Bug in Linux. The tree header height is 0 in constructor,\r |
280 | // so we need to reset it later when the control is resized.\r | |
281 | tree.addControlListener(new ControlAdapter() {\r | |
f379b3eb | 282 | int depth = 0;\r |
d269c2fa PT |
283 | @Override\r |
284 | public void controlResized(ControlEvent e) {\r | |
f379b3eb PT |
285 | if (depth == 0) {\r |
286 | depth++;\r | |
287 | tree.getVerticalBar().setEnabled(false);\r | |
288 | // this can trigger controlResized recursively\r | |
289 | tree.getVerticalBar().setVisible(false);\r | |
290 | depth--;\r | |
291 | }\r | |
d269c2fa PT |
292 | fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());\r |
293 | }\r | |
294 | });\r | |
295 | \r | |
549f3c43 | 296 | // ensure synchronization of expanded items between tree and time graph\r |
fb5cad3d PT |
297 | fTreeViewer.addTreeListener(new ITreeViewerListener() {\r |
298 | @Override\r | |
299 | public void treeCollapsed(TreeExpansionEvent event) {\r | |
300 | fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);\r | |
301 | }\r | |
302 | \r | |
303 | @Override\r | |
304 | public void treeExpanded(TreeExpansionEvent event) {\r | |
305 | fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true);\r | |
306 | }\r | |
307 | });\r | |
308 | \r | |
549f3c43 | 309 | // ensure synchronization of expanded items between tree and time graph\r |
fb5cad3d PT |
310 | fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {\r |
311 | @Override\r | |
312 | public void treeCollapsed(TimeGraphTreeExpansionEvent event) {\r | |
313 | fTreeViewer.setExpandedState(event.getEntry(), false);\r | |
314 | }\r | |
315 | \r | |
316 | @Override\r | |
317 | public void treeExpanded(TimeGraphTreeExpansionEvent event) {\r | |
318 | fTreeViewer.setExpandedState(event.getEntry(), true);\r | |
319 | }\r | |
320 | });\r | |
321 | \r | |
322 | // prevent mouse button from selecting a filler tree item\r | |
323 | tree.addListener(SWT.MouseDown, new Listener() {\r | |
324 | @Override\r | |
325 | public void handleEvent(Event event) {\r | |
326 | TreeItem treeItem = tree.getItem(new Point(event.x, event.y));\r | |
327 | if (treeItem == null || treeItem.getData() == FILLER) {\r | |
328 | event.doit = false;\r | |
329 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
330 | if (treeItems.size() == 0) {\r | |
b83af2c3 PT |
331 | fTreeViewer.setSelection(new StructuredSelection());\r |
332 | fTimeGraphViewer.setSelection(null);\r | |
fb5cad3d PT |
333 | return;\r |
334 | }\r | |
335 | // this prevents from scrolling up when selecting\r | |
336 | // the partially visible tree item at the bottom\r | |
337 | tree.select(treeItems.get(treeItems.size() - 1));\r | |
338 | fTreeViewer.setSelection(new StructuredSelection());\r | |
339 | fTimeGraphViewer.setSelection(null);\r | |
340 | }\r | |
341 | }\r | |
342 | });\r | |
343 | \r | |
549f3c43 | 344 | // prevent mouse wheel from scrolling down into filler tree items\r |
fb5cad3d PT |
345 | tree.addListener(SWT.MouseWheel, new Listener() {\r |
346 | @Override\r | |
347 | public void handleEvent(Event event) {\r | |
348 | event.doit = false;\r | |
349 | Slider scrollBar = fTimeGraphViewer.getVerticalBar();\r | |
350 | fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);\r | |
351 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
352 | if (treeItems.size() == 0) {\r | |
353 | return;\r | |
354 | }\r | |
355 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
356 | tree.setTopItem(treeItem);\r | |
357 | }\r | |
358 | });\r | |
359 | \r | |
360 | // prevent key stroke from selecting a filler tree item\r | |
361 | tree.addListener(SWT.KeyDown, new Listener() {\r | |
362 | @Override\r | |
363 | public void handleEvent(Event event) {\r | |
364 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
365 | if (treeItems.size() == 0) {\r | |
b83af2c3 PT |
366 | fTreeViewer.setSelection(new StructuredSelection());\r |
367 | event.doit = false;\r | |
fb5cad3d PT |
368 | return;\r |
369 | }\r | |
370 | if (event.keyCode == SWT.ARROW_DOWN) {\r | |
371 | int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);\r | |
372 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r | |
373 | event.doit = false;\r | |
374 | } else if (event.keyCode == SWT.PAGE_DOWN) {\r | |
375 | int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;\r | |
519ea816 | 376 | int countPerPage = height / getItemHeight(tree);\r |
fb5cad3d PT |
377 | int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);\r |
378 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r | |
379 | event.doit = false;\r | |
380 | } else if (event.keyCode == SWT.END) {\r | |
381 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());\r | |
382 | event.doit = false;\r | |
383 | }\r | |
384 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
385 | tree.setTopItem(treeItem);\r | |
386 | if (fTimeGraphViewer.getSelectionIndex() >= 0) {\r | |
387 | fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));\r | |
388 | } else {\r | |
389 | fTreeViewer.setSelection(new StructuredSelection());\r | |
390 | }\r | |
391 | }\r | |
392 | });\r | |
393 | \r | |
549f3c43 | 394 | // ensure alignment of top item between tree and time graph\r |
fb5cad3d PT |
395 | fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {\r |
396 | @Override\r | |
397 | public void controlResized(ControlEvent e) {\r | |
398 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
399 | if (treeItems.size() == 0) {\r | |
400 | return;\r | |
401 | }\r | |
402 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
403 | tree.setTopItem(treeItem);\r | |
404 | }\r | |
405 | });\r | |
406 | \r | |
549f3c43 | 407 | // ensure synchronization of selected item between tree and time graph\r |
fb5cad3d PT |
408 | fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {\r |
409 | @Override\r | |
410 | public void selectionChanged(SelectionChangedEvent event) {\r | |
411 | if (fInhibitTreeSelection) {\r | |
412 | return;\r | |
413 | }\r | |
414 | if (event.getSelection() instanceof IStructuredSelection) {\r | |
415 | Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();\r | |
fb5cad3d PT |
416 | if (selection instanceof ITimeGraphEntry) {\r |
417 | fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);\r | |
418 | }\r | |
b83af2c3 | 419 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r |
c45586b7 AM |
420 | if (treeItems.size() == 0) {\r |
421 | return;\r | |
422 | }\r | |
fb5cad3d PT |
423 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r |
424 | tree.setTopItem(treeItem);\r | |
425 | }\r | |
426 | }\r | |
427 | });\r | |
428 | \r | |
549f3c43 | 429 | // ensure synchronization of selected item between tree and time graph\r |
fb5cad3d PT |
430 | fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {\r |
431 | @Override\r | |
432 | public void selectionChanged(TimeGraphSelectionEvent event) {\r | |
433 | ITimeGraphEntry entry = fTimeGraphViewer.getSelection();\r | |
434 | fInhibitTreeSelection = true; // block the tree selection changed listener\r | |
435 | if (entry != null) {\r | |
436 | StructuredSelection selection = new StructuredSelection(entry);\r | |
437 | fTreeViewer.setSelection(selection);\r | |
438 | } else {\r | |
439 | fTreeViewer.setSelection(new StructuredSelection());\r | |
440 | }\r | |
441 | fInhibitTreeSelection = false;\r | |
442 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
443 | if (treeItems.size() == 0) {\r | |
444 | return;\r | |
445 | }\r | |
446 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
447 | tree.setTopItem(treeItem);\r | |
448 | }\r | |
449 | });\r | |
450 | \r | |
549f3c43 | 451 | // ensure alignment of top item between tree and time graph\r |
fb5cad3d PT |
452 | fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {\r |
453 | @Override\r | |
454 | public void widgetSelected(SelectionEvent e) {\r | |
455 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
456 | if (treeItems.size() == 0) {\r | |
457 | return;\r | |
458 | }\r | |
459 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
460 | tree.setTopItem(treeItem);\r | |
461 | }\r | |
462 | });\r | |
463 | \r | |
549f3c43 | 464 | // ensure alignment of top item between tree and time graph\r |
b83af2c3 PT |
465 | fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {\r |
466 | @Override\r | |
467 | public void mouseScrolled(MouseEvent e) {\r | |
468 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
469 | if (treeItems.size() == 0) {\r | |
470 | return;\r | |
471 | }\r | |
472 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
473 | tree.setTopItem(treeItem);\r | |
474 | }\r | |
475 | });\r | |
476 | \r | |
e9f70626 PT |
477 | // ensure the tree has focus control when mouse is over it if the time graph had control\r |
478 | fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {\r | |
479 | @Override\r | |
480 | public void mouseEnter(MouseEvent e) {\r | |
481 | if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {\r | |
482 | fTreeViewer.getControl().setFocus();\r | |
483 | }\r | |
484 | }\r | |
485 | });\r | |
486 | \r | |
487 | // ensure the time graph has focus control when mouse is over it if the tree had control\r | |
488 | fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {\r | |
489 | @Override\r | |
490 | public void mouseEnter(MouseEvent e) {\r | |
491 | if (fTreeViewer.getControl().isFocusControl()) {\r | |
492 | fTimeGraphViewer.getTimeGraphControl().setFocus();\r | |
493 | }\r | |
494 | }\r | |
495 | });\r | |
496 | fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {\r | |
497 | @Override\r | |
498 | public void mouseEnter(MouseEvent e) {\r | |
499 | if (fTreeViewer.getControl().isFocusControl()) {\r | |
500 | fTimeGraphViewer.getTimeGraphControl().setFocus();\r | |
501 | }\r | |
502 | }\r | |
503 | });\r | |
504 | \r | |
549f3c43 PT |
505 | // The filler rows are required to ensure alignment when the tree does not have a\r |
506 | // visible horizontal scroll bar. The tree does not allow its top item to be set\r | |
507 | // to a value that would cause blank space to be drawn at the bottom of the tree.\r | |
519ea816 | 508 | fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);\r |
fb5cad3d PT |
509 | \r |
510 | sash.setWeights(new int[] { 1, 1 });\r | |
511 | }\r | |
512 | \r | |
fb5cad3d PT |
513 | // ------------------------------------------------------------------------\r |
514 | // Accessors\r | |
515 | // ------------------------------------------------------------------------\r | |
516 | \r | |
517 | /**\r | |
518 | * Returns this time graph combo's tree viewer.\r | |
013a5f1c | 519 | *\r |
fb5cad3d PT |
520 | * @return the tree viewer\r |
521 | */\r | |
522 | public TreeViewer getTreeViewer() {\r | |
523 | return fTreeViewer;\r | |
524 | }\r | |
525 | \r | |
526 | /**\r | |
527 | * Returns this time graph combo's time graph viewer.\r | |
013a5f1c | 528 | *\r |
fb5cad3d PT |
529 | * @return the time graph viewer\r |
530 | */\r | |
531 | public TimeGraphViewer getTimeGraphViewer() {\r | |
532 | return fTimeGraphViewer;\r | |
533 | }\r | |
534 | \r | |
b83af2c3 PT |
535 | // ------------------------------------------------------------------------\r |
536 | // Control\r | |
537 | // ------------------------------------------------------------------------\r | |
538 | \r | |
539 | /* (non-Javadoc)\r | |
540 | * @see org.eclipse.swt.widgets.Control#redraw()\r | |
541 | */\r | |
542 | @Override\r | |
543 | public void redraw() {\r | |
544 | fTimeGraphViewer.getControl().redraw();\r | |
545 | super.redraw();\r | |
546 | }\r | |
547 | \r | |
fb5cad3d PT |
548 | // ------------------------------------------------------------------------\r |
549 | // Operations\r | |
550 | // ------------------------------------------------------------------------\r | |
551 | \r | |
552 | /**\r | |
553 | * Sets the tree content provider used by this time graph combo.\r | |
013a5f1c | 554 | *\r |
fb5cad3d PT |
555 | * @param contentProvider the tree content provider\r |
556 | */\r | |
557 | public void setTreeContentProvider(ITreeContentProvider contentProvider) {\r | |
558 | fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));\r | |
559 | }\r | |
560 | \r | |
561 | /**\r | |
562 | * Sets the tree label provider used by this time graph combo.\r | |
013a5f1c | 563 | *\r |
fb5cad3d PT |
564 | * @param treeLabelProvider the tree label provider\r |
565 | */\r | |
566 | public void setTreeLabelProvider(ITableLabelProvider labelProvider) {\r | |
567 | fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));\r | |
568 | }\r | |
569 | \r | |
570 | /**\r | |
571 | * Sets the tree columns for this time graph combo.\r | |
013a5f1c | 572 | *\r |
fb5cad3d PT |
573 | * @param columnNames the tree column names\r |
574 | */\r | |
575 | public void setTreeColumns(String[] columnNames) {\r | |
576 | final Tree tree = fTreeViewer.getTree();\r | |
577 | for (String columnName : columnNames) {\r | |
578 | TreeColumn column = new TreeColumn(tree, SWT.LEFT);\r | |
579 | column.setText(columnName);\r | |
580 | column.pack();\r | |
581 | }\r | |
582 | }\r | |
583 | \r | |
584 | \r | |
585 | /**\r | |
586 | * Sets the time graph provider used by this time graph combo.\r | |
013a5f1c | 587 | *\r |
fb5cad3d PT |
588 | * @param timeGraphProvider the time graph provider\r |
589 | */\r | |
b83af2c3 | 590 | public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {\r |
fb5cad3d PT |
591 | fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);\r |
592 | }\r | |
593 | \r | |
594 | /**\r | |
595 | * Sets or clears the input for this time graph combo.\r | |
b83af2c3 | 596 | * The input array should only contain top-level elements.\r |
fb5cad3d PT |
597 | *\r |
598 | * @param input the input of this time graph combo, or <code>null</code> if none\r | |
599 | */\r | |
600 | public void setInput(ITimeGraphEntry[] input) {\r | |
549f3c43 | 601 | fInhibitTreeSelection = true;\r |
fb5cad3d | 602 | fTreeViewer.setInput(input);\r |
e9f70626 | 603 | fInhibitTreeSelection = false;\r |
fb5cad3d PT |
604 | fTreeViewer.expandAll();\r |
605 | fTreeViewer.getTree().getVerticalBar().setEnabled(false);\r | |
d269c2fa | 606 | fTreeViewer.getTree().getVerticalBar().setVisible(false);\r |
519ea816 | 607 | fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));\r |
fb5cad3d PT |
608 | fTimeGraphViewer.setInput(input);\r |
609 | }\r | |
610 | \r | |
611 | /**\r | |
612 | * Refreshes this time graph completely with information freshly obtained from its model.\r | |
613 | */\r | |
614 | public void refresh() {\r | |
615 | fTreeViewer.refresh();\r | |
616 | fTimeGraphViewer.refresh();\r | |
617 | }\r | |
618 | \r | |
619 | /**\r | |
620 | * Adds a listener for selection changes in this time graph combo.\r | |
621 | *\r | |
622 | * @param listener a selection listener\r | |
623 | */\r | |
624 | public void addSelectionListener(ITimeGraphSelectionListener listener) {\r | |
625 | SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);\r | |
626 | fTreeViewer.addSelectionChangedListener(listenerWrapper);\r | |
627 | fSelectionListenerMap.put(listener, listenerWrapper);\r | |
628 | fTimeGraphViewer.addSelectionListener(listenerWrapper);\r | |
629 | }\r | |
630 | \r | |
631 | /**\r | |
632 | * Removes the given selection listener from this time graph combo.\r | |
633 | *\r | |
634 | * @param listener a selection changed listener\r | |
635 | */\r | |
636 | public void removeSelectionListener(ITimeGraphSelectionListener listener) {\r | |
637 | SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);\r | |
638 | fTreeViewer.removeSelectionChangedListener(listenerWrapper);\r | |
639 | fTimeGraphViewer.removeSelectionListener(listenerWrapper);\r | |
640 | }\r | |
549f3c43 PT |
641 | \r |
642 | /**\r | |
643 | * Sets the current selection for this time graph combo.\r | |
644 | *\r | |
645 | * @param selection the new selection\r | |
646 | */\r | |
647 | public void setSelection(ITimeGraphEntry selection) {\r | |
648 | fTimeGraphViewer.setSelection(selection);\r | |
649 | fInhibitTreeSelection = true; // block the tree selection changed listener\r | |
650 | if (selection != null) {\r | |
651 | StructuredSelection structuredSelection = new StructuredSelection(selection);\r | |
652 | fTreeViewer.setSelection(structuredSelection);\r | |
653 | } else {\r | |
654 | fTreeViewer.setSelection(new StructuredSelection());\r | |
655 | }\r | |
656 | fInhibitTreeSelection = false;\r | |
657 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(fTreeViewer.getTree());\r | |
658 | if (treeItems.size() == 0) {\r | |
659 | return;\r | |
660 | }\r | |
661 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r | |
662 | fTreeViewer.getTree().setTopItem(treeItem);\r | |
663 | }\r | |
664 | \r | |
665 | // ------------------------------------------------------------------------\r | |
666 | // Internal\r | |
667 | // ------------------------------------------------------------------------\r | |
668 | \r | |
669 | private ArrayList<TreeItem> getVisibleExpandedItems(Tree tree) {\r | |
670 | ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r | |
671 | for (TreeItem item : tree.getItems()) {\r | |
672 | if (item.getData() == FILLER) {\r | |
673 | break;\r | |
674 | }\r | |
675 | items.add(item);\r | |
676 | if (item.getExpanded()) {\r | |
677 | items.addAll(getVisibleExpandedItems(item));\r | |
678 | }\r | |
679 | }\r | |
680 | return items;\r | |
681 | }\r | |
682 | \r | |
683 | private ArrayList<TreeItem> getVisibleExpandedItems(TreeItem treeItem) {\r | |
684 | ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r | |
685 | for (TreeItem item : treeItem.getItems()) {\r | |
686 | items.add(item);\r | |
687 | if (item.getExpanded()) {\r | |
688 | items.addAll(getVisibleExpandedItems(item));\r | |
689 | }\r | |
690 | }\r | |
691 | return items;\r | |
692 | }\r | |
693 | \r | |
694 | private int getItemHeight(final Tree tree) {\r | |
695 | /*\r | |
696 | * Bug in Linux. The method getItemHeight doesn't always return the correct value.\r | |
697 | */\r | |
698 | if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$\r | |
699 | if (fLinuxItemHeight != 0) {\r | |
700 | return fLinuxItemHeight;\r | |
701 | }\r | |
702 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r | |
703 | if (treeItems.size() > 1) {\r | |
704 | final TreeItem treeItem0 = treeItems.get(0);\r | |
705 | final TreeItem treeItem1 = treeItems.get(1);\r | |
706 | PaintListener paintListener = new PaintListener() {\r | |
707 | @Override\r | |
708 | public void paintControl(PaintEvent e) {\r | |
709 | tree.removePaintListener(this);\r | |
710 | int y0 = treeItem0.getBounds().y;\r | |
711 | int y1 = treeItem1.getBounds().y;\r | |
712 | int itemHeight = y1 - y0;\r | |
713 | if (itemHeight > 0) {\r | |
714 | fLinuxItemHeight = itemHeight;\r | |
715 | fTimeGraphViewer.setItemHeight(itemHeight);\r | |
716 | }\r | |
717 | }\r | |
718 | };\r | |
719 | tree.addPaintListener(paintListener);\r | |
720 | }\r | |
721 | } else {\r | |
722 | fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore\r | |
723 | }\r | |
724 | return tree.getItemHeight();\r | |
725 | }\r | |
726 | \r | |
fb5cad3d | 727 | }\r |