- rename IDirtyMarkableSelectionProvider to IDirtyMarkable
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / checklist / ChecklistEditor.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.taxeditor.editor.view.checklist;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.SortedSet;
20
21 import org.apache.log4j.Logger;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.core.runtime.jobs.Job;
26 import org.eclipse.jface.viewers.ISelection;
27 import org.eclipse.jface.viewers.StructuredViewer;
28 import org.eclipse.jface.viewers.TableViewer;
29 import org.eclipse.jface.viewers.TableViewerColumn;
30 import org.eclipse.swt.SWT;
31 import org.eclipse.swt.events.ModifyListener;
32 import org.eclipse.swt.events.SelectionAdapter;
33 import org.eclipse.swt.events.SelectionEvent;
34 import org.eclipse.swt.layout.GridData;
35 import org.eclipse.swt.layout.GridLayout;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.Label;
39 import org.eclipse.swt.widgets.Menu;
40 import org.eclipse.swt.widgets.Table;
41 import org.eclipse.swt.widgets.TableColumn;
42 import org.eclipse.swt.widgets.Text;
43 import org.eclipse.swt.widgets.ToolBar;
44 import org.eclipse.swt.widgets.ToolItem;
45 import org.eclipse.ui.IEditorInput;
46 import org.eclipse.ui.IEditorSite;
47 import org.eclipse.ui.IMemento;
48 import org.eclipse.ui.ISelectionListener;
49 import org.eclipse.ui.ISelectionService;
50 import org.eclipse.ui.IWorkbenchPart;
51 import org.eclipse.ui.PartInitException;
52 import org.eclipse.ui.part.EditorPart;
53 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
54
55 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
56 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
57 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
58 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
59 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
60 import eu.etaxonomy.cdm.model.taxon.Classification;
61 import eu.etaxonomy.cdm.model.taxon.Taxon;
62 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
63 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
64 import eu.etaxonomy.taxeditor.editor.EditorUtil;
65 import eu.etaxonomy.taxeditor.editor.SimpleSelectionProvider;
66 import eu.etaxonomy.taxeditor.editor.view.checklist.edit.DistributionEditingSupport;
67 import eu.etaxonomy.taxeditor.editor.view.checklist.edit.RankEditingSupport;
68 import eu.etaxonomy.taxeditor.editor.view.checklist.filter.ChecklistEditorFilter;
69 import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistDropdownSelectionListener;
70 import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistFocusListener;
71 import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistModifyListener;
72 import eu.etaxonomy.taxeditor.model.IContextListener;
73 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
74 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
75 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
76 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
77 import eu.etaxonomy.taxeditor.preference.Resources;
78 import eu.etaxonomy.taxeditor.store.CdmStore;
79
80 /**
81 * <p>
82 * ChecklistViewPart class.
83 * </p>
84 *
85 * @author a.oppermann
86 * @created 30.04.2014
87 * @version 1.0
88 */
89 public class ChecklistEditor extends EditorPart implements ISelectionListener, IPartContentHasFactualData,
90 IConversationEnabled, IContextListener, IPartContentHasDetails, IDirtyMarkable {
91
92 private static final Logger logger = Logger.getLogger(ChecklistEditor.class);
93 /**
94 * Key used for storing Checklist Editor View
95 */
96 private static final String CHECKLIST_EDITOR_MEMENTO_KEY = "checklistEditor";
97
98 private class ChecklistJob extends Job {
99
100 private List<TaxonNode> taxonNodes;
101
102 /**
103 * @param name
104 */
105 public ChecklistJob(String title, List<TaxonNode> listTaxonNodes) {
106 super(title);
107 this.taxonNodes = listTaxonNodes;
108
109 }
110
111 /*
112 * (non-Javadoc)
113 *
114 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.
115 * IProgressMonitor)
116 */
117 @Override
118 public IStatus run(final IProgressMonitor monitor) {
119 try {
120 logger.debug("Begin of eclipse core runtime Job to Retrieve datasources");
121 monitor.beginTask("Retrieving TaxonNodes", taxonNodes.size() + 1);
122
123 final List<Taxon> taxonList = new ArrayList<Taxon>();
124
125 for (TaxonNode taxonNode : taxonNodes) {
126 HibernateProxyHelper hh = new HibernateProxyHelper();
127 Taxon taxon = hh.deproxy(taxonNode.getTaxon(), Taxon.class);
128 taxonList.add(taxon);
129 }
130 // set View input
131 Display.getDefault().asyncExec(new Runnable() {
132
133 @Override
134 public void run() {
135 viewer.setInput(taxonList);
136 }
137 });
138 monitor.worked(1);
139
140 } finally {
141 monitor.done();
142 taxonNodes = null;
143 }
144 return Status.OK_STATUS;
145 }
146 }
147
148 /**
149 * Constant
150 * <code>ID="eu.etaxonomy.taxeditor.store.datasource"{trunked}</code>
151 */
152 public static String ID = "eu.etaxonomy.taxeditor.editor.view.checklist.ChecklistEditor";
153
154 private TableViewer viewer;
155
156 private String partNameCache;
157
158 private IWorkbenchSiteProgressService service;
159
160 private ITaxonNodeService taxonNodeService;
161
162 private ChecklistEditorComparator comparator;
163
164 private ChecklistEditorInput checklistEditorInput;
165
166 private SimpleSelectionProvider simpleSelectionProvider;
167
168 private ISelectionService selectionService;
169
170 private ConversationHolder conversation;
171
172 private Integer countNodes;
173
174 private List<TaxonNode> selectedTaxonNodes;
175
176 private boolean dirty;
177
178 private ChecklistEditorFilter filter;
179
180 private Menu headerMenu;
181
182 private Label statusLabel;
183
184 private SortedSet<DefinedTermBase> terms = null;
185
186 /**
187 * @return the selectedTaxonNodes
188 */
189 public List<TaxonNode> getSelectedTaxonNodes() {
190 return selectedTaxonNodes;
191 }
192
193 /**
194 * @param selectedTaxonNodes
195 * the selectedTaxonNodes to set
196 */
197 public void setSelectedTaxonNodes(List<TaxonNode> selectedTaxonNodes) {
198 this.selectedTaxonNodes = selectedTaxonNodes;
199 }
200
201 /**
202 * <p>
203 * Constructor for CdmDataSourceViewPart.
204 * </p>
205 */
206 public ChecklistEditor() {
207 }
208
209 /** {@inheritDoc} */
210 @Override
211 public void createPartControl(Composite parent) {
212 service = (IWorkbenchSiteProgressService) getSite().getAdapter(IWorkbenchSiteProgressService.class);
213 taxonNodeService = CdmStore.getService(ITaxonNodeService.class);
214 selectionService = getSite().getWorkbenchWindow().getSelectionService();
215 selectionService.addSelectionListener(this);
216 filter = new ChecklistEditorFilter();
217 ChecklistLabelProvider labelProvider = new ChecklistLabelProvider();
218 terms = (SortedSet<DefinedTermBase>) labelProvider.getTermsOrderedByLabels(labelProvider.getNamedAreas(),
219 CdmStore.getDefaultLanguage());
220 createTopComposite(parent);
221 loadDataInput();
222 }
223
224 /**
225 * @param parent
226 */
227 private void createTopComposite(Composite parent) {
228 GridLayout gridLayout = new GridLayout(3, false);
229 gridLayout.marginWidth = 0;
230 gridLayout.marginHeight = 0;
231 parent.setLayout(gridLayout);
232
233 final Text searchText = createSearchBar(parent);
234
235 // if (terms != null && !terms.isEmpty()) {
236 createToolbar(parent);
237 // }
238 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
239 getSite().setSelectionProvider(viewer);
240
241 final ModifyListener modifyListener = new ChecklistModifyListener(viewer, filter, searchText);
242
243 searchText.addFocusListener(new ChecklistFocusListener(searchText, modifyListener));
244 searchText.addModifyListener(modifyListener);
245
246 // Layout the viewer
247 GridData gridData = new GridData();
248 gridData.verticalAlignment = GridData.FILL;
249 gridData.horizontalSpan = 3;
250 gridData.grabExcessHorizontalSpace = true;
251 gridData.grabExcessVerticalSpace = true;
252 gridData.horizontalAlignment = GridData.FILL;
253 viewer.getControl().setLayoutData(gridData);
254
255 viewer.addFilter(filter);
256 createColumns(viewer);
257
258 viewer.setContentProvider(new ChecklistContentProvider());
259 viewer.setLabelProvider(new ChecklistLabelProvider());
260 comparator = new ChecklistEditorComparator();
261 viewer.setComparator(comparator);
262
263 createStatusBar(parent);
264 }
265
266 /**
267 * @param parent
268 * @return
269 */
270 private Text createSearchBar(Composite parent) {
271 Label searchLabel = new Label(parent, SWT.NONE);
272 searchLabel.setText("Search: ");
273 final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.CANCEL);
274 searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
275 searchText.setForeground(EditorUtil.getColor(Resources.SEARCH_VIEW_FOREGROUND));
276 searchText.setText("type filter text");
277 return searchText;
278 }
279
280 /**
281 * @param parent
282 */
283 private void createToolbar(Composite parent) {
284 ToolBar toolBar = new ToolBar(parent, SWT.NONE);
285 ToolItem toolItem = new ToolItem(toolBar, SWT.DROP_DOWN | SWT.BORDER);
286 toolItem.setText("Distribution Status");
287 toolItem.setToolTipText("Show Distribution Status for selected Areas");
288 ChecklistDropdownSelectionListener dropListener = new ChecklistDropdownSelectionListener(toolItem, this, terms);
289 for (DefinedTermBase<DefinedTermBase> term : terms) {
290 if(term!=null){
291 dropListener.add(term);
292 }
293 }
294 toolItem.addSelectionListener(dropListener);
295 toolBar.pack();
296 }
297
298 private void createStatusBar(Composite composite) {
299 GridData gridData = new GridData();
300 gridData.horizontalSpan = 3;
301 gridData.grabExcessHorizontalSpace = true;
302 gridData.horizontalAlignment = GridData.FILL;
303
304 statusLabel = new Label(composite, SWT.LEFT);
305 statusLabel.setText("Anzahl der Elemente: " + (countNodes != null ? countNodes : "uknown"));
306 statusLabel.setLayoutData(gridData);
307 }
308
309 // This will create the columns for the table
310 private void createColumns(TableViewer viewer) {
311 Table table = viewer.getTable();
312 List<String> titles = new ArrayList<String>();
313 Collections.addAll(titles, "Taxon", "Author", "Reference", "Rank");
314 List<Integer> bounds = new ArrayList<Integer>();
315 Collections.addAll(bounds, 300, 200, 200, 200);
316 Map<Integer, Boolean>restoreColumnWidth = new HashMap<Integer, Boolean>();
317 if (terms != null) {
318 int i = 4;
319 for (DefinedTermBase<DefinedTermBase> term : terms) {
320 if(term != null){
321 restoreColumnWidth.put(i, PreferencesUtil.getPreferenceStore().getBoolean(term.getUuid().toString()));
322 titles.add(term.getTitleCache());
323 bounds.add(200);
324 i++;
325 }
326 }
327 }
328
329 for (int i = 0; i < titles.size(); i++) {
330 TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
331 column.getColumn().setText(titles.get(i));
332 column.getColumn().setWidth(bounds.get(i));
333 column.getColumn().setResizable(true);
334 column.getColumn().setMoveable(true);
335 column.getColumn().addSelectionListener(getSelectionAdapter(column.getColumn(), i));
336 if (i == 1) {
337 table.setSortColumn(column.getColumn());
338 }
339 if (i == 3) {
340 column.setEditingSupport(new RankEditingSupport(viewer, this));
341 }
342 if (i >= 4) {
343 //read PrefrenceStore to setWidth according to last saved state
344 if(restoreColumnWidth.get(i)){
345 column.getColumn().setWidth(100);
346 }else{
347 column.getColumn().setWidth(0);
348 }
349 column.setEditingSupport(new DistributionEditingSupport(viewer, this, i));
350 }
351 }
352 table.setSortDirection(SWT.DOWN);
353 table.setHeaderVisible(true);
354 table.setLinesVisible(true);
355 }
356
357 /**
358 *
359 * pull data from database and set input for view
360 *
361 */
362 private void loadDataInput() {
363 Classification classification = checklistEditorInput.getClassification();
364 TaxonNode taxonNode = checklistEditorInput.getTaxonNode();
365 if (classification != null && taxonNode == null) {
366 countNodes = taxonNodeService.countAllNodesForClassification(classification);
367 statusLabel.setText("Anzahl der Elemente: " + (countNodes != null ? countNodes : "uknown"));
368 // This should not kill the view nor the editor if something goes
369 // wrong
370 // TODO: don't load the whole taxonNode Object but rather a small
371 // and simple Solution
372 // FIXME: May be don't open classification which are greater than
373 // 3000 Taxa
374 selectedTaxonNodes = taxonNodeService.listAllNodesForClassification(classification, 0, countNodes);
375 getService().schedule(new ChecklistJob("loading Taxa", selectedTaxonNodes), Job.LONG);
376 }
377 if (taxonNode != null) {
378 selectedTaxonNodes = taxonNodeService.loadChildNodesOfTaxonNode(taxonNode, NODE_INIT_STRATEGY, true, false);
379 getService().schedule(new ChecklistJob("loading Taxa", selectedTaxonNodes), Job.LONG);
380 }
381 }
382
383 /** {@inheritDoc} */
384 @Override
385 public void dispose() {
386 super.dispose();
387 }
388
389 /** {@inheritDoc} */
390 @Override
391 public void setFocus() {
392 viewer.getControl().setFocus();
393 }
394
395 /*
396 * (non-Javadoc)
397 *
398 * @see org.eclipse.ui.part.WorkbenchPart#showBusy(boolean)
399 */
400 /** {@inheritDoc} */
401 @Override
402 public void showBusy(boolean busy) {
403 super.showBusy(busy);
404 // viewer.getTable().setEnabled(!busy);
405 if (busy) {
406 partNameCache = getPartName();
407 setPartName("Loading " + countNodes + " Taxa...");
408 } else {
409 if (partNameCache != null) {
410 setPartName(partNameCache);
411 }
412 }
413 }
414
415 /**
416 * <p>
417 * Getter for the field <code>viewer</code>.
418 * </p>
419 *
420 * @return a {@link org.eclipse.jface.viewers.StructuredViewer} object.
421 */
422 public StructuredViewer getViewer() {
423 return viewer;
424 }
425
426 public void refresh() {
427 viewer.refresh();
428 }
429
430 /**
431 * <p>
432 * Getter for the field <code>service</code>.
433 * </p>
434 *
435 * @return the service
436 */
437 public IWorkbenchSiteProgressService getService() {
438 return service;
439 }
440
441 private SelectionAdapter getSelectionAdapter(final TableColumn column, final int index) {
442 SelectionAdapter selectionAdapter = new SelectionAdapter() {
443 @Override
444 public void widgetSelected(SelectionEvent e) {
445 comparator.setColumn(index);
446 int dir = viewer.getTable().getSortDirection();
447 if (viewer.getTable().getSortColumn() == column) {
448 dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
449 } else {
450 dir = SWT.DOWN;
451 }
452 viewer.getTable().setSortDirection(dir);
453 viewer.getTable().setSortColumn(column);
454 viewer.refresh();
455 }
456 };
457 return selectionAdapter;
458 }
459
460 @Override
461 public void doSave(IProgressMonitor monitor) {
462 try {
463 monitor.beginTask("Saving Editor", 1);
464 getConversationHolder().bind();
465 getConversationHolder().commit(true);
466 setDirty(false);
467 monitor.worked(1);
468 } finally {
469 monitor.done();
470 }
471
472 }
473
474 @Override
475 public void doSaveAs() {
476 // TODO Auto-generated method stub
477
478 }
479
480 @Override
481 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
482 setSite(site);
483 setInput(input);
484 if (input instanceof ChecklistEditorInput) {
485 checklistEditorInput = (ChecklistEditorInput) input;
486 setPartName(getPartName() + ": " + checklistEditorInput.getName());
487 conversation = ((ChecklistEditorInput) input).getConversationHolder();
488 }
489 simpleSelectionProvider = new SimpleSelectionProvider();
490 getSite().setSelectionProvider(simpleSelectionProvider);
491 }
492
493 @Override
494 public boolean isSaveAsAllowed() {
495 // TODO Auto-generated method stub
496 return false;
497 }
498
499 /*
500 * (non-Javadoc)
501 *
502 * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.
503 * IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
504 */
505 @Override
506 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
507 // TODO Auto-generated method stub
508
509 }
510
511 /*
512 * (non-Javadoc)
513 *
514 * @see
515 * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostDataChangeObserver#update
516 * (eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap)
517 */
518 @Override
519 public void update(CdmDataChangeMap changeEvents) {
520
521 }
522
523 /*
524 * (non-Javadoc)
525 *
526 * @see
527 * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
528 * ()
529 */
530 @Override
531 public ConversationHolder getConversationHolder() {
532 return conversation;
533 }
534
535 /*
536 * (non-Javadoc)
537 *
538 * @see
539 * eu.etaxonomy.taxeditor.model.IContextListener#contextAboutToStop(org.
540 * eclipse.ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
541 */
542
543 @Override
544 public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
545
546 }
547
548 /*
549 * (non-Javadoc)
550 *
551 * @see
552 * eu.etaxonomy.taxeditor.model.IContextListener#contextStop(org.eclipse
553 * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
554 */
555 @Override
556 public void contextStop(IMemento memento, IProgressMonitor monitor) {
557 // TODO Auto-generated method stub
558 // IStructuredSelection sel = (IStructuredSelection) this.viewer.getSelection();
559 // if (sel.isEmpty()) {
560 // return;
561 // }
562 // memento = memento.createChild("tree-selections");
563 // Iterator iter = sel.iterator();
564 // while (iter.hasNext()) {
565 // String nodeName = (String) iter.next();
566 // memento.createChild("selected-nodes", nodeName);
567 // }
568 }
569
570 /*
571 * (non-Javadoc)
572 *
573 * @see
574 * eu.etaxonomy.taxeditor.model.IContextListener#contextStart(org.eclipse
575 * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
576 */
577 @Override
578 public void contextStart(IMemento memento, IProgressMonitor monitor) {
579
580 }
581
582 /*
583 * (non-Javadoc)
584 *
585 * @see
586 * eu.etaxonomy.taxeditor.model.IContextListener#contextRefresh(org.eclipse
587 * .core.runtime.IProgressMonitor)
588 */
589 @Override
590 public void contextRefresh(IProgressMonitor monitor) {
591 // TODO Auto-generated method stub
592
593 }
594
595 /*
596 * (non-Javadoc)
597 *
598 * @see
599 * eu.etaxonomy.taxeditor.model.IContextListener#workbenchShutdown(org.eclipse
600 * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
601 */
602 @Override
603 public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
604 conversation.clear();
605 conversation.close();
606 conversation = null;
607 }
608
609 private static final List<String> NODE_INIT_STRATEGY = Arrays.asList(new String[] { "descriptions",
610 "descriptions.*", "description.state", "feature", "feature.*", "childNodes", "childNodes.taxon",
611 "childNodes.taxon.name", "taxonNodes", "taxonNodes.*", "taxonNodes.taxon.*", "taxon.*",
612 "taxon.descriptions", "taxon.sec", "taxon.name.*", "taxon.synonymRelations", "terms", "name.*",
613 "name.rank.representations", "name.status.type.representations", "sources.$", "stateData.$" });
614
615 /*
616 * (non-Javadoc)
617 *
618 * @see
619 * eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider#changed(
620 * java.lang.Object)
621 */
622 @Override
623 public void changed(Object element) {
624 if (element != null) {
625 viewer.update(element, null);
626 setDirty(true);
627 firePropertyChange(PROP_DIRTY);
628 }
629 }
630
631 /* (non-Javadoc)
632 * @see eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider#forceDirty()
633 */
634 @Override
635 public void forceDirty() {
636 changed(null);
637 }
638
639 public void setDirty(boolean dirty) {
640 this.dirty = dirty;
641 firePropertyChange(PROP_DIRTY);
642 }
643
644 /*
645 * (non-Javadoc)
646 *
647 * @see org.eclipse.ui.forms.editor.FormEditor#isDirty()
648 */
649 @Override
650 public boolean isDirty() {
651 return dirty;
652 }
653 }