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