2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.taxeditor
.navigation
;
12 import org
.apache
.log4j
.Logger
;
13 import org
.eclipse
.core
.databinding
.observable
.set
.IObservableSet
;
14 import org
.eclipse
.jface
.action
.Action
;
15 import org
.eclipse
.jface
.action
.IMenuListener
;
16 import org
.eclipse
.jface
.action
.IMenuManager
;
17 import org
.eclipse
.jface
.action
.MenuManager
;
18 import org
.eclipse
.jface
.action
.Separator
;
19 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
20 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
21 import org
.eclipse
.jface
.viewers
.CellEditor
;
22 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
23 import org
.eclipse
.jface
.viewers
.ICellEditorListener
;
24 import org
.eclipse
.jface
.viewers
.ICellModifier
;
25 import org
.eclipse
.jface
.viewers
.IContentProvider
;
26 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
27 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
28 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
29 import org
.eclipse
.jface
.viewers
.TreeSelection
;
30 import org
.eclipse
.jface
.viewers
.TreeViewer
;
31 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
32 import org
.eclipse
.swt
.SWT
;
33 import org
.eclipse
.swt
.dnd
.DND
;
34 import org
.eclipse
.swt
.dnd
.DragSource
;
35 import org
.eclipse
.swt
.dnd
.DragSourceAdapter
;
36 import org
.eclipse
.swt
.dnd
.DragSourceEvent
;
37 import org
.eclipse
.swt
.dnd
.DropTarget
;
38 import org
.eclipse
.swt
.dnd
.DropTargetAdapter
;
39 import org
.eclipse
.swt
.dnd
.DropTargetEvent
;
40 import org
.eclipse
.swt
.dnd
.Transfer
;
41 import org
.eclipse
.swt
.graphics
.Point
;
42 import org
.eclipse
.swt
.graphics
.Rectangle
;
43 import org
.eclipse
.swt
.layout
.FillLayout
;
44 import org
.eclipse
.swt
.widgets
.Composite
;
45 import org
.eclipse
.swt
.widgets
.Display
;
46 import org
.eclipse
.swt
.widgets
.Event
;
47 import org
.eclipse
.swt
.widgets
.Item
;
48 import org
.eclipse
.swt
.widgets
.Label
;
49 import org
.eclipse
.swt
.widgets
.Listener
;
50 import org
.eclipse
.swt
.widgets
.Menu
;
51 import org
.eclipse
.swt
.widgets
.Shell
;
52 import org
.eclipse
.swt
.widgets
.Tree
;
53 import org
.eclipse
.swt
.widgets
.TreeItem
;
54 import org
.springframework
.transaction
.TransactionStatus
;
56 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
57 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
58 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
59 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
60 import eu
.etaxonomy
.taxeditor
.ITaxEditorConstants
;
61 import eu
.etaxonomy
.taxeditor
.TaxEditorPlugin
;
62 import eu
.etaxonomy
.taxeditor
.actions
.TaxonTransfer
;
63 import eu
.etaxonomy
.taxeditor
.actions
.cdm
.DeleteTaxonAction
;
64 import eu
.etaxonomy
.taxeditor
.actions
.cdm
.MoveTaxonAction
;
65 import eu
.etaxonomy
.taxeditor
.actions
.cdm
.SaveTaxonAction
;
66 import eu
.etaxonomy
.taxeditor
.actions
.ui
.AddQuickNameAction
;
67 import eu
.etaxonomy
.taxeditor
.actions
.ui
.OpenNewChildNameEditorAction
;
68 import eu
.etaxonomy
.taxeditor
.actions
.ui
.OpenTaxonEditorAction
;
69 import eu
.etaxonomy
.taxeditor
.editor
.ContextMenu
;
70 import eu
.etaxonomy
.taxeditor
.editor
.name
.IterableSynonymyList
;
71 import eu
.etaxonomy
.taxeditor
.model
.CdmUtil
;
72 import eu
.etaxonomy
.taxeditor
.model
.TaxonomicTreeContentProvider
;
75 * Taxon tree viewer which responds to events within individual taxa.
77 * Good overview of TreeViewer:
78 * http://www.eclipse.org/articles/Article-TreeViewer/TreeViewerArticle.htm
84 public class TaxonomicTreeViewer
extends TreeViewer
{
85 private static final Logger logger
= Logger
86 .getLogger(TaxonomicTreeViewer
.class);
89 private ContextMenu contextMenu
;
91 private boolean quickAddMode
;
93 private boolean initialized
= false;
96 * Creates a lazy-loading taxonomic tree that is sorted by TitleCache, and
97 * which listens for name changes and new taxa
101 public TaxonomicTreeViewer(Composite parent
) {
103 // SW.VIRTUAL causes nodes to be loaded on-demand, improving performance
104 super(parent
, SWT
.VIRTUAL
);
106 tree
= this.getTree();
107 tree
.setLinesVisible(false);
108 tree
.setHeaderVisible(false);
116 public boolean isInitialized() {
121 * Tool tips are used to show warnings; there is no tooltip mechanism for
122 * individual tree items out-of-the-box, so tipListener below builds one
124 private void createToolTips() {
125 Tree tree
= this.getTree();
127 // Disable native tooltip
128 tree
.setToolTipText("");
130 tree
.addListener(SWT
.Dispose
, tipListener
);
131 tree
.addListener(SWT
.KeyDown
, tipListener
);
132 tree
.addListener(SWT
.MouseMove
, tipListener
);
133 tree
.addListener(SWT
.MouseHover
, tipListener
);
136 void createDragAndDrop() {
137 final Tree tree
= this.getTree();
138 Transfer
[] types
= new Transfer
[] { TaxonTransfer
.getInstance() };
139 int operations
= DND
.DROP_MOVE
| DND
.DROP_COPY
| DND
.DROP_LINK
;
141 final DragSource source
= new DragSource(tree
, operations
);
142 source
.setTransfer(types
);
143 final TreeItem
[] dragSourceItem
= new TreeItem
[1];
144 source
.addDragListener(new DragSourceAdapter() {
145 public void dragStart(DragSourceEvent event
) {
146 TreeItem
[] selection
= tree
.getSelection();
147 if (selection
.length
> 0) {
149 dragSourceItem
[0] = selection
[0];
155 public void dragSetData(DragSourceEvent event
) {
156 Taxon taxon
= (Taxon
) dragSourceItem
[0].getData();
157 TaxonTransfer
.getInstance().setTaxon(taxon
);
161 DropTarget target
= new DropTarget(tree
, operations
);
162 target
.setTransfer(types
);
163 target
.addDropListener(new DropTargetAdapter() {
164 public void drop(DropTargetEvent event
) {
166 // Only drop ONTO another TreeItem OR outside the tree area.
167 // Dropping between TreeItems makes no sense because tree is
169 Taxon taxon
= (Taxon
) dragSourceItem
[0].getData();
170 Taxon parentTaxon
= null;
172 // If event.item == null, user has dragged outside of the tree
173 if (event
.item
== null) {
176 parentTaxon
= (Taxon
) event
.item
.getData();
179 new MoveTaxonAction(taxon
, parentTaxon
).run();
186 private void removeQuicklyAddedTaxon() {
187 if (quickAddTaxon
!= null) {
188 new DeleteTaxonAction(quickAddTaxon
).run();
189 // if above doesn't work, do remove session taxon but also w remove children
190 // TaxEditorPlugin.getDefault().removeSessionTaxon(quickAddTaxon);
191 quickAddTaxon
= null;
196 * Taxa with no NameCache - i.e. added with quick name - can be edited in
197 * place in the tax. tree
199 public void createQuickAdd() {
201 final Tree tree
= this.getTree();
202 final TextCellEditor taxTreeNodeEditor
= new TextCellEditor(tree
);
203 taxTreeNodeEditor
.addListener(new ICellEditorListener() {
204 public void applyEditorValue() {
205 Object value
= taxTreeNodeEditor
.getValue();
206 if (value
instanceof String
) {
207 String trimmedValue
= ((String
) value
).trim();
209 if (trimmedValue
.length() == 0) {
210 removeQuicklyAddedTaxon();
213 NonViralName name
= (NonViralName
) quickAddTaxon
.getName();
214 // CdmUtil.parseFullName(name, trimmedValue, null, true);
215 CdmUtil
.parseFullReference(name
, trimmedValue
, null, true);
217 // TaxonNameBase name = CdmUtil.parseFullReference(
218 // trimmedValue, null, null);
219 // name.addTaxonBase(quickAddTaxon);
221 new SaveTaxonAction(quickAddTaxon
).run();
222 quickAddTaxon
= null;
227 public void cancelEditor() {
228 removeQuicklyAddedTaxon();
231 public void editorValueChanged(boolean oldValidState
,
232 boolean newValidState
) {
236 this.setCellEditors(new CellEditor
[] { taxTreeNodeEditor
});
237 this.setColumnProperties(new String
[] { "col1" });
238 this.setCellModifier(new ICellModifier() {
239 public boolean canModify(Object element
, String property
) {
241 if (element
instanceof Taxon
) {
242 Taxon taxon
= (Taxon
) element
;
244 // If name element has not been initialized,
245 // this is a taxon added with QuickAdd
246 // if (taxon.getName() == null) {
247 // quickAddTaxon = taxon;
251 // BUG Quickly added taxon now retrieved via the quick add action's
252 // firePropertyChange - using NULL name to signal this is the
253 // quick name taxon was causing odd hibernate errors when quickly
254 // added taxon was created and deleted in the same session
255 if (taxon
.equals(quickAddTaxon
)) {
260 quickAddTaxon
= null;
264 public Object
getValue(Object element
, String property
) {
265 // If this node is editable, TitleCache is by definition empty
269 public void modify(Object element
, String property
, Object value
) {
275 * On double click, open name editor
277 public void createDoubleClickListener() {
278 this.addDoubleClickListener(new IDoubleClickListener() {
280 public void doubleClick(DoubleClickEvent event
) {
282 if (event
.getSelection() instanceof StructuredSelection
) {
283 Object element
= ((StructuredSelection
) event
284 .getSelection()).getFirstElement();
285 if (element
instanceof Taxon
) {
286 new OpenTaxonEditorAction((Taxon
) element
).run();
294 * Set up content providers and viewer input
296 private void createContent() {
298 // Yet another custom content provider ...
299 IContentProvider viewerContentProviderList
= new TaxonomicTreeContentProvider();
300 this.setContentProvider(viewerContentProviderList
);
302 // Label provider that listens for changes to name cache
303 IObservableSet observableTaxonSet
= TaxEditorPlugin
.getDefault()
304 .getObservableSessionTaxa();
305 this.setLabelProvider(new TaxonomicTreeLabelProvider(
306 observableTaxonSet
));
308 // TaxonTreeList added to every time a node is opened with its
309 // children, or when a new taxon is added
310 this.setInput(TaxEditorPlugin
.getDefault().getSessionRootTaxa());
312 // Sort according to "getColumnText" above, i.e. by NameCache
313 this.setComparator(new ViewerComparator());
317 * If taxon node in the tree is hidden, open and select it
321 public void revealTaxon(Taxon taxon
) {
322 this.setSelection(new StructuredSelection(taxon
), true);
326 // Implement a "fake" tooltip
327 final Listener labelListener
= new Listener() {
328 public void handleEvent(Event event
) {
329 Label label
= (Label
) event
.widget
;
330 Shell shell
= label
.getShell();
331 switch (event
.type
) {
333 Event e
= new Event();
334 e
.item
= (TreeItem
) label
.getData("_TREEITEM");
335 // Assuming table is single select, set the selection as if
336 // the mouse down event went through to the table
337 // Tree tree = TaxonomicTreeViewer.this.getTree();
338 Tree tree
= getTree();
339 tree
.setSelection(new TreeItem
[] { (TreeItem
) e
.item
});
340 tree
.notifyListeners(SWT
.Selection
, e
);
351 Listener tipListener
= new Listener() {
355 public void handleEvent(Event event
) {
356 switch (event
.type
) {
359 case SWT
.MouseMove
: {
367 case SWT
.MouseHover
: {
368 // Item item = TaxonomicTreeViewer.this.getItemAt(new
369 // Point(event.x, event.y));
370 Item item
= getItemAt(new Point(event
.x
, event
.y
));
372 if (tip
!= null && !tip
.isDisposed())
374 tip
= new Shell(Display
.getCurrent(), SWT
.ON_TOP
375 | SWT
.NO_FOCUS
| SWT
.TOOL
);
376 tip
.setBackground(Display
.getCurrent().getSystemColor(
377 SWT
.COLOR_INFO_BACKGROUND
));
378 FillLayout layout
= new FillLayout();
379 layout
.marginWidth
= 2;
380 tip
.setLayout(layout
);
381 label
= new Label(tip
, SWT
.NONE
);
382 label
.setForeground(Display
.getCurrent().getSystemColor(
383 SWT
.COLOR_INFO_FOREGROUND
));
384 label
.setBackground(Display
.getCurrent().getSystemColor(
385 SWT
.COLOR_INFO_BACKGROUND
));
386 label
.setData("_TABLEITEM", item
);
387 label
.setText(item
.getText());
388 if (item
.getData() instanceof Taxon
) {
389 Taxon taxon
= (Taxon
) item
.getData();
390 IterableSynonymyList synonymyList
= new IterableSynonymyList(
392 String synonymyListDisplay
= CdmUtil
393 .getDisplayName(taxon
);
395 for (TaxonBase synonymOrMisName
: synonymyList
) {
396 TaxonNameBase name
= synonymOrMisName
.getName();
398 synonymyListDisplay
+= "\n "
399 + CdmUtil
.getDisplayName(name
);
402 label
.setText(synonymyListDisplay
);
404 // label.setText ("");
405 label
.addListener(SWT
.MouseExit
, labelListener
);
406 label
.addListener(SWT
.MouseDown
, labelListener
);
407 Point size
= tip
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
);
408 Rectangle rect
= ((TreeItem
) item
).getBounds(0);
409 // Point pt = TaxonomicTreeViewer.this.getTree().toDisplay
411 Point pt
= getTree().toDisplay(rect
.x
, rect
.y
);
412 tip
.setBounds(pt
.x
, pt
.y
, size
.x
, size
.y
);
413 tip
.setVisible(true);
420 public void createMenu() {
421 final MenuManager menuManager
= new MenuManager();
422 menuManager
.setRemoveAllWhenShown(true);
423 final Menu menu
= menuManager
.createContextMenu(tree
);
426 menuManager
.addMenuListener(new IMenuListener() {
427 public void menuAboutToShow(IMenuManager manager
) {
429 Object selection
= ((TreeSelection
) getSelection())
431 if (!(selection
instanceof Taxon
)) {
434 Taxon taxon
= (Taxon
) selection
;
436 Action openEditorAction
= new OpenTaxonEditorAction(taxon
);
437 manager
.add(openEditorAction
);
439 Action openNewChildEditorAction
= new OpenNewChildNameEditorAction(
441 manager
.add(openNewChildEditorAction
);
443 Action openQuickNameAction
= new AddQuickNameAction(taxon
);
444 manager
.add(openQuickNameAction
);
446 .addPropertyChangeListener(new IPropertyChangeListener() {
447 public void propertyChange(PropertyChangeEvent event
) {
448 if (event
.getProperty().equals(
449 ITaxEditorConstants
.QUICK_NAME_TAXON
)
450 && event
.getNewValue() instanceof Taxon
) {
451 quickAddTaxon
= (Taxon
) event
.getNewValue();
456 manager
.add(new Separator());
458 Action deleteTaxonAction
= new DeleteTaxonAction(taxon
);
459 manager
.add(deleteTaxonAction
);