4017e0aa3259a6dd54a669759fe698dd5cb4724c
[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());
107 Composite composite = new Composite(parent, SWT.NULL);
108 composite.setLayout(new GridLayout());
109 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
110
111 Composite composite_treeTitle = new Composite(composite, SWT.NULL);
112 composite_treeTitle.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true,
113 false));
114 composite_treeTitle.setLayout(new GridLayout(3, false));
115
116 label_title = new Label(composite_treeTitle, SWT.NULL);
117 label_title.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
118 label_title.setText("Feature Tree");
119
120 text_title = new Text(composite_treeTitle, SWT.BORDER);
121 text_title.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
122
123 btnOpenFeatureTree = new Button(composite_treeTitle, SWT.NONE);
124 btnOpenFeatureTree.setToolTipText("Open Tree");
125 btnOpenFeatureTree.setImage(ImageResources.getImage(ImageResources.BROWSE_ICON));
126
127 Composite composite_treeContent = new Composite(composite, SWT.NULL);
128 composite_treeContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL,
129 true, true));
130 composite_treeContent.setLayout(new GridLayout(2, false));
131
132 viewer = new TreeViewer(composite_treeContent);
133 Tree tree = viewer.getTree();
134 tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
135 viewer.getControl().setLayoutData(
136 new GridData(SWT.FILL, SWT.FILL, true, true));
137
138 Composite composite_buttons = new Composite(composite_treeContent,
139 SWT.NULL);
140 composite_buttons.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false,
141 false));
142 composite_buttons.setLayout(new GridLayout());
143
144 button_add = new Button(composite_buttons, SWT.PUSH);
145 button_add.setToolTipText("Add a feature to this feature tree.");
146 button_add.setImage(ImageResources.getImage(ImageResources.ADD_EDIT));
147 button_remove = new Button(composite_buttons, SWT.PUSH);
148 button_remove.setToolTipText("Remove a feature from this feature tree.");
149 button_remove.setImage(ImageResources.getImage(ImageResources.TRASH_ICON));
150
151 init(shell);
152 }
153
154 private void setDirty(boolean isDirty){
155 this.dirty.setDirty(isDirty);
156 }
157
158 private void init(Shell shell) {
159 viewer.setContentProvider(new FeatureTreeContentProvider());
160 viewer.setLabelProvider(new FeatureTreeLabelProvider());
161
162 int ops = DND.DROP_COPY | DND.DROP_MOVE;
163 Transfer[] transfers = new Transfer[] { FeatureNodeTransfer
164 .getInstance() };
165 viewer.addDragSupport(ops, transfers, new FeatureNodeDragListener(
166 viewer));
167 viewer.addDropSupport(ops, transfers,
168 new FeatureNodeDropAdapter(viewer));
169
170 viewer.addSelectionChangedListener(this);
171
172 button_add.addSelectionListener(new AddButtonListener());
173 button_remove.addSelectionListener(new RemoveSelectionListener());
174
175 btnOpenFeatureTree.addSelectionListener(new SelectionListener() {
176
177 @Override
178 public void widgetSelected(SelectionEvent e) {
179 FeatureTree tree = FeatureTreeSelectionDialog.select(shell, conversation, null);
180 if(tree!=null){
181 setSelectedTree(tree);
182 }
183 }
184
185 @Override
186 public void widgetDefaultSelected(SelectionEvent e) {
187 }
188 });
189 }
190
191 public void setSelectedTree(FeatureTree featureTree) {
192 this.featureTree = HibernateProxyHelper.deproxy(featureTree, FeatureTree.class);
193 this.featureTree.setRoot(HibernateProxyHelper.deproxy(featureTree.getRoot(), FeatureNode.class));
194 viewer.setInput(featureTree);
195
196 text_title.removeModifyListener(this);
197 text_title.setText(featureTree.getTitleCache());
198 text_title.addModifyListener(this);
199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public void modifyText(ModifyEvent e) {
204 featureTree.setTitleCache(text_title.getText(), true);
205 setDirty(true);
206 }
207
208 /** {@inheritDoc} */
209 @Override
210 public void selectionChanged(SelectionChangedEvent event) {
211 IStructuredSelection selection = (IStructuredSelection) event
212 .getSelection();
213
214 button_add.setEnabled(selection.size() <= 1);
215 button_remove.setEnabled(selection.size() > 0);
216 //propagate selection
217 IStructuredSelection isel = (IStructuredSelection) event.getSelection();
218 selService.setSelection((isel.size() == 1 ? isel.getFirstElement() : isel.toArray()));
219 }
220
221 @Focus
222 public void focus(){
223 viewer.getControl().setFocus();
224 if(conversation!=null && !conversation.isBound()){
225 conversation.bind();
226 }
227 }
228
229 @Persist
230 public void save(){
231 CdmStore.getService(IFeatureTreeService.class).saveOrUpdate(featureTree);
232 setDirty(false);
233 }
234
235 private class AddButtonListener extends SelectionAdapter {
236 @Override
237 public void widgetSelected(SelectionEvent e) {
238 AvailableFeaturesWizard wizard = new AvailableFeaturesWizard(
239 featureTree);
240 WizardDialog dialog = new WizardDialog(shell, wizard);
241
242 if (dialog.open() == IStatus.OK) {
243 IStructuredSelection selection = (IStructuredSelection) viewer
244 .getSelection();
245 FeatureNode parent = (FeatureNode) (selection.getFirstElement() != null ? selection
246 .getFirstElement() : ((FeatureTree) viewer.getInput())
247 .getRoot());
248 Collection<Feature> additionalFeatures = wizard
249 .getAdditionalFeatures();
250 for (Feature feature : additionalFeatures) {
251 FeatureNode child = FeatureNode.NewInstance(feature);
252 CdmStore.getService(IFeatureNodeService.class).merge(child, true);
253
254 parent.addChild(child);
255 }
256 setDirty(true);
257 viewer.refresh();
258 }
259 }
260
261 }
262
263 private class RemoveSelectionListener extends SelectionAdapter {
264 @Override
265 public void widgetSelected(SelectionEvent e) {
266 IStructuredSelection selection = (IStructuredSelection) viewer
267 .getSelection();
268
269 for (Object selectedObject : selection.toArray()) {
270 FeatureNode featureNode = (FeatureNode) selectedObject;
271 FeatureNode parent = featureNode.getParent();
272 parent.removeChild(featureNode);
273
274 CdmStore.getService(IFeatureNodeService.class).deleteFeatureNode(featureNode.getUuid(), new FeatureNodeDeletionConfigurator());
275
276 }
277 setDirty(true);
278 viewer.refresh();
279 }
280 }
281
282 private class FeatureNodeDragListener extends DragSourceAdapter {
283
284 private final TreeViewer viewer;
285
286 public FeatureNodeDragListener(TreeViewer viewer) {
287 this.viewer = viewer;
288 }
289
290 /**
291 * Method declared on DragSourceListener
292 */
293 @Override
294 public void dragFinished(DragSourceEvent event) {
295 if (!event.doit) {
296 return;
297 }
298 // if the featureNode was moved, remove it from the source viewer
299 if (event.detail == DND.DROP_MOVE) {
300 IStructuredSelection selection = (IStructuredSelection) viewer
301 .getSelection();
302 viewer.remove(selection);
303 viewer.refresh();
304 }
305 }
306
307 /**
308 * Method declared on DragSourceListener
309 */
310 @Override
311 public void dragSetData(DragSourceEvent event) {
312 IStructuredSelection selection = (IStructuredSelection) viewer
313 .getSelection();
314 FeatureNode[] featureNodes = (FeatureNode[]) selection.toList()
315 .toArray(new FeatureNode[selection.size()]);
316 if (FeatureNodeTransfer.getInstance().isSupportedType(
317 event.dataType)) {
318 event.data = featureNodes;
319 }
320 }
321
322 /**
323 * Method declared on DragSourceListener
324 */
325 @Override
326 public void dragStart(DragSourceEvent event) {
327 event.doit = !viewer.getSelection().isEmpty();
328 }
329
330 }
331
332 private class FeatureNodeDropAdapter extends ViewerDropAdapter {
333
334 protected FeatureNodeDropAdapter(Viewer viewer) {
335 super(viewer);
336 }
337
338 @Override
339 public boolean performDrop(Object data) {
340 FeatureNode target = (FeatureNode) getCurrentTarget();
341 int position = 0;
342
343 if (target != null) {
344 int location = getCurrentLocation();
345 FeatureNode parent = target.getParent();
346 if (location == LOCATION_BEFORE) {
347 position = Math.max(0, parent.getIndex(target) - 1);
348 target = parent;
349 }
350
351 if (location == LOCATION_AFTER) {
352 position = parent.getIndex(target);
353 target = parent;
354 }
355 }
356
357 // set target to root node if there is no target specified
358 if (target == null) {
359 FeatureTree featureTree = (FeatureTree) getViewer().getInput();
360 target = featureTree.getRoot();
361 }
362
363 Object[] droppedObjects = (Object[]) data;
364 TreeViewer viewer = (TreeViewer) getViewer();
365
366 // cannot drop a feature node onto itself
367 for (Object droppedObject : droppedObjects) {
368 if (droppedObject == null) {
369 MessagingUtils.warningDialog(
370 "Operation not supported yet",
371 this,
372 "We are currently unable to change the order of freshly created "
373 + "feature trees nodes. Please close and reopen the dialog to change the order of features.");
374 return false;
375 }
376 if (droppedObject.equals(target)) {
377 return false;
378 }
379 }
380 for (Object droppedObject : droppedObjects) {
381 FeatureNode droppedNode = (FeatureNode) droppedObject;
382 target.addChild(droppedNode, position);
383 viewer.add(target, droppedNode);
384 viewer.reveal(droppedNode);
385 }
386 setDirty(true);
387 return true;
388 }
389
390 @Override
391 public boolean validateDrop(Object target, int operation,
392 TransferData transferData) {
393 return FeatureNodeTransfer.getInstance().isSupportedType(
394 transferData);
395 }
396
397 }
398 }