/******************************************************************************* * Copyright (c) 2011, 2012, 2013, 2014 Red Hat, Inc. * All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.eclipse.bpmn2.modeler.ui.preferences; import java.util.List; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.list.WritableList; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; 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.Tree; public class ModelEnablementTreeViewer extends Composite { private Group group; private Tree tree; private CheckboxTreeViewer treeViewer; public ModelEnablementTreeViewer(Composite parent, String name) { super(parent, SWT.NONE); GridData data; setLayout(new GridLayout(1, false)); setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); group = new Group(this, SWT.NONE); group.setText(name); data = new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1); data.heightHint = 100; data.widthHint = 50; group.setLayoutData(data); group.setLayout(new GridLayout(1,false)); treeViewer = new CheckboxTreeViewer(group, SWT.BORDER); tree = treeViewer.getTree(); data = new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1); data.heightHint = 100; data.widthHint = 50; tree.setLayoutData(data); treeViewer.setCheckStateProvider(new ICheckStateProvider() { @Override public boolean isChecked(Object element) { if (element instanceof ModelEnablementTreeEntry) { ModelEnablementTreeEntry entry = (ModelEnablementTreeEntry)element; if (entry.getChildren().size()>0) { for (ModelEnablementTreeEntry child : entry.getChildren()) { if (child.getEnabled()) return true; } return false; } return entry.getEnabled(); } return false; } @Override public boolean isGrayed(Object element) { if (element instanceof ModelEnablementTreeEntry) { ModelEnablementTreeEntry entry = (ModelEnablementTreeEntry)element; int countEnabled = 0; for (ModelEnablementTreeEntry child : entry.getChildren()) { if (child.getEnabled()) ++countEnabled; } return countEnabled>0 && countEnabled != entry.getChildren().size(); } return false; } }); // adjust height of the tree viewers to fill their container when dialog is resized // oddly enough, setting GridData.widthHint still causes the controls to fill available // horizontal space, but setting heightHint just keeps them the same height. Probably // because a GridLayout has a fixed number of columns, but variable number of rows. parent.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { GridData gd = (GridData) tree.getLayoutData(); gd.heightHint = 1000; gd = (GridData) group.getLayoutData(); gd.heightHint = 1000; group.layout(); layout(); } }); treeViewer.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { boolean checked = event.getChecked(); Object element = event.getElement(); if (element instanceof ModelEnablementTreeEntry) { ModelEnablementTreeEntry entry = (ModelEnablementTreeEntry)element; updateDescendents(entry, checked); updateAncestors(entry.getParent(), checked); } } void updateDescendents(ModelEnablementTreeEntry entry, boolean checked) { for (ModelEnablementTreeEntry child : entry.getChildren()) { updateDescendents(child,checked); } entry.setSubtreeEnabled(checked); treeViewer.setSubtreeChecked(entry, checked); treeViewer.setChecked(entry, checked); treeViewer.setGrayed(entry, false); for (ModelEnablementTreeEntry friend : entry.getFriends()) { updateAncestors(friend, checked); if (friend.getParent()!=null) updateAncestors(friend.getParent(), checked); } for (ModelEnablementTreeEntry child : entry.getChildren()) { for (ModelEnablementTreeEntry friend : child.getFriends()) { if (child.getParent()!=null) updateAncestors(child.getParent(), checked); updateAncestors(friend, checked); } } } void updateAncestors(ModelEnablementTreeEntry entry, boolean checked) { while (entry!=null) { int enabled = entry.getSubtreeEnabledCount(); int size = entry.getSubtreeEnabledCount(); if (enabled==0) { treeViewer.setChecked(entry, false); entry.setEnabled(false); checked = true; } else if (enabled==size) { treeViewer.setChecked(entry, true); treeViewer.setGrayed(entry, false); entry.setEnabled(true); } else { treeViewer.setGrayChecked(entry, true); entry.setEnabled(true); } for (ModelEnablementTreeEntry friend : entry.getFriends()) { updateAncestors(friend, checked); } refreshSiblings(entry); entry = entry.getParent(); } } private void refreshSiblings(Object element) { Control kids[] = getParent().getChildren(); for (Control k : kids) { if (k instanceof ModelEnablementTreeViewer) { if (k.isVisible()) ((ModelEnablementTreeViewer)k).refresh(element); } } } }); treeViewer.setComparer(new IElementComparer() { @Override public boolean equals(Object a, Object b) { return a == b; } @Override public int hashCode(Object element) { return System.identityHashCode(element); } }); treeViewer.setUseHashlookup(true); } public DataBindingContext setInput(List<ModelEnablementTreeEntry> entries) { if (treeViewer==null || entries==null) return null; DataBindingContext bindingContext = new DataBindingContext(); // treeViewer.setContentProvider(new ITreeContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public void dispose() { } @Override public boolean hasChildren(Object element) { if (element instanceof ModelEnablementTreeEntry) { return !((ModelEnablementTreeEntry) element).getChildren().isEmpty(); } return false; } @Override public Object getParent(Object element) { if (element instanceof ModelEnablementTreeEntry) { return ((ModelEnablementTreeEntry) element).getParent(); } return null; } @Override public Object[] getElements(Object inputElement) { if (inputElement instanceof WritableList) { return ((WritableList) inputElement).toArray(); } return null; } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof ModelEnablementTreeEntry) { return ((ModelEnablementTreeEntry) parentElement).getChildren().toArray(); } return null; } }); treeViewer.setLabelProvider(new ILabelProvider() { @Override public void removeListener(ILabelProviderListener listener) { } @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void dispose() { } @Override public void addListener(ILabelProviderListener listener) { } @Override public Image getImage(Object element) { return null; } @Override public String getText(Object element) { if (element instanceof ModelEnablementTreeEntry) { return ((ModelEnablementTreeEntry) element).getName(); } return null; } }); WritableList writableList = new WritableList(entries, ModelEnablementTreeEntry.class); treeViewer.setInput(writableList); // return bindingContext; } public void addCheckStateListener(ICheckStateListener listener) { treeViewer.addCheckStateListener(listener); } public void refresh() { treeViewer.refresh(); } public void refresh(Object element) { treeViewer.refresh(element); } public Tree getTree() { return tree; } public void setVisible(boolean visible) { super.setVisible(visible); if (getParent().getLayout() instanceof GridLayout) { GridData data = (GridData)getLayoutData(); data.exclude = !visible; getParent().layout(); } } }