af9ef8dadfc7746ac77300179ea32c9e8b906fe1
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / featuretree / e4 / FeatureTreeEditor.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.taxeditor.featuretree.e4;
11
12 import java.util.Collection;
13
14 import javax.annotation.PostConstruct;
15 import javax.inject.Inject;
16 import javax.inject.Named;
17
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.e4.ui.di.Focus;
20 import org.eclipse.e4.ui.di.Persist;
21 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
22 import org.eclipse.e4.ui.services.IServiceConstants;
23 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
24 import org.eclipse.jface.viewers.ISelectionChangedListener;
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.jface.viewers.SelectionChangedEvent;
27 import org.eclipse.jface.viewers.TreeViewer;
28 import org.eclipse.jface.viewers.Viewer;
29 import org.eclipse.jface.viewers.ViewerDropAdapter;
30 import org.eclipse.jface.wizard.WizardDialog;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.dnd.DND;
33 import org.eclipse.swt.dnd.DragSourceAdapter;
34 import org.eclipse.swt.dnd.DragSourceEvent;
35 import org.eclipse.swt.dnd.Transfer;
36 import org.eclipse.swt.dnd.TransferData;
37 import org.eclipse.swt.events.ModifyEvent;
38 import org.eclipse.swt.events.ModifyListener;
39 import org.eclipse.swt.events.SelectionAdapter;
40 import org.eclipse.swt.events.SelectionEvent;
41 import org.eclipse.swt.events.SelectionListener;
42 import org.eclipse.swt.layout.GridData;
43 import org.eclipse.swt.layout.GridLayout;
44 import org.eclipse.swt.widgets.Button;
45 import org.eclipse.swt.widgets.Composite;
46 import org.eclipse.swt.widgets.Label;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.Text;
49 import org.eclipse.swt.widgets.Tree;
50
51 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
52 import eu.etaxonomy.cdm.api.service.IFeatureNodeService;
53 import eu.etaxonomy.cdm.api.service.IFeatureTreeService;
54 import eu.etaxonomy.cdm.api.service.config.FeatureNodeDeletionConfigurator;
55 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
56 import eu.etaxonomy.cdm.model.description.Feature;
57 import eu.etaxonomy.cdm.model.description.FeatureNode;
58 import eu.etaxonomy.cdm.model.description.FeatureTree;
59 import eu.etaxonomy.taxeditor.featuretree.AvailableFeaturesWizard;
60 import eu.etaxonomy.taxeditor.featuretree.FeatureNodeTransfer;
61 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeContentProvider;
62 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeLabelProvider;
63 import eu.etaxonomy.taxeditor.model.ImageResources;
64 import eu.etaxonomy.taxeditor.model.MessagingUtils;
65 import eu.etaxonomy.taxeditor.store.CdmStore;
66 import eu.etaxonomy.taxeditor.ui.dialog.selection.FeatureTreeSelectionDialog;
67
68 /**
69 *
70 * @author pplitzner
71 * @date 06.06.2017
72 *
73 */
74 public class FeatureTreeEditor implements
75 ModifyListener, ISelectionChangedListener {
76
77 private TreeViewer viewer;
78 private Label label_title;
79 private Button button_add;
80 private Button button_remove;
81 private FeatureTree featureTree;
82 private Text text_title;
83 private Button btnOpenFeatureTree;
84
85 private ConversationHolder conversation;
86
87 @Inject
88 private ESelectionService selService;
89
90 @Inject
91 private MDirtyable dirty;
92
93 private Shell shell;
94
95 @Inject
96 public FeatureTreeEditor(@Named(IServiceConstants.ACTIVE_SHELL)Shell shell) {
97 this.shell = shell;
98 if(conversation==null){
99 conversation = CdmStore.createConversation();
100 }
101 }
102
103 /** {@inheritDoc} */
104 @PostConstruct
105 public void createControl(Composite parent, @Named(IServiceConstants.ACTIVE_SHELL)Shell shell) {
106 parent.setLayout(new GridLayout(2, false));
107
108 Composite composite_treeTitle = new Composite(parent, SWT.NULL);
109 composite_treeTitle.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true,
110 false));
111 GridLayout gl_composite_treeTitle = new GridLayout(2, false);
112 gl_composite_treeTitle.marginWidth = 0;
113 composite_treeTitle.setLayout(gl_composite_treeTitle);
114
115 label_title = new Label(composite_treeTitle, SWT.NULL);
116 label_title.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
117 label_title.setText("Feature Tree");
118
119 text_title = new Text(composite_treeTitle, SWT.BORDER);
120 text_title.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
121
122 btnOpenFeatureTree = new Button(parent, SWT.NONE);
123 btnOpenFeatureTree.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
124 btnOpenFeatureTree.setToolTipText("Open Tree");
125 btnOpenFeatureTree.setImage(ImageResources.getImage(ImageResources.BROWSE_ICON));
126
127 viewer = new TreeViewer(parent);
128 Tree tree = viewer.getTree();
129 tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
130 viewer.getControl().setLayoutData(
131 new GridData(SWT.FILL, SWT.FILL, true, true));
132
133 Composite composite_buttons = new Composite(parent,
134 SWT.NULL);
135 composite_buttons.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false,
136 false));
137 composite_buttons.setLayout(new GridLayout());
138
139 button_add = new Button(composite_buttons, SWT.PUSH);
140 button_add.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
141 button_add.setToolTipText("Add a feature to this feature tree.");
142 button_add.setImage(ImageResources.getImage(ImageResources.ADD_EDIT));
143 button_remove = new Button(composite_buttons, SWT.PUSH);
144 button_remove.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
145 button_remove.setToolTipText("Remove a feature from this feature tree.");
146 button_remove.setImage(ImageResources.getImage(ImageResources.TRASH_ICON));
147
148 init(shell);
149 }
150
151 private void setDirty(boolean isDirty){
152 this.dirty.setDirty(isDirty);
153 }
154
155 private void init(Shell shell) {
156 viewer.setContentProvider(new FeatureTreeContentProvider());
157 viewer.setLabelProvider(new FeatureTreeLabelProvider());
158
159 int ops = DND.DROP_COPY | DND.DROP_MOVE;
160 Transfer[] transfers = new Transfer[] { FeatureNodeTransfer
161 .getInstance() };
162 viewer.addDragSupport(ops, transfers, new FeatureNodeDragListener(
163 viewer));
164 viewer.addDropSupport(ops, transfers,
165 new FeatureNodeDropAdapter(viewer));
166
167 viewer.addSelectionChangedListener(this);
168
169 button_add.addSelectionListener(new AddButtonListener());
170 button_remove.addSelectionListener(new RemoveSelectionListener());
171
172 btnOpenFeatureTree.addSelectionListener(new SelectionListener() {
173
174 @Override
175 public void widgetSelected(SelectionEvent e) {
176 FeatureTree tree = FeatureTreeSelectionDialog.select(shell, conversation, null);
177 if(tree!=null){
178 setSelectedTree(tree);
179 }
180 }
181
182 @Override
183 public void widgetDefaultSelected(SelectionEvent e) {
184 }
185 });
186 }
187
188 public void setSelectedTree(FeatureTree featureTree) {
189 this.featureTree = HibernateProxyHelper.deproxy(featureTree, FeatureTree.class);
190 this.featureTree.setRoot(HibernateProxyHelper.deproxy(featureTree.getRoot(), FeatureNode.class));
191 viewer.setInput(featureTree);
192
193 text_title.removeModifyListener(this);
194 text_title.setText(featureTree.getTitleCache());
195 text_title.addModifyListener(this);
196 }
197
198 /** {@inheritDoc} */
199 @Override
200 public void modifyText(ModifyEvent e) {
201 featureTree.setTitleCache(text_title.getText(), true);
202 setDirty(true);
203 }
204
205 /** {@inheritDoc} */
206 @Override
207 public void selectionChanged(SelectionChangedEvent event) {
208 IStructuredSelection selection = (IStructuredSelection) event
209 .getSelection();
210
211 button_add.setEnabled(selection.size() <= 1);
212 button_remove.setEnabled(selection.size() > 0);
213 //propagate selection
214 IStructuredSelection isel = (IStructuredSelection) event.getSelection();
215 selService.setSelection((isel.size() == 1 ? isel.getFirstElement() : isel.toArray()));
216 }
217
218 @Focus
219 public void focus(){
220 viewer.getControl().setFocus();
221 if(conversation!=null && !conversation.isBound()){
222 conversation.bind();
223 }
224 }
225
226 @Persist
227 public void save(){
228 CdmStore.getService(IFeatureTreeService.class).saveOrUpdate(featureTree);
229 setDirty(false);
230 }
231
232 private class AddButtonListener extends SelectionAdapter {
233 @Override
234 public void widgetSelected(SelectionEvent e) {
235 AvailableFeaturesWizard wizard = new AvailableFeaturesWizard(
236 featureTree);
237 WizardDialog dialog = new WizardDialog(shell, wizard);
238
239 if (dialog.open() == IStatus.OK) {
240 IStructuredSelection selection = (IStructuredSelection) viewer
241 .getSelection();
242 FeatureNode parent = (FeatureNode) (selection.getFirstElement() != null ? selection
243 .getFirstElement() : ((FeatureTree) viewer.getInput())
244 .getRoot());
245 Collection<Feature> additionalFeatures = wizard
246 .getAdditionalFeatures();
247 for (Feature feature : additionalFeatures) {
248 FeatureNode child = FeatureNode.NewInstance(feature);
249 CdmStore.getService(IFeatureNodeService.class).merge(child, true);
250
251 parent.addChild(child);
252 }
253 setDirty(true);
254 viewer.refresh();
255 }
256 }
257
258 }
259
260 private class RemoveSelectionListener extends SelectionAdapter {
261 @Override
262 public void widgetSelected(SelectionEvent e) {
263 IStructuredSelection selection = (IStructuredSelection) viewer
264 .getSelection();
265
266 for (Object selectedObject : selection.toArray()) {
267 FeatureNode featureNode = (FeatureNode) selectedObject;
268 FeatureNode parent = featureNode.getParent();
269 parent.removeChild(featureNode);
270
271 CdmStore.getService(IFeatureNodeService.class).deleteFeatureNode(featureNode.getUuid(), new FeatureNodeDeletionConfigurator());
272
273 }
274 setDirty(true);
275 viewer.refresh();
276 }
277 }
278
279 private class FeatureNodeDragListener extends DragSourceAdapter {
280
281 private final TreeViewer viewer;
282
283 public FeatureNodeDragListener(TreeViewer viewer) {
284 this.viewer = viewer;
285 }
286
287 /**
288 * Method declared on DragSourceListener
289 */
290 @Override
291 public void dragFinished(DragSourceEvent event) {
292 if (!event.doit) {
293 return;
294 }
295 // if the featureNode was moved, remove it from the source viewer
296 if (event.detail == DND.DROP_MOVE) {
297 IStructuredSelection selection = (IStructuredSelection) viewer
298 .getSelection();
299 viewer.remove(selection);
300 viewer.refresh();
301 }
302 }
303
304 /**
305 * Method declared on DragSourceListener
306 */
307 @Override
308 public void dragSetData(DragSourceEvent event) {
309 IStructuredSelection selection = (IStructuredSelection) viewer
310 .getSelection();
311 FeatureNode[] featureNodes = (FeatureNode[]) selection.toList()
312 .toArray(new FeatureNode[selection.size()]);
313 if (FeatureNodeTransfer.getInstance().isSupportedType(
314 event.dataType)) {
315 event.data = featureNodes;
316 }
317 }
318
319 /**
320 * Method declared on DragSourceListener
321 */
322 @Override
323 public void dragStart(DragSourceEvent event) {
324 event.doit = !viewer.getSelection().isEmpty();
325 }
326
327 }
328
329 private class FeatureNodeDropAdapter extends ViewerDropAdapter {
330
331 protected FeatureNodeDropAdapter(Viewer viewer) {
332 super(viewer);
333 }
334
335 @Override
336 public boolean performDrop(Object data) {
337 FeatureNode target = (FeatureNode) getCurrentTarget();
338 int position = 0;
339
340 if (target != null) {
341 int location = getCurrentLocation();
342 FeatureNode parent = target.getParent();
343 if (location == LOCATION_BEFORE) {
344 position = Math.max(0, parent.getIndex(target) - 1);
345 target = parent;
346 }
347
348 if (location == LOCATION_AFTER) {
349 position = parent.getIndex(target);
350 target = parent;
351 }
352 }
353
354 // set target to root node if there is no target specified
355 if (target == null) {
356 FeatureTree featureTree = (FeatureTree) getViewer().getInput();
357 target = featureTree.getRoot();
358 }
359
360 Object[] droppedObjects = (Object[]) data;
361 TreeViewer viewer = (TreeViewer) getViewer();
362
363 // cannot drop a feature node onto itself
364 for (Object droppedObject : droppedObjects) {
365 if (droppedObject == null) {
366 MessagingUtils.warningDialog(
367 "Operation not supported yet",
368 this,
369 "We are currently unable to change the order of freshly created "
370 + "feature trees nodes. Please close and reopen the dialog to change the order of features.");
371 return false;
372 }
373 if (droppedObject.equals(target)) {
374 return false;
375 }
376 }
377 for (Object droppedObject : droppedObjects) {
378 FeatureNode droppedNode = (FeatureNode) droppedObject;
379 target.addChild(droppedNode, position);
380 viewer.add(target, droppedNode);
381 viewer.reveal(droppedNode);
382 }
383 setDirty(true);
384 return true;
385 }
386
387 @Override
388 public boolean validateDrop(Object target, int operation,
389 TransferData transferData) {
390 return FeatureNodeTransfer.getInstance().isSupportedType(
391 transferData);
392 }
393
394 }
395 }