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