/*******************************************************************************
* Copyright (c) 2009 Johannes Utzig.
* All rights reserved. This program and the accompanying materials
* are 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:
* Johannes Utzig - initial API and implementation
*******************************************************************************/
package org.eclipse.buckminster.ui.dependency.visualizer;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.buckminster.core.CorePlugin;
import org.eclipse.buckminster.core.metadata.model.BOMNode;
import org.eclipse.buckminster.core.metadata.model.BillOfMaterials;
import org.eclipse.buckminster.core.parser.IParser;
import org.eclipse.buckminster.ui.dependency.visualizer.controls.FilterControl;
import org.eclipse.buckminster.ui.dependency.visualizer.controls.HighlightPathControl;
import org.eclipse.buckminster.ui.dependency.visualizer.controls.LayoutControl;
import org.eclipse.buckminster.ui.dependency.visualizer.controls.listener.IViewerSettingChangeListener;
import org.eclipse.buckminster.ui.dependency.visualizer.controls.listener.ViewerSettingChangeEvent;
import org.eclipse.buckminster.ui.dependency.visualizer.input.BOMEditorInput;
import org.eclipse.buckminster.ui.dependency.visualizer.viewer.DependencyViewer;
import org.eclipse.buckminster.ui.dependency.visualizer.viewer.provider.DependencyLabelProvider;
import org.eclipse.buckminster.ui.dependency.visualizer.viewer.provider.DependencyTreeContentProvider;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.EditorPart;
/**
* The main class for the dependency visualization.
*
* This is the editor registered for bom files
*
* @author Johannes Utzig
*
*/
public class DependencyVisualizer extends EditorPart {
private DependencyViewer graphViewer;
private BillOfMaterials bom;
private FormToolkit toolkit;
private TreeViewer treeViewer;
public static final String ID = "org.eclipse.buckminster.dependency.visualizer.editor1"; //$NON-NLS-1$
@Override
public void createPartControl(Composite parent) {
parent.setLayout(new FillLayout());
Form form = toolkit.createForm(parent);
form.setText(Messages.DependencyVisualisation);
parent = form.getBody();
parent.setLayout(new GridLayout(2, false));
SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL | SWT.SMOOTH);
GridDataFactory.fillDefaults().grab(true, true).applyTo(sashForm);
createNavigationSection(sashForm);
Section graphSection = toolkit.createSection(sashForm, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR);
sashForm.setWeights(new int[] { 25, 70 });
graphSection.setText(Messages.Graph);
graphSection.setLayout(new FillLayout());
this.graphViewer = new DependencyViewer(graphSection, SWT.NONE);
graphViewer.setInputAndRoot(Collections.singletonList(bom));
graphSection.setClient(graphViewer.getControl());
createActionsSection(parent);
}
/**
* this implementation does nothing
*/
@Override
public void doSave(IProgressMonitor monitor) {
// nothing to do
}
/**
* this implementation does nothing
*/
@Override
public void doSaveAs() {
// nothing to do
}
@Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
setSite(site);
setInput(input);
toolkit = new FormToolkit(site.getShell().getDisplay());
if (input instanceof IFileEditorInput) {
IFileEditorInput editorInput = (IFileEditorInput) input;
try {
IParser<BillOfMaterials> parser = CorePlugin.getDefault().getParserFactory().getBillOfMaterialsParser(true);
InputStream in = editorInput.getFile().getContents();
bom = parser.parse(editorInput.getName(), in);
setPartName(bom.getViewName());
in.close();
} catch (CoreException e) {
Activator.getDefault().log(e);
} catch (IOException e) {
Activator.getDefault().log(e);
}
} else if (input instanceof BOMEditorInput) {
BOMEditorInput editorInput = (BOMEditorInput) input;
bom = editorInput.getBillOfMaterials();
}
}
@Override
public boolean isDirty() {
return false;
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public void setFocus() {
graphViewer.getControl().setFocus();
}
private void createActionsSection(Composite parent) {
Section actions = toolkit.createSection(parent, ExpandableComposite.EXPANDED | ExpandableComposite.TWISTIE | ExpandableComposite.COMPACT
| ExpandableComposite.TITLE_BAR);
actions.setLayout(new FillLayout());
ScrolledComposite scroll = new ScrolledComposite(actions, SWT.V_SCROLL) {
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
return new Point(getMinWidth(), 100);
}
};
toolkit.adapt(scroll);
Composite actionComposite = toolkit.createComposite(scroll);
GridLayout layout = new GridLayout(1, true);
layout.marginBottom = 0;
layout.marginLeft = 0;
layout.marginRight = 0;
layout.marginTop = 0;
actionComposite.setLayout(layout);
GridDataFactory.fillDefaults().applyTo(actionComposite);
Section filterSection = toolkit.createSection(actionComposite, ExpandableComposite.COMPACT | ExpandableComposite.SHORT_TITLE_BAR
| ExpandableComposite.EXPANDED);
filterSection.setText(Messages.Filters);
filterSection.setLayout(new GridLayout(1, true));
filterSection.setClient(createFilterComposite(filterSection));
GridDataFactory.fillDefaults().applyTo(filterSection);
Section layoutSection = toolkit.createSection(actionComposite, ExpandableComposite.COMPACT | ExpandableComposite.SHORT_TITLE_BAR
| ExpandableComposite.EXPANDED);
layoutSection.setLayout(new FillLayout());
layoutSection.setText(Messages.Layout);
layoutSection.setClient(createLayoutComposite(layoutSection));
GridDataFactory.fillDefaults().applyTo(layoutSection);
Section pathSection = toolkit.createSection(actionComposite, ExpandableComposite.COMPACT | ExpandableComposite.SHORT_TITLE_BAR
| ExpandableComposite.EXPANDED);
pathSection.setLayout(new FillLayout());
pathSection.setText(Messages.PathHighlighting);
pathSection.setClient(createHighlightComposite(pathSection));
GridDataFactory.fillDefaults().applyTo(pathSection);
scroll.setExpandHorizontal(true);
scroll.setExpandVertical(true);
scroll.setMinSize(actionComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
scroll.setContent(actionComposite);
scroll.layout();
actions.setClient(scroll);
actions.setText(Messages.Settings);
GridDataFactory.fillDefaults().applyTo(actions);
}
private Control createFilterComposite(Section filterSection) {
FilterControl control = new FilterControl(toolkit);
control.addViewerSettingChangeListener(graphViewer);
control.addViewerSettingChangeListener(new IViewerSettingChangeListener() {
@Override
public void viewerSettingChanged(ViewerSettingChangeEvent event) {
switch (event.getType()) {
case FILTER_ADDED:
treeViewer.addFilter((ViewerFilter) event.getData());
break;
case FILTER_REMOVED:
treeViewer.removeFilter((ViewerFilter) event.getData());
break;
default:
break;
}
}
});
return control.createControl(filterSection);
}
private Control createHighlightComposite(Section layoutSection) {
HighlightPathControl control = new HighlightPathControl(toolkit);
control.addViewerSettingChangeListener(graphViewer);
return control.createControl(layoutSection);
}
private Control createLayoutComposite(Section layoutSection) {
LayoutControl control = new LayoutControl(toolkit);
control.addViewerSettingChangeListener(graphViewer);
return control.createControl(layoutSection);
}
private void createNavigationSection(Composite parent) {
Section navigationSection = toolkit.createSection(parent, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR
| ExpandableComposite.COMPACT | ExpandableComposite.TWISTIE);
GridDataFactory.fillDefaults().grab(false, true).applyTo(navigationSection);
navigationSection.setText(Messages.Navigation);
navigationSection.setLayout(new FillLayout());
treeViewer = new TreeViewer(toolkit.createTree(navigationSection, SWT.NONE));
treeViewer.setAutoExpandLevel(1);
treeViewer.setUseHashlookup(true);
navigationSection.setClient(treeViewer.getControl());
final DependencyTreeContentProvider treeContentProvider = new DependencyTreeContentProvider(treeViewer);
treeViewer.setContentProvider(treeContentProvider);
treeViewer.setLabelProvider(new DependencyLabelProvider());
List<BOMNode> input = new ArrayList<BOMNode>();
input.add(bom);
treeViewer.setInput(input);
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
Object o = ((IStructuredSelection) event.getSelection()).getFirstElement();
// zest seems to have issues with only one element in the graph
// (and it wouldn't make much sense anyway)
if (treeContentProvider.hasChildren(o)) {
if (o instanceof BOMNode) {
BOMNode node = (BOMNode) o;
DependencyVisualizer.this.graphViewer.setInputAndRoot(Collections.singletonList(node));
}
} else {
// set selection to match the tree
if (event.getSelection() instanceof ITreeSelection) {
ITreeSelection treeSelection = (ITreeSelection) event.getSelection();
TreePath[] paths = treeSelection.getPaths();
if (paths == null || paths.length == 0) {
// the previously selected element got filtered out.
// fall back to the root
DependencyVisualizer.this.graphViewer.setInputAndRoot(Collections.singletonList(bom));
DependencyVisualizer.this.graphViewer.setSelection(new StructuredSelection(bom), true);
return;
}
TreePath path = paths[0].getParentPath();
if (path != null) {
Object nodeParent = path.getLastSegment();
if (nodeParent instanceof BOMNode) {
BOMNode node = (BOMNode) nodeParent;
DependencyVisualizer.this.graphViewer.setInputAndRoot(Collections.singletonList(node));
DependencyVisualizer.this.graphViewer.setSelection(event.getSelection(), true);
}
}
}
}
}
});
}
}