1 /*******************************************************************************
2 * Copyright (c) 2000, 2013 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font should be
11 * activated and used by other components.
12 * Lubomir Marinov <lubomir.marinov@gmail.com> - Fix for bug 182122 -[Dialogs]
13 * CheckedTreeSelectionDialog#createSelectionButtons(Composite) fails to
14 * align the selection buttons to the right
15 * François Rajotte - Support for multiple columns + selection control
16 * Patrick Tasse - Fix Sonar warnings
17 * Generoso Pagano - Add tree filter
18 *******************************************************************************/
20 package org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.dialogs
;
22 import java
.util
.ArrayList
;
23 import java
.util
.Arrays
;
24 import java
.util
.List
;
26 import org
.eclipse
.core
.runtime
.IStatus
;
27 import org
.eclipse
.core
.runtime
.Status
;
28 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
29 import org
.eclipse
.jface
.viewers
.CheckStateChangedEvent
;
30 import org
.eclipse
.jface
.viewers
.CheckboxTreeViewer
;
31 import org
.eclipse
.jface
.viewers
.IBaseLabelProvider
;
32 import org
.eclipse
.jface
.viewers
.ICheckStateListener
;
33 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
34 import org
.eclipse
.jface
.viewers
.TreeSelection
;
35 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
36 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
37 import org
.eclipse
.swt
.SWT
;
38 import org
.eclipse
.swt
.custom
.BusyIndicator
;
39 import org
.eclipse
.swt
.events
.SelectionAdapter
;
40 import org
.eclipse
.swt
.events
.SelectionEvent
;
41 import org
.eclipse
.swt
.layout
.GridData
;
42 import org
.eclipse
.swt
.layout
.GridLayout
;
43 import org
.eclipse
.swt
.widgets
.Button
;
44 import org
.eclipse
.swt
.widgets
.Composite
;
45 import org
.eclipse
.swt
.widgets
.Control
;
46 import org
.eclipse
.swt
.widgets
.Label
;
47 import org
.eclipse
.swt
.widgets
.Shell
;
48 import org
.eclipse
.swt
.widgets
.Tree
;
49 import org
.eclipse
.swt
.widgets
.TreeColumn
;
50 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Messages
;
51 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
52 import org
.eclipse
.ui
.PlatformUI
;
53 import org
.eclipse
.ui
.dialogs
.ISelectionStatusValidator
;
54 import org
.eclipse
.ui
.dialogs
.PatternFilter
;
55 import org
.eclipse
.ui
.dialogs
.SelectionStatusDialog
;
58 * Filter dialog for the time graphs This class is derived from the
59 * CheckedTreeSelectionDialog It was necessary to develop this similar dialog to
60 * allow multiple columns
64 * @author François Rajotte
66 public class TimeGraphFilterDialog
extends SelectionStatusDialog
{
67 private static final int BUTTON_CHECK_SELECTED_ID
= IDialogConstants
.CLIENT_ID
;
68 private static final int BUTTON_UNCHECK_SELECTED_ID
= IDialogConstants
.CLIENT_ID
+ 1;
69 private static final int BUTTON_CHECK_SUBTREE_ID
= IDialogConstants
.CLIENT_ID
+ 2;
70 private static final int BUTTON_UNCHECK_SUBTREE_ID
= IDialogConstants
.CLIENT_ID
+ 3;
72 private static final int DEFAULT_WIDTH
= 60;
73 private static final int DEFAULT_HEIGHT
= 18;
75 private FilteredCheckboxTree fTree
;
77 private IBaseLabelProvider fLabelProvider
;
79 private ITreeContentProvider fContentProvider
;
81 private String
[] fColumnNames
;
83 private ISelectionStatusValidator fValidator
= null;
85 private ViewerComparator fComparator
;
87 private String fEmptyListMessage
= ""; //$NON-NLS-1$
89 private IStatus fCurrStatus
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
90 0, "", null); //$NON-NLS-1$
92 private List
<ViewerFilter
> fFilters
;
94 private Object fInput
;
96 private boolean fIsEmpty
;
98 private int fWidth
= DEFAULT_WIDTH
;
100 private int fHeight
= DEFAULT_HEIGHT
;
102 private Object
[] fExpandedElements
;
105 * Constructs an instance of <code>ElementTreeSelectionDialog</code>.
108 * The shell to parent from.
110 public TimeGraphFilterDialog(Shell parent
) {
112 setResult(new ArrayList
<>(0));
113 setStatusLineAboveButtons(true);
114 setHelpAvailable(false);
115 fExpandedElements
= null;
119 * Sets the initial selection. Convenience method.
122 * the initial selection.
124 public void setInitialSelection(Object selection
) {
125 setInitialSelections(new Object
[] { selection
});
129 * Sets the message to be displayed if the list is empty.
132 * the message to be displayed.
134 public void setEmptyListMessage(String message
) {
135 fEmptyListMessage
= message
;
139 * Sets the comparator used by the tree viewer.
144 public void setComparator(ViewerComparator comparator
) {
145 fComparator
= comparator
;
149 * Adds a filter to the tree viewer.
154 public void addFilter(ViewerFilter filter
) {
155 if (fFilters
== null) {
156 fFilters
= new ArrayList
<>();
158 fFilters
.add(filter
);
162 * Sets an optional validator to check if the selection is valid. The
163 * validator is invoked whenever the selection changes.
166 * the validator to validate the selection.
168 public void setValidator(ISelectionStatusValidator validator
) {
169 fValidator
= validator
;
173 * Sets the tree input.
178 public void setInput(Object input
) {
183 * Expands elements in the tree.
186 * The elements that will be expanded.
188 public void setExpandedElements(Object
[] elements
) {
189 if (elements
!= null) {
190 fExpandedElements
= Arrays
.copyOf(elements
, elements
.length
);
192 fExpandedElements
= null;
197 * Sets the size of the tree in unit of characters.
200 * the width of the tree.
202 * the height of the tree.
204 public void setSize(int width
, int height
) {
210 * @param contentProvider
211 * The content provider for the table
213 public void setContentProvider(ITreeContentProvider contentProvider
) {
214 fContentProvider
= contentProvider
;
218 * @param labelProvider
219 * The label provider for the table
221 public void setLabelProvider(IBaseLabelProvider labelProvider
) {
222 fLabelProvider
= labelProvider
;
227 * An array of column names to display
229 public void setColumnNames(String
[] columnNames
) {
230 if (columnNames
!= null) {
231 fColumnNames
= Arrays
.copyOf(columnNames
, columnNames
.length
);
238 * Validate the receiver and update the status with the result.
241 protected void updateOKStatus() {
243 if (fValidator
!= null) {
244 fCurrStatus
= fValidator
.validate(fTree
.getCheckedElements());
245 updateStatus(fCurrStatus
);
246 } else if (!fCurrStatus
.isOK()) {
247 fCurrStatus
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
248 IStatus
.OK
, "", //$NON-NLS-1$
252 fCurrStatus
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
253 IStatus
.OK
, fEmptyListMessage
, null);
255 updateStatus(fCurrStatus
);
260 fIsEmpty
= evaluateIfTreeEmpty(fInput
);
262 return getReturnCode();
266 protected void cancelPressed() {
268 super.cancelPressed();
272 protected void computeResult() {
273 setResult(Arrays
.asList(fTree
.getCheckedElements()));
277 public void create() {
278 BusyIndicator
.showWhile(null, new Runnable() {
281 TimeGraphFilterDialog
.super.create();
282 fTree
.setCheckedElements(getInitialElementSelections()
284 if (fExpandedElements
!= null) {
285 fTree
.getViewer().setExpandedElements(fExpandedElements
);
293 protected Control
createDialogArea(Composite parent
) {
294 Composite composite
= (Composite
) super.createDialogArea(parent
);
295 Label messageLabel
= createMessageArea(composite
);
296 CheckboxTreeViewer treeViewer
= createTreeViewer(composite
);
297 Control buttonComposite
= createSelectionButtons(composite
);
298 GridData data
= new GridData(GridData
.FILL_BOTH
);
299 data
.widthHint
= convertWidthInCharsToPixels(fWidth
);
300 data
.heightHint
= convertHeightInCharsToPixels(fHeight
);
301 Tree treeWidget
= treeViewer
.getTree();
302 treeWidget
.setLayoutData(data
);
303 treeWidget
.setFont(parent
.getFont());
305 messageLabel
.setEnabled(false);
306 treeWidget
.setEnabled(false);
307 buttonComposite
.setEnabled(false);
313 * Creates the tree viewer.
316 * the parent composite
317 * @return the tree viewer
319 protected CheckboxTreeViewer
createTreeViewer(Composite parent
) {
320 PatternFilter filter
= new TreePatternFilter();
321 filter
.setIncludeLeadingWildcard(true);
322 fTree
= new FilteredCheckboxTree(parent
, SWT
.BORDER
| SWT
.MULTI
, filter
, true);
324 Tree tree
= fTree
.getViewer().getTree();
325 tree
.setHeaderVisible(true);
326 for (String columnName
: fColumnNames
) {
327 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
328 column
.setText(columnName
);
332 fTree
.getViewer().setContentProvider(fContentProvider
);
333 fTree
.getViewer().setLabelProvider(fLabelProvider
);
334 fTree
.addCheckStateListener(new CheckStateListener());
335 fTree
.getViewer().setComparator(fComparator
);
336 if (fFilters
!= null) {
337 for (int i
= 0; i
!= fFilters
.size(); i
++) {
338 fTree
.getViewer().addFilter(fFilters
.get(i
));
341 fTree
.getViewer().setInput(fInput
);
343 // pack the columns again for a nice view...
344 for (TreeColumn column
: tree
.getColumns()) {
347 return (CheckboxTreeViewer
) fTree
.getViewer();
351 * Returns the tree viewer.
353 * @return the tree viewer
355 protected CheckboxTreeViewer
getTreeViewer() {
356 return (CheckboxTreeViewer
) fTree
.getViewer();
360 * Adds the selection and deselection buttons to the dialog.
363 * the parent composite
364 * @return Composite the composite the buttons were created in.
366 protected Composite
createSelectionButtons(Composite composite
) {
367 Composite buttonComposite
= new Composite(composite
, SWT
.RIGHT
);
368 GridLayout layout
= new GridLayout();
369 layout
.marginWidth
= 0;
370 layout
.horizontalSpacing
= convertHorizontalDLUsToPixels(IDialogConstants
.HORIZONTAL_SPACING
);
371 buttonComposite
.setLayout(layout
);
372 buttonComposite
.setFont(composite
.getFont());
373 GridData data
= new GridData(GridData
.HORIZONTAL_ALIGN_END
374 | GridData
.GRAB_HORIZONTAL
);
375 data
.grabExcessHorizontalSpace
= true;
376 buttonComposite
.setLayoutData(data
);
378 /* Create the buttons in the good order to place them as we want */
379 Button checkSelectedButton
= createButton(buttonComposite
,
380 BUTTON_CHECK_SELECTED_ID
, Messages
.TmfTimeFilterDialog_CHECK_SELECTED
,
382 Button checkSubtreeButton
= createButton(buttonComposite
,
383 BUTTON_CHECK_SUBTREE_ID
, Messages
.TmfTimeFilterDialog_CHECK_SUBTREE
,
385 Button checkAllButton
= createButton(buttonComposite
,
386 IDialogConstants
.SELECT_ALL_ID
, Messages
.TmfTimeFilterDialog_CHECK_ALL
,
389 Button uncheckSelectedButton
= createButton(buttonComposite
,
390 BUTTON_UNCHECK_SELECTED_ID
, Messages
.TmfTimeFilterDialog_UNCHECK_SELECTED
,
392 Button uncheckSubtreeButton
= createButton(buttonComposite
,
393 BUTTON_UNCHECK_SUBTREE_ID
, Messages
.TmfTimeFilterDialog_UNCHECK_SUBTREE
,
395 Button uncheckAllButton
= createButton(buttonComposite
,
396 IDialogConstants
.DESELECT_ALL_ID
, Messages
.TmfTimeFilterDialog_UNCHECK_ALL
,
400 * Apply the layout again after creating the buttons to override
401 * createButton messing with the columns
403 layout
.numColumns
= 3;
404 buttonComposite
.setLayout(layout
);
406 /* Add a listener to each button */
407 checkSelectedButton
.addSelectionListener(new SelectionAdapter() {
409 public void widgetSelected(SelectionEvent e
) {
410 TreeSelection selection
= (TreeSelection
) fTree
.getViewer().getSelection();
412 for (Object element
: selection
.toArray()) {
413 checkElement(element
);
420 checkSubtreeButton
.addSelectionListener(new SelectionAdapter() {
422 public void widgetSelected(SelectionEvent e
) {
423 TreeSelection selection
= (TreeSelection
) fTree
.getViewer().getSelection();
425 for (Object element
: selection
.toArray()) {
426 checkElementAndSubtree(element
);
431 checkAllButton
.addSelectionListener(new SelectionAdapter() {
433 public void widgetSelected(SelectionEvent e
) {
434 Object
[] viewerElements
= fContentProvider
.getElements(fInput
);
436 for (int i
= 0; i
< viewerElements
.length
; i
++) {
437 fTree
.setSubtreeChecked(viewerElements
[i
], true);
444 uncheckSelectedButton
.addSelectionListener(new SelectionAdapter() {
446 public void widgetSelected(SelectionEvent e
) {
447 TreeSelection selection
= (TreeSelection
) fTree
.getViewer().getSelection();
449 for (Object element
: selection
.toArray()) {
450 uncheckElement(element
);
457 uncheckSubtreeButton
.addSelectionListener(new SelectionAdapter() {
459 public void widgetSelected(SelectionEvent e
) {
460 TreeSelection selection
= (TreeSelection
) fTree
.getViewer().getSelection();
462 for (Object element
: selection
.toArray()) {
463 uncheckElement(element
);
470 uncheckAllButton
.addSelectionListener(new SelectionAdapter() {
472 public void widgetSelected(SelectionEvent e
) {
473 Object
[] viewerElements
= fContentProvider
.getElements(fInput
);
474 for (Object element
: viewerElements
) {
475 if (fTree
.getViewer().testFindItem(element
) != null) {
476 // uncheck only visible roots and their children
477 uncheckElement(element
);
484 return buttonComposite
;
488 * Check an element and all its parents.
491 * The element to check.
493 private void checkElement(Object element
) {
494 fTree
.setChecked(element
, true);
496 Object parent
= fContentProvider
.getParent(element
);
498 if (parent
!= null && !fTree
.getChecked(parent
)) {
499 checkElement(parent
);
504 * Check an element, all its parents and all its children.
507 * The element to check.
509 private void checkElementAndSubtree(Object element
) {
510 checkElement(element
);
512 for (Object child
: fContentProvider
.getChildren(element
)) {
513 checkElementAndSubtree(child
);
518 * Uncheck an element and all its children.
521 * The element to uncheck.
523 private void uncheckElement(Object element
) {
524 fTree
.setChecked(element
, false);
526 for (Object child
: fContentProvider
.getChildren(element
)) {
527 uncheckElement(child
);
531 private boolean evaluateIfTreeEmpty(Object input
) {
532 Object
[] elements
= fContentProvider
.getElements(input
);
533 if (elements
.length
> 0 && fFilters
!= null) {
534 for (int i
= 0; i
< fFilters
.size(); i
++) {
535 ViewerFilter curr
= fFilters
.get(i
);
536 elements
= curr
.filter(fTree
.getViewer(), input
, elements
);
539 return elements
.length
== 0;
546 private class CheckStateListener
implements ICheckStateListener
{
548 CheckStateListener() {
552 public void checkStateChanged(CheckStateChangedEvent event
) {
554 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
555 boolean checked
= event
.getChecked();
559 uncheckElement(entry
);
561 } catch (ClassCastException e
) {