ref #6694 Implement Feature Tree Editor
authorPatrick Plitzner <p.plitzner@bgbm.org>
Tue, 6 Jun 2017 12:41:09 +0000 (14:41 +0200)
committerPatrick Plitzner <p.plitzner@bgbm.org>
Tue, 6 Jun 2017 12:41:09 +0000 (14:41 +0200)
eu.etaxonomy.taxeditor.store/fragment.e4xmi
eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/featuretree/e4/FeatureTreeEditor.java [new file with mode: 0644]

index 8bb56eeb9e6c1cbe158164529f6ac3e3988b358a..93d0dcc01b3c0bd93f7856525980ed4cfddd2124 100644 (file)
@@ -2,7 +2,8 @@
 <fragment:ModelFragments xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/descriptor/basic" xmlns:commands="http://www.eclipse.org/ui/2010/UIModel/application/commands" xmlns:fragment="http://www.eclipse.org/ui/2010/UIModel/fragment" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmlns:ui="http://www.eclipse.org/ui/2010/UIModel/application/ui" xmi:id="_OhwnQCTfEeeiN5lBIuqN3g">
   <imports xsi:type="commands:Command" xmi:id="_bnVKsCZxEeeQLpuomSmVoQ" elementId="eu.etaxonomy.taxeditor.command.openPart"/>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_xVa1wCXXEeex9MwMOgUeWw" featurename="descriptors" parentElementId="xpath:/">
-    <elements xsi:type="basic:PartDescriptor" xmi:id="_2xmxoCXXEeex9MwMOgUeWw" elementId="eu.etaxonomy.taxeditor.store.partdescriptor.sessionsView" label="%view.name.SESSIONS" closeable="true" contributionURI="bundleclass://eu.etaxonomy.taxeditor.store/eu.etaxonomy.taxeditor.view.sessions.SessionsViewPart"/>
+    <elements xsi:type="basic:PartDescriptor" xmi:id="_2xmxoCXXEeex9MwMOgUeWw" elementId="eu.etaxonomy.taxeditor.view.sessions.SessionsViewPart" label="%view.name.SESSIONS" closeable="true" contributionURI="bundleclass://eu.etaxonomy.taxeditor.store/eu.etaxonomy.taxeditor.view.sessions.SessionsViewPart"/>
+    <elements xsi:type="basic:PartDescriptor" xmi:id="_J-3UEEqgEeeN-dEYnw7m3g" elementId="eu.etaxonomy.taxeditor.featuretree.e4.FeatureTreeEditor" label="Feature Tree Editor" closeable="true" dirtyable="true" contributionURI="bundleclass://eu.etaxonomy.taxeditor.store/eu.etaxonomy.taxeditor.featuretree.e4.FeatureTreeEditor"/>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_3rW8wCXaEeex9MwMOgUeWw" featurename="children" parentElementId="eu.etaxonomy.taxeditor.menu.showView" positionInList="after:bulkeditor.menus.openmenu">
     <elements xsi:type="menu:HandledMenuItem" xmi:id="_K6sowCaGEeeTEd_pKXBflg" elementId="eu.etaxonomy.taxeditor.store.showViewMenu.details" label="%command.label.0" command="_bnVKsCZxEeeQLpuomSmVoQ">
     </elements>
     <elements xsi:type="menu:HandledMenuItem" xmi:id="_jFskQCZyEeeQLpuomSmVoQ" elementId="eu.etaxonomy.taxeditor.store.showViewMenu.sessions" label="%view.name.SESSIONS" command="_bnVKsCZxEeeQLpuomSmVoQ">
       <visibleWhen xsi:type="ui:CoreExpression" xmi:id="_W-C-gCaJEeeTEd_pKXBflg" coreExpressionId="isCdmStoreConnected"/>
-      <parameters xmi:id="_BylIUCZzEeeQLpuomSmVoQ" elementId="eu.etaxonomy.taxeditor.store.showView.parameter.sessionView" name="eu.etaxonomy.taxeditor.workbench.commandparameter.partName" value="eu.etaxonomy.taxeditor.store.partdescriptor.sessionsView"/>
+      <parameters xmi:id="_BylIUCZzEeeQLpuomSmVoQ" elementId="eu.etaxonomy.taxeditor.store.showView.parameter.sessionView" name="eu.etaxonomy.taxeditor.workbench.commandparameter.partName" value="eu.etaxonomy.taxeditor.view.sessions.SessionsViewPart"/>
+    </elements>
+    <elements xsi:type="menu:HandledMenuItem" xmi:id="_VINAAEqgEeeN-dEYnw7m3g" elementId="eu.etaxonomy.taxeditor.store.showViewMenu.sessions" label="Feature Tree Editor" command="_bnVKsCZxEeeQLpuomSmVoQ">
+      <visibleWhen xsi:type="ui:CoreExpression" xmi:id="_VINAAUqgEeeN-dEYnw7m3g" coreExpressionId="isCdmStoreConnected"/>
+      <parameters xmi:id="_VINAAkqgEeeN-dEYnw7m3g" elementId="eu.etaxonomy.taxeditor.store.showView.parameter.sessionView" name="eu.etaxonomy.taxeditor.workbench.commandparameter.partName" value="eu.etaxonomy.taxeditor.featuretree.e4.FeatureTreeEditor"/>
     </elements>
   </fragments>
 </fragment:ModelFragments>
diff --git a/eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/featuretree/e4/FeatureTreeEditor.java b/eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/featuretree/e4/FeatureTreeEditor.java
new file mode 100644 (file)
index 0000000..d0cc3c4
--- /dev/null
@@ -0,0 +1,384 @@
+/**
+ * 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);
+               }
+
+       }
+}