--- /dev/null
+/**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+
+package eu.etaxonomy.taxeditor.featuretree.e4;
+
+import java.util.Collection;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.e4.ui.di.Persist;
+import org.eclipse.e4.ui.model.application.ui.MDirtyable;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSourceAdapter;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+
+import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
+import eu.etaxonomy.cdm.api.service.IFeatureNodeService;
+import eu.etaxonomy.cdm.api.service.IFeatureTreeService;
+import eu.etaxonomy.cdm.api.service.config.FeatureNodeDeletionConfigurator;
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.FeatureNode;
+import eu.etaxonomy.cdm.model.description.FeatureTree;
+import eu.etaxonomy.taxeditor.featuretree.AvailableFeaturesWizard;
+import eu.etaxonomy.taxeditor.featuretree.FeatureNodeTransfer;
+import eu.etaxonomy.taxeditor.featuretree.FeatureTreeContentProvider;
+import eu.etaxonomy.taxeditor.featuretree.FeatureTreeLabelProvider;
+import eu.etaxonomy.taxeditor.model.ImageResources;
+import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.taxeditor.store.CdmStore;
+import eu.etaxonomy.taxeditor.ui.dialog.selection.FeatureTreeSelectionDialog;
+
+/**
+ *
+ * @author pplitzner
+ * @date 06.06.2017
+ *
+ */
+public class FeatureTreeEditor implements
+ ModifyListener, ISelectionChangedListener {
+
+ private TreeViewer viewer;
+ private Label label_title;
+ private Button button_add;
+ private Button button_remove;
+ private Label label_treeInfo;
+ private FeatureTree featureTree;
+ private Text text_title;
+ private Button btnOpenFeatureTree;
+ private Button btnNewFeatureTree;
+
+ private ConversationHolder conversation;
+ @Inject
+ private MDirtyable dirty;
+
+ private Shell shell;
+
+ @Inject
+ public FeatureTreeEditor(@Named(IServiceConstants.ACTIVE_SHELL)Shell shell) {
+ this.shell = shell;
+ if(conversation==null){
+ conversation = CdmStore.createConversation();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @PostConstruct
+ public void createControl(Composite parent, @Named(IServiceConstants.ACTIVE_SHELL)Shell shell) {
+ parent.setLayout(new GridLayout());
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Composite composite_treeTitle = new Composite(composite, SWT.NULL);
+ composite_treeTitle.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true,
+ false));
+ composite_treeTitle.setLayout(new GridLayout(4, false));
+
+ label_title = new Label(composite_treeTitle, SWT.NULL);
+ label_title.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ label_title.setText("Title");
+
+ text_title = new Text(composite_treeTitle, SWT.BORDER);
+ text_title.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ btnOpenFeatureTree = new Button(composite_treeTitle, SWT.NONE);
+ btnOpenFeatureTree.setImage(ImageResources.getImage(ImageResources.BROWSE_ICON));
+ btnOpenFeatureTree.setText("Open Tree");
+
+ btnNewFeatureTree = new Button(composite_treeTitle, SWT.NONE);
+ btnNewFeatureTree.setText("New Feature Tree");
+
+ Composite composite_treeContent = new Composite(composite, SWT.NULL);
+ composite_treeContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL,
+ true, true));
+ composite_treeContent.setLayout(new GridLayout(2, false));
+
+ viewer = new TreeViewer(composite_treeContent);
+ Tree tree = viewer.getTree();
+ tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ viewer.getControl().setLayoutData(
+ new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Composite composite_buttons = new Composite(composite_treeContent,
+ SWT.NULL);
+ composite_buttons.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false,
+ false));
+ composite_buttons.setLayout(new GridLayout());
+
+ button_add = new Button(composite_buttons, SWT.PUSH);
+ button_add.setText("Add");
+ button_add.setToolTipText("Add a feature to this feature treee.");
+ button_remove = new Button(composite_buttons, SWT.PUSH);
+ button_remove.setText("Remove");
+ button_remove
+ .setToolTipText("Remove a feature from this feature tree.");
+
+ label_treeInfo = new Label(composite, SWT.NULL);
+ label_treeInfo
+ .setText("Order and nesting of feature nodes may be changed through drag and drop.");
+
+ init(shell);
+ }
+
+ private void setDirty(boolean isDirty){
+ this.dirty.setDirty(isDirty);
+ }
+
+ private void init(Shell shell) {
+ viewer.setContentProvider(new FeatureTreeContentProvider());
+ viewer.setLabelProvider(new FeatureTreeLabelProvider());
+
+ int ops = DND.DROP_COPY | DND.DROP_MOVE;
+ Transfer[] transfers = new Transfer[] { FeatureNodeTransfer
+ .getInstance() };
+ viewer.addDragSupport(ops, transfers, new FeatureNodeDragListener(
+ viewer));
+ viewer.addDropSupport(ops, transfers,
+ new FeatureNodeDropAdapter(viewer));
+
+ viewer.addSelectionChangedListener(this);
+
+ button_add.addSelectionListener(new AddButtonListener());
+ button_remove.addSelectionListener(new RemoveSelectionListener());
+
+ btnOpenFeatureTree.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ FeatureTree tree = FeatureTreeSelectionDialog.select(shell, conversation, null);
+ setSelectedTree(tree);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+ }
+
+ public void setSelectedTree(FeatureTree featureTree) {
+ this.featureTree = HibernateProxyHelper.deproxy(featureTree, FeatureTree.class);
+ this.featureTree.setRoot(HibernateProxyHelper.deproxy(featureTree.getRoot(), FeatureNode.class));
+ viewer.setInput(featureTree);
+
+ text_title.removeModifyListener(this);
+ text_title.setText(featureTree.getTitleCache());
+ text_title.addModifyListener(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void modifyText(ModifyEvent e) {
+ featureTree.setTitleCache(text_title.getText(), true);
+ setDirty(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) event
+ .getSelection();
+
+ button_add.setEnabled(selection.size() <= 1);
+ button_remove.setEnabled(selection.size() > 0);
+ }
+
+ @Persist
+ public void save(){
+ CdmStore.getService(IFeatureTreeService.class).saveOrUpdate(featureTree);
+ }
+
+ private class AddButtonListener extends SelectionAdapter {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ AvailableFeaturesWizard wizard = new AvailableFeaturesWizard(
+ featureTree);
+ WizardDialog dialog = new WizardDialog(shell, wizard);
+
+ if (dialog.open() == IStatus.OK) {
+ IStructuredSelection selection = (IStructuredSelection) viewer
+ .getSelection();
+ FeatureNode parent = (FeatureNode) (selection.getFirstElement() != null ? selection
+ .getFirstElement() : ((FeatureTree) viewer.getInput())
+ .getRoot());
+ Collection<Feature> additionalFeatures = wizard
+ .getAdditionalFeatures();
+ for (Feature feature : additionalFeatures) {
+ FeatureNode child = FeatureNode.NewInstance(feature);
+ CdmStore.getService(IFeatureNodeService.class).merge(child, true);
+
+ parent.addChild(child);
+ }
+ viewer.refresh();
+ }
+ }
+
+ }
+
+ private class RemoveSelectionListener extends SelectionAdapter {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection = (IStructuredSelection) viewer
+ .getSelection();
+
+ for (Object selectedObject : selection.toArray()) {
+ FeatureNode featureNode = (FeatureNode) selectedObject;
+ FeatureNode parent = featureNode.getParent();
+ parent.removeChild(featureNode);
+
+ CdmStore.getService(IFeatureNodeService.class).deleteFeatureNode(featureNode.getUuid(), new FeatureNodeDeletionConfigurator());
+
+ }
+ viewer.refresh();
+ }
+ }
+
+ private class FeatureNodeDragListener extends DragSourceAdapter {
+
+ private final TreeViewer viewer;
+
+ public FeatureNodeDragListener(TreeViewer viewer) {
+ this.viewer = viewer;
+ }
+
+ /**
+ * Method declared on DragSourceListener
+ */
+ @Override
+ public void dragFinished(DragSourceEvent event) {
+ if (!event.doit) {
+ return;
+ }
+ // if the featureNode was moved, remove it from the source viewer
+ if (event.detail == DND.DROP_MOVE) {
+ IStructuredSelection selection = (IStructuredSelection) viewer
+ .getSelection();
+ viewer.refresh();
+ }
+ }
+
+ /**
+ * Method declared on DragSourceListener
+ */
+ @Override
+ public void dragSetData(DragSourceEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) viewer
+ .getSelection();
+ FeatureNode[] featureNodes = (FeatureNode[]) selection.toList()
+ .toArray(new FeatureNode[selection.size()]);
+ if (FeatureNodeTransfer.getInstance().isSupportedType(
+ event.dataType)) {
+ event.data = featureNodes;
+ }
+ }
+
+ /**
+ * Method declared on DragSourceListener
+ */
+ @Override
+ public void dragStart(DragSourceEvent event) {
+ event.doit = !viewer.getSelection().isEmpty();
+ }
+
+ }
+
+ private class FeatureNodeDropAdapter extends ViewerDropAdapter {
+
+ protected FeatureNodeDropAdapter(Viewer viewer) {
+ super(viewer);
+ }
+
+ @Override
+ public boolean performDrop(Object data) {
+ FeatureNode target = (FeatureNode) getCurrentTarget();
+ int position = 0;
+
+ if (target != null) {
+ int location = getCurrentLocation();
+ FeatureNode parent = target.getParent();
+ if (location == LOCATION_BEFORE) {
+ position = Math.max(0, parent.getIndex(target) - 1);
+ target = parent;
+ }
+
+ if (location == LOCATION_AFTER) {
+ position = parent.getIndex(target);
+ target = parent;
+ }
+ }
+
+ // set target to root node if there is no target specified
+ if (target == null) {
+ FeatureTree featureTree = (FeatureTree) getViewer().getInput();
+ target = featureTree.getRoot();
+ }
+
+ Object[] droppedObjects = (Object[]) data;
+ TreeViewer viewer = (TreeViewer) getViewer();
+
+ // cannot drop a feature node onto itself
+ for (Object droppedObject : droppedObjects) {
+ if (droppedObject == null) {
+ MessagingUtils.warningDialog(
+ "Operation not supported yet",
+ this,
+ "We are currently unable to change the order of freshly created "
+ + "feature trees nodes. Please close and reopen the dialog to change the order of features.");
+ return false;
+ }
+ if (droppedObject.equals(target)) {
+ return false;
+ }
+ }
+ for (Object droppedObject : droppedObjects) {
+ FeatureNode droppedNode = (FeatureNode) droppedObject;
+ target.addChild(droppedNode, position);
+ viewer.add(target, droppedNode);
+ viewer.reveal(droppedNode);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean validateDrop(Object target, int operation,
+ TransferData transferData) {
+ return FeatureNodeTransfer.getInstance().isSupportedType(
+ transferData);
+ }
+
+ }
+}