+// $Id$
+/**
+ * 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.editor.view.checklist;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+
+import org.apache.log4j.Logger;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.ISelectionService;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.EditorPart;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+
+import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
+import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
+import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;
+import eu.etaxonomy.cdm.model.taxon.Classification;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.taxon.TaxonNode;
+import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
+import eu.etaxonomy.taxeditor.editor.EditorUtil;
+import eu.etaxonomy.taxeditor.editor.SimpleSelectionProvider;
+import eu.etaxonomy.taxeditor.editor.view.checklist.edit.DistributionEditingSupport;
+import eu.etaxonomy.taxeditor.editor.view.checklist.edit.RankEditingSupport;
+import eu.etaxonomy.taxeditor.editor.view.checklist.filter.ChecklistEditorFilter;
+import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistDropdownSelectionListener;
+import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistFocusListener;
+import eu.etaxonomy.taxeditor.editor.view.checklist.listener.ChecklistModifyListener;
+import eu.etaxonomy.taxeditor.model.IContextListener;
+import eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider;
+import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
+import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
+import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
+import eu.etaxonomy.taxeditor.preference.Resources;
+import eu.etaxonomy.taxeditor.store.CdmStore;
+
+/**
+ * <p>
+ * ChecklistViewPart class.
+ * </p>
+ *
+ * @author a.oppermann
+ * @created 30.04.2014
+ * @version 1.0
+ */
+public class ChecklistEditor extends EditorPart implements ISelectionListener, IPartContentHasFactualData,
+ IConversationEnabled, IContextListener, IPartContentHasDetails, IDirtyMarkableSelectionProvider {
+
+ private static final Logger logger = Logger.getLogger(ChecklistEditor.class);
+ /**
+ * Key used for storing Checklist Editor View
+ */
+ private static final String CHECKLIST_EDITOR_MEMENTO_KEY = "checklistEditor";
+
+ private class ChecklistJob extends Job {
+
+ private List<TaxonNode> taxonNodes;
+
+ /**
+ * @param name
+ */
+ public ChecklistJob(String title, List<TaxonNode> listTaxonNodes) {
+ super(title);
+ this.taxonNodes = listTaxonNodes;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.
+ * IProgressMonitor)
+ */
+ @Override
+ public IStatus run(final IProgressMonitor monitor) {
+ try {
+ logger.debug("Begin of eclipse core runtime Job to Retrieve datasources");
+ monitor.beginTask("Retrieving TaxonNodes", taxonNodes.size() + 1);
+
+ final List<Taxon> taxonList = new ArrayList<Taxon>();
+
+ for (TaxonNode taxonNode : taxonNodes) {
+ HibernateProxyHelper hh = new HibernateProxyHelper();
+ Taxon taxon = hh.deproxy(taxonNode.getTaxon(), Taxon.class);
+ taxonList.add(taxon);
+ }
+ // set View input
+ Display.getDefault().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ viewer.setInput(taxonList);
+ }
+ });
+ monitor.worked(1);
+
+ } finally {
+ monitor.done();
+ taxonNodes = null;
+ }
+ return Status.OK_STATUS;
+ }
+ }
+
+ /**
+ * Constant
+ * <code>ID="eu.etaxonomy.taxeditor.store.datasource"{trunked}</code>
+ */
+ public static String ID = "eu.etaxonomy.taxeditor.editor.view.checklist.ChecklistEditor";
+
+ private TableViewer viewer;
+
+ private String partNameCache;
+
+ private IWorkbenchSiteProgressService service;
+
+ private ITaxonNodeService taxonNodeService;
+
+ private ChecklistEditorComparator comparator;
+
+ private ChecklistEditorInput checklistEditorInput;
+
+ private SimpleSelectionProvider simpleSelectionProvider;
+
+ private ISelectionService selectionService;
+
+ private ConversationHolder conversation;
+
+ private Integer countNodes;
+
+ private List<TaxonNode> selectedTaxonNodes;
+
+ private boolean dirty;
+
+ private ChecklistEditorFilter filter;
+
+ private Menu headerMenu;
+
+ private Label statusLabel;
+
+ private SortedSet<DefinedTermBase> terms = null;
+
+ /**
+ * @return the selectedTaxonNodes
+ */
+ public List<TaxonNode> getSelectedTaxonNodes() {
+ return selectedTaxonNodes;
+ }
+
+ /**
+ * @param selectedTaxonNodes
+ * the selectedTaxonNodes to set
+ */
+ public void setSelectedTaxonNodes(List<TaxonNode> selectedTaxonNodes) {
+ this.selectedTaxonNodes = selectedTaxonNodes;
+ }
+
+ /**
+ * <p>
+ * Constructor for CdmDataSourceViewPart.
+ * </p>
+ */
+ public ChecklistEditor() {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void createPartControl(Composite parent) {
+ service = (IWorkbenchSiteProgressService) getSite().getAdapter(IWorkbenchSiteProgressService.class);
+ taxonNodeService = CdmStore.getService(ITaxonNodeService.class);
+ selectionService = getSite().getWorkbenchWindow().getSelectionService();
+ selectionService.addSelectionListener(this);
+ filter = new ChecklistEditorFilter();
+ ChecklistLabelProvider labelProvider = new ChecklistLabelProvider();
+ terms = (SortedSet<DefinedTermBase>) labelProvider.getTermsOrderedByLabels(labelProvider.getNamedAreas(),
+ CdmStore.getDefaultLanguage());
+ createTopComposite(parent);
+ loadDataInput();
+ }
+
+ /**
+ * @param parent
+ */
+ private void createTopComposite(Composite parent) {
+ GridLayout gridLayout = new GridLayout(3, false);
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ parent.setLayout(gridLayout);
+
+ final Text searchText = createSearchBar(parent);
+
+ if (terms != null && !terms.isEmpty()) {
+ createToolbar(parent);
+ }
+ viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
+ getSite().setSelectionProvider(viewer);
+
+ final ModifyListener modifyListener = new ChecklistModifyListener(viewer, filter, searchText);
+
+ searchText.addFocusListener(new ChecklistFocusListener(searchText, modifyListener));
+ searchText.addModifyListener(modifyListener);
+
+ // Layout the viewer
+ GridData gridData = new GridData();
+ gridData.verticalAlignment = GridData.FILL;
+ gridData.horizontalSpan = 3;
+ gridData.grabExcessHorizontalSpace = true;
+ gridData.grabExcessVerticalSpace = true;
+ gridData.horizontalAlignment = GridData.FILL;
+ viewer.getControl().setLayoutData(gridData);
+
+ viewer.addFilter(filter);
+ createColumns(viewer);
+
+ viewer.setContentProvider(new ChecklistContentProvider());
+ viewer.setLabelProvider(new ChecklistLabelProvider());
+ comparator = new ChecklistEditorComparator();
+ viewer.setComparator(comparator);
+
+ createStatusBar(parent);
+ }
+
+ /**
+ * @param parent
+ * @return
+ */
+ private Text createSearchBar(Composite parent) {
+ Label searchLabel = new Label(parent, SWT.NONE);
+ searchLabel.setText("Search: ");
+ final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.CANCEL);
+ searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ searchText.setForeground(EditorUtil.getColor(Resources.SEARCH_VIEW_FOREGROUND));
+ searchText.setText("type filter text");
+ return searchText;
+ }
+
+ /**
+ * @param parent
+ */
+ private void createToolbar(Composite parent) {
+ ToolBar toolBar = new ToolBar(parent, SWT.NONE);
+ ToolItem toolItem = new ToolItem(toolBar, SWT.DROP_DOWN | SWT.BORDER);
+ toolItem.setText("Distribution Status");
+ toolItem.setToolTipText("Show Distribution Status for selected Areas");
+ ChecklistDropdownSelectionListener dropListener = new ChecklistDropdownSelectionListener(toolItem, this, terms);
+ for (DefinedTermBase<DefinedTermBase> term : terms) {
+ if(term!=null){
+ dropListener.add(term);
+ }
+ }
+ toolItem.addSelectionListener(dropListener);
+ toolBar.pack();
+ }
+
+ private void createStatusBar(Composite composite) {
+ GridData gridData = new GridData();
+ gridData.horizontalSpan = 3;
+ gridData.grabExcessHorizontalSpace = true;
+ gridData.horizontalAlignment = GridData.FILL;
+
+ statusLabel = new Label(composite, SWT.LEFT);
+ statusLabel.setText("Anzahl der Elemente: " + (countNodes != null ? countNodes : "uknown"));
+ statusLabel.setLayoutData(gridData);
+ }
+
+ // This will create the columns for the table
+ private void createColumns(TableViewer viewer) {
+ Table table = viewer.getTable();
+ List<String> titles = new ArrayList<String>();
+ Collections.addAll(titles, "Taxon", "Author", "Reference", "Rank");
+ List<Integer> bounds = new ArrayList<Integer>();
+ Collections.addAll(bounds, 300, 200, 200, 200);
+ Map<Integer, Boolean>restoreColumnWidth = new HashMap<Integer, Boolean>();
+ if (terms != null) {
+ int i = 4;
+ for (DefinedTermBase<DefinedTermBase> term : terms) {
+ if(term != null){
+ restoreColumnWidth.put(i, PreferencesUtil.getPreferenceStore().getBoolean(term.getUuid().toString()));
+ titles.add(term.getTitleCache());
+ bounds.add(200);
+ i++;
+ }
+ }
+ }
+
+ for (int i = 0; i < titles.size(); i++) {
+ TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+ column.getColumn().setText(titles.get(i));
+ column.getColumn().setWidth(bounds.get(i));
+ column.getColumn().setResizable(true);
+ column.getColumn().setMoveable(true);
+ column.getColumn().addSelectionListener(getSelectionAdapter(column.getColumn(), i));
+ if (i == 1) {
+ table.setSortColumn(column.getColumn());
+ }
+ if (i == 3) {
+ column.setEditingSupport(new RankEditingSupport(viewer, this));
+ }
+ if (i >= 4) {
+ //read PrefrenceStore to setWidth according to last saved state
+ if(restoreColumnWidth.get(i)){
+ column.getColumn().setWidth(100);
+ }else{
+ column.getColumn().setWidth(0);
+ }
+ column.setEditingSupport(new DistributionEditingSupport(viewer, this, i));
+ }
+ }
+ table.setSortDirection(SWT.DOWN);
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+ }
+
+ /**
+ *
+ * pull data from database and set input for view
+ *
+ */
+ private void loadDataInput() {
+ Classification classification = checklistEditorInput.getClassification();
+ TaxonNode taxonNode = checklistEditorInput.getTaxonNode();
+ if (classification != null && taxonNode == null) {
+ countNodes = taxonNodeService.countAllNodesForClassification(classification);
+ statusLabel.setText("Anzahl der Elemente: " + (countNodes != null ? countNodes : "uknown"));
+ // This should not kill the view nor the editor if something goes
+ // wrong
+ // TODO: don't load the whole taxonNode Object but rather a small
+ // and simple Solution
+ // FIXME: May be don't open classification which are greater than
+ // 3000 Taxa
+ selectedTaxonNodes = taxonNodeService.listAllNodesForClassification(classification, 0, countNodes);
+ getService().schedule(new ChecklistJob("loading Taxa", selectedTaxonNodes), Job.LONG);
+ }
+ if (taxonNode != null) {
+ selectedTaxonNodes = taxonNodeService.loadChildNodesOfTaxonNode(taxonNode, NODE_INIT_STRATEGY, true, false);
+ getService().schedule(new ChecklistJob("loading Taxa", selectedTaxonNodes), Job.LONG);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFocus() {
+ viewer.getControl().setFocus();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.part.WorkbenchPart#showBusy(boolean)
+ */
+ /** {@inheritDoc} */
+ @Override
+ public void showBusy(boolean busy) {
+ super.showBusy(busy);
+ // viewer.getTable().setEnabled(!busy);
+ if (busy) {
+ partNameCache = getPartName();
+ setPartName("Loading " + countNodes + " Taxa...");
+ } else {
+ if (partNameCache != null) {
+ setPartName(partNameCache);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Getter for the field <code>viewer</code>.
+ * </p>
+ *
+ * @return a {@link org.eclipse.jface.viewers.StructuredViewer} object.
+ */
+ public StructuredViewer getViewer() {
+ return viewer;
+ }
+
+ public void refresh() {
+ viewer.refresh();
+ }
+
+ /**
+ * <p>
+ * Getter for the field <code>service</code>.
+ * </p>
+ *
+ * @return the service
+ */
+ public IWorkbenchSiteProgressService getService() {
+ return service;
+ }
+
+ private SelectionAdapter getSelectionAdapter(final TableColumn column, final int index) {
+ SelectionAdapter selectionAdapter = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ comparator.setColumn(index);
+ int dir = viewer.getTable().getSortDirection();
+ if (viewer.getTable().getSortColumn() == column) {
+ dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
+ } else {
+ dir = SWT.DOWN;
+ }
+ viewer.getTable().setSortDirection(dir);
+ viewer.getTable().setSortColumn(column);
+ viewer.refresh();
+ }
+ };
+ return selectionAdapter;
+ }
+
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Saving Editor", 1);
+ getConversationHolder().bind();
+ getConversationHolder().commit(true);
+ setDirty(false);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+
+ }
+
+ @Override
+ public void doSaveAs() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ setSite(site);
+ setInput(input);
+ if (input instanceof ChecklistEditorInput) {
+ checklistEditorInput = (ChecklistEditorInput) input;
+ setPartName(getPartName() + ": " + checklistEditorInput.getName());
+ conversation = ((ChecklistEditorInput) input).getConversationHolder();
+ }
+ simpleSelectionProvider = new SimpleSelectionProvider();
+ getSite().setSelectionProvider(simpleSelectionProvider);
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.
+ * IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostDataChangeObserver#update
+ * (eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap)
+ */
+ @Override
+ public void update(CdmDataChangeMap changeEvents) {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
+ * ()
+ */
+ @Override
+ public ConversationHolder getConversationHolder() {
+ return conversation;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IContextListener#contextAboutToStop(org.
+ * eclipse.ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
+ */
+
+ @Override
+ public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IContextListener#contextStop(org.eclipse
+ * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void contextStop(IMemento memento, IProgressMonitor monitor) {
+ // TODO Auto-generated method stub
+// IStructuredSelection sel = (IStructuredSelection) this.viewer.getSelection();
+// if (sel.isEmpty()) {
+// return;
+// }
+// memento = memento.createChild("tree-selections");
+// Iterator iter = sel.iterator();
+// while (iter.hasNext()) {
+// String nodeName = (String) iter.next();
+// memento.createChild("selected-nodes", nodeName);
+// }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IContextListener#contextStart(org.eclipse
+ * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void contextStart(IMemento memento, IProgressMonitor monitor) {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IContextListener#contextRefresh(org.eclipse
+ * .core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void contextRefresh(IProgressMonitor monitor) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IContextListener#workbenchShutdown(org.eclipse
+ * .ui.IMemento, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
+ conversation.clear();
+ conversation.close();
+ conversation = null;
+ }
+
+ private static final List<String> NODE_INIT_STRATEGY = Arrays.asList(new String[] { "descriptions",
+ "descriptions.*", "description.state", "feature", "feature.*", "childNodes", "childNodes.taxon",
+ "childNodes.taxon.name", "taxonNodes", "taxonNodes.*", "taxonNodes.taxon.*", "taxon.*",
+ "taxon.descriptions", "taxon.sec", "taxon.name.*", "taxon.synonymRelations", "terms", "name.*",
+ "name.rank.representations", "name.status.type.representations", "sources.$", "stateData.$" });
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider#changed(
+ * java.lang.Object)
+ */
+ @Override
+ public void changed(Object element) {
+ if (element != null) {
+ viewer.update(element, null);
+ setDirty(true);
+ firePropertyChange(PROP_DIRTY);
+ }
+ }
+
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ firePropertyChange(PROP_DIRTY);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.forms.editor.FormEditor#isDirty()
+ */
+ @Override
+ public boolean isDirty() {
+ return dirty;
+ }
+}