/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.gui.workflow.view.timeline;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
/**
* Dialog that helps to manage filter settings.
*
* @author Hendrik Abbenhaus
*/
public class TimelineFilterDialog extends Dialog implements ICheckStateListener {
/** Initial string. */
public String initialFilterText = "";
/** The viewer. */
private CheckboxTreeViewer viewer;
/** The content provider. */
private TreeContentProvider contentProvider;
private Tree tree;
private ComponentViewerFilter filter;
private Set<String> componentNameFilter = new HashSet<String>();
private TimelineFilterTreeNode[] currentCheckedElements = null;
public TimelineFilterDialog(Shell parentShell, String[] currentFilter) {
super(parentShell);
this.setShellStyle(SWT.RESIZE | SWT.MAX | SWT.PRIMARY_MODAL);
for (String currentString : currentFilter) {
componentNameFilter.add(currentString);
}
}
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
GridLayout gridLayout = new GridLayout(3, false);
gridLayout.horizontalSpacing = 0;
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
container.setLayout(gridLayout);
Group sourceGroup = new Group(container, SWT.NONE);
sourceGroup.setText(Messages.selectComponents);
GridData gridData1 = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
gridData1.widthHint = 1;
sourceGroup.setLayoutData(gridData1);
GridLayout gridLayout1 = new GridLayout(1, false);
gridLayout1.marginTop = 5;
gridLayout1.marginWidth = 0;
gridLayout1.verticalSpacing = 0;
gridLayout1.marginHeight = 0;
gridLayout1.horizontalSpacing = 0;
sourceGroup.setLayout(gridLayout1);
viewer = new CheckboxTreeViewer(sourceGroup, SWT.NONE);
tree = viewer.getTree();
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
tree.setLinesVisible(true);
// viewer.addCheckStateListener(
filter = new ComponentViewerFilter(initialFilterText);
viewer.addFilter(filter);
viewer.setUseHashlookup(true);
TreeContentProvider contentprovider = new TreeContentProvider();
this.contentProvider = contentprovider;
viewer.setContentProvider(contentprovider);
viewer.setLabelProvider(new TreeLabelProvider());
viewer.addCheckStateListener(this);
GridData gridData31 = new GridData();
gridData31.grabExcessHorizontalSpace = true;
gridData31.horizontalAlignment = GridData.FILL;
Text sourceFilterText = new Text(sourceGroup, SWT.BORDER);
sourceFilterText.setMessage(Messages.filterDialogFilterDefault);
sourceFilterText.setToolTipText(Messages.filterDialogToolTipText);
sourceFilterText.setLayoutData(gridData31);
if (!initialFilterText.equals("")) {
sourceFilterText.setText(initialFilterText);
}
sourceFilterText.addModifyListener(new FilterTextModifyListener(currentCheckedElements));
return container;
}
@Override
protected void okPressed() {
componentNameFilter.clear();
for (TreeItem currentObj : viewer.getTree().getItems()) {
TimelineFilterTreeNode currentNode = (TimelineFilterTreeNode) currentObj.getData();
for (TimelineFilterTreeNode c : currentNode.getChildren()){
if (viewer.getChecked(c)){
componentNameFilter.add(c.getDisplayName());
}
}
}
super.okPressed();
};
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, true);
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(Messages.filterDialogTitle);
}
/**
* Sets the content of the timeline.
* @param newrows the rows
*/
public void setContent(TimelineComponentRow[] newrows) {
if (newrows == null || newrows.length == 0){
return;
}
List<TimelineFilterTreeNode> checkedElements = new ArrayList<TimelineFilterTreeNode>();
TimelineFilterTreeNode root = new TimelineFilterTreeNode();
for (TimelineComponentRow current : newrows) {
TimelineFilterTreeNode parentNode = root.hasChildWithComponentID(current.getComponentID());
if (parentNode == null) {
parentNode = new TimelineFilterTreeNode();
parentNode.setComponentID(current.getComponentID());
parentNode.setParent(root);
root.addChild(parentNode);
}
TimelineFilterTreeNode n = new TimelineFilterTreeNode();
n.setRow(current);
if (isAllowedComponentName(current.getName())){
checkedElements.add(n);
}
parentNode.addChild(n);
}
viewer.setInput(root);
viewer.refresh();
viewer.expandAll();
this.currentCheckedElements = checkedElements.toArray(new TimelineFilterTreeNode[checkedElements.size()]);
viewer.setCheckedElements(this.currentCheckedElements);
for (TimelineFilterTreeNode currentNode : checkedElements){
updateTree(currentNode, getCheckedElements(), true);
}
}
/**
*
* @param name
* @return Returns {@value false}, if the given component-name is not allowed and
* {@value true} if there is no componentNameFilter or contains the given name.
*/
private boolean isAllowedComponentName(String name){
if (componentNameFilter == null){
return true;
}
for (String currentFilterString : componentNameFilter){
if (name.equals(currentFilterString)){
return true;
}
}
return false;
}
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
Object node = event.getElement();
if (viewer.getGrayed(node)) {
viewer.setGrayChecked(node, false);
}
List<Object> checkedElements = getCheckedElements();
updateTree(node, checkedElements, event.getChecked());
}
/**
* Returns an array of filtered Component names.
* @return the array of filtered Component names
*/
public String[] getFilteredNames() {
return componentNameFilter.toArray(new String[componentNameFilter.size()]);
}
/**
* Updates the tree.
* @param node the node
* @param checkedElements the checked elements
* @param checked the checked
*/
private void updateTree(Object node, List<Object> checkedElements, boolean checked) {
List<Object> descendants = getDescendants(node);
Set<Object> checkedSet = new HashSet<Object>(checkedElements);
for (Object n : descendants) {
viewer.setGrayChecked(n, false);
viewer.setChecked(n, checked);
if (checked) {
checkedSet.add(n);
} else {
checkedSet.remove(n);
}
}
updateAncestors(node, checkedSet);
}
/**
* Update ancestors.
* @param child the child
* @param checkedElements the checked elements
*/
private void updateAncestors(Object child, Set<Object> checkedElements) {
Object parent = contentProvider.getParent(child);
if (parent == null) {
return;
}
boolean isGreyed = viewer.getChecked(child) && viewer.getGrayed(child);
if (isGreyed) {
// if child is greyed then everying up should be greyed as well
viewer.setGrayChecked(parent, true);
} else {
Object[] children = contentProvider.getChildren(parent);
List<Object> cloned = new ArrayList<Object>();
cloned.addAll(Arrays.asList(children));
cloned.removeAll(checkedElements);
if (cloned.isEmpty()) {
// every child is checked
viewer.setGrayed(parent, false);
viewer.setChecked(parent, true);
checkedElements.add(parent);
} else {
if (viewer.getChecked(parent) && !viewer.getGrayed(parent)) {
checkedElements.remove(parent);
}
viewer.setGrayChecked(parent, false);
// some children selected but not all
if (cloned.size() < children.length) {
viewer.setGrayChecked(parent, true);
}
}
}
updateAncestors(parent, checkedElements);
}
/**
* Gets the descendants.
*
* @param node the node
* @return the descendants
*/
private List<Object> getDescendants(Object node) {
List<Object> desc = new ArrayList<Object>();
getDescendantsHelper(desc, node);
return desc;
}
/**
* Gets the descendants helper.
*
* @param descendants the descendants
* @param node the node
* @return the descendants helper
*/
private void getDescendantsHelper(List<Object> descendants, Object node) {
Object[] children = contentProvider.getChildren(node);
if (children == null || children.length == 0) {
return;
}
descendants.addAll(Arrays.asList(children));
for (Object child : children) {
getDescendantsHelper(descendants, child);
}
}
/**
* Gets the checked elements (excluding grayed out elements).
*
* @return the checked elements
*/
public List<Object> getCheckedElements() {
List<Object> checkedElements = new ArrayList<Object>(Arrays.asList(viewer.getCheckedElements()));
checkedElements.removeAll(getGrayedElements());
return checkedElements;
}
/**
* Gets the grayed elements.
*
* @return the grayed elements
*/
public List<Object> getGrayedElements() {
return Arrays.asList(viewer.getGrayedElements());
}
/**
* Listener that updates tree on filter modification.
*
* @author Oliver Seebach
*/
private final class FilterTextModifyListener implements ModifyListener {
/**
* Constructor receiving the currently checked elements in the tree.
*
* @param currentCheckedElements The currently checked elements in the tree
*/
FilterTextModifyListener(TimelineFilterTreeNode[] currentCheckedElements) {
}
@Override
public void modifyText(ModifyEvent arg0) {
currentCheckedElements = getCheckedElements().toArray(new TimelineFilterTreeNode[getCheckedElements().size()]);
filter.setFilterString(((Text) arg0.getSource()).getText());
viewer.refresh();
viewer.expandAll();
viewer.setCheckedElements(currentCheckedElements);
for (TimelineFilterTreeNode currentNode : currentCheckedElements){
updateTree(currentNode, getCheckedElements(), true);
}
}
}
/**
* Label Provider for Component Tree.
* @author Hendrik Abbenhaus
*/
public class TreeLabelProvider extends LabelProvider {
@Override
public String getText(Object element) {
if (element instanceof TimelineFilterTreeNode) {
TimelineFilterTreeNode current = (TimelineFilterTreeNode) element;
return current.getDisplayName();
}
return null;
}
@Override
public Image getImage(Object element) {
if (element instanceof TimelineFilterTreeNode) {
TimelineFilterTreeNode current = (TimelineFilterTreeNode) element;
if (current.hasRow()) {
return current.getRow().getIcon();
} else {
return TimelineView.getImageIconFromId(current.getComponentID(), this);
}
}
return null;
}
}
/**
* Content Provider for Component Tree.
* @author Hendrik Abbenhaus
*/
public class TreeContentProvider implements ITreeContentProvider {
@Override
public Object[] getChildren(Object parentElement) {
return ((TimelineFilterTreeNode) parentElement).getChildren().toArray();
}
@Override
public Object getParent(Object element) {
return ((TimelineFilterTreeNode) element).getParent();
}
@Override
public boolean hasChildren(Object element) {
return !((TimelineFilterTreeNode) element).getChildren().isEmpty();
}
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
public void dispose() {}
@Override
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {}
}
/**
* Filter to remove unwanted components in {@link TimelineFilterDialog}.
* @author Hendrik Abbenhaus
*/
public class ComponentViewerFilter extends ViewerFilter {
private String filterString = "";
public ComponentViewerFilter(String initialText) {
if (!initialText.equals("")) {
filterString = initialText;
}
}
@Override
public boolean select(Viewer arg0, Object arg1, Object arg2) {
if (arg2 instanceof TimelineFilterTreeNode) {
TimelineFilterTreeNode item = ((TimelineFilterTreeNode) arg2);
if (item.getDisplayName().toLowerCase().toString().contains(filterString.toLowerCase())) {
return true;
}
if (item.getParent() != null){
if (item.getParent().getDisplayName() != null){
if (item.getParent().getDisplayName().toLowerCase().toString().contains(filterString.toLowerCase())){
return true;
}
}
}
if (item.getChildren() != null){
if (!item.getChildren().isEmpty()){
for (TimelineFilterTreeNode current : item.getChildren()){
if (current.getDisplayName().toLowerCase().toString().contains(filterString.toLowerCase())) {
return true;
}
}
}
}
}
return false;
}
public String getFilterString() {
return filterString;
}
public void setFilterString(String filterString) {
this.filterString = filterString;
}
}
}