Project

General

Profile

Download (29.8 KB) Statistics
| Branch: | Tag: | Revision:
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.editor.name.container;
11

    
12
import java.util.List;
13
import java.util.Set;
14

    
15
import javax.naming.OperationNotSupportedException;
16

    
17
import org.apache.log4j.Logger;
18
import org.eclipse.core.runtime.Assert;
19
import org.eclipse.jface.dialogs.Dialog;
20
import org.eclipse.swt.dnd.DND;
21
import org.eclipse.swt.dnd.DragSource;
22
import org.eclipse.swt.dnd.Transfer;
23
import org.eclipse.swt.events.ControlAdapter;
24
import org.eclipse.swt.events.ControlEvent;
25
import org.eclipse.swt.events.ControlListener;
26
import org.eclipse.swt.events.FocusAdapter;
27
import org.eclipse.swt.events.FocusEvent;
28
import org.eclipse.swt.events.FocusListener;
29
import org.eclipse.swt.events.ModifyEvent;
30
import org.eclipse.swt.events.ModifyListener;
31
import org.eclipse.swt.events.MouseAdapter;
32
import org.eclipse.swt.events.MouseEvent;
33
import org.eclipse.swt.graphics.Color;
34
import org.eclipse.swt.graphics.Font;
35
import org.eclipse.swt.graphics.Image;
36
import org.eclipse.swt.widgets.Composite;
37
import org.eclipse.swt.widgets.Control;
38
import org.eclipse.swt.widgets.Display;
39
import org.eclipse.swt.widgets.Label;
40
import org.eclipse.swt.widgets.Menu;
41
import org.eclipse.ui.forms.IFormPart;
42
import org.eclipse.ui.forms.IManagedForm;
43
import org.eclipse.ui.forms.widgets.TableWrapData;
44
import org.eclipse.ui.forms.widgets.TableWrapLayout;
45

    
46
import eu.etaxonomy.cdm.common.CdmUtils;
47
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
48
import eu.etaxonomy.cdm.model.name.NameRelationship;
49
import eu.etaxonomy.cdm.model.name.NonViralName;
50
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
51
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
52
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
53
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
54
import eu.etaxonomy.taxeditor.editor.EditorUtil;
55
import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
56
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
57
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
58
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
59
import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
60
import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
61
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
62
import eu.etaxonomy.taxeditor.model.NameHelper;
63
import eu.etaxonomy.taxeditor.model.TextHelper;
64
import eu.etaxonomy.taxeditor.parser.ParseHandler;
65
import eu.etaxonomy.taxeditor.preference.Resources;
66
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.EditorAnnotationFacade;
67
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.EditorAnnotationFacade.EditorAnnotationType;
68
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.INameViewer;
69
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.LineBreakListenerFacade;
70
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.LineWrapSupportFacade;
71
import eu.etaxonomy.taxeditor.singlesource.editor.name.container.NameViewerFacade;
72
import eu.etaxonomy.taxeditor.singlesource.org.eclipse.jface.text.IPosition;
73
import eu.etaxonomy.taxeditor.singlesource.org.eclipse.jface.text.PositionFacade;
74
import eu.etaxonomy.taxeditor.singlesource.org.eclipse.jface.window.IToolTip;
75
import eu.etaxonomy.taxeditor.singlesource.org.eclipse.jface.window.ToolTipFacade;
76
import eu.etaxonomy.taxeditor.singlesource.org.eclipse.swt.widgets.DisplayProxy;
77

    
78
/**
79
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
80
 * Editor. This should be used to maintain a consistent look and feel for all Editor
81
 * freetext area components, such as DescriptionElementComposite.
82
 * <p>
83
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
84
 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
85
 * populate the property sheet with the data.
86
 * </p>
87
 * <p>
88
 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
89
 * own <code>getData()</code>.
90
 * </p>
91
 * <p>
92
 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
93
 * <code>createBorderSupport()</code>.
94
 * </p>
95
 *
96
 * @author p.ciardelli
97
 * @author n.hoffmann
98
 * @created 02.06.2008
99
 * @version 1.0
100
 */
101
abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart, IContainerConstants, IElementHasDetails {	
102
	private static final Logger logger = Logger.getLogger(AbstractGroupedContainer.class);
103
	
104
	protected ParseHandler parseHandler;
105
	
106
	private FocusListener nameCompositeFocusListener;
107
	private ModifyListener nameCompositeModifyListener;
108

    
109
	protected NameViewerFacade nameViewer;
110
	
111
	private AbstractGroup group;
112
	
113
	private Label nonEditableInfoLabel;
114
	private IToolTip nonEditableInfoHover;
115
	
116
	private static AbstractGroupedContainer selection;
117
	
118
	private FocusListener focusListener;
119
	private LineBreakListenerFacade lineBreakListener;
120

    
121
	private int cursorPosition;
122

    
123
	protected Composite control;
124

    
125
	private Color backgroundColor;
126
	private boolean isDirty;
127
	
128
	
129
	/**
130
	 * <p>Constructor for AbstractGroupedContainer.</p>
131
	 *
132
	 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
133
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
134
	 * @param taxonBase a T object.
135
	 * @param <T> a T object.
136
	 */
137
	public AbstractGroupedContainer(T taxonBase){
138
		setData(taxonBase);
139
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
140
	}
141
	
142
	public void createContent() {
143
		createControl();
144
		
145
		try {
146
			createTextViewer();
147
			createLineWrapSupport();
148
			createLineBreakListener();
149
		} catch (OperationNotSupportedException e) {
150
			logger.warn("AbstractGroupedContainer.createContent(): Operation not supported:" +  e.getMessage());
151
		}
152

    
153
		setMenu(getEditor().getMenu());
154
		
155
		setDraggableControl(new Control[]{getControl(), getNameViewer().getDraggableControl()});
156
		
157
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
158
		
159
		
160
		initializeComposite();
161
		
162
		createListener();
163

    
164
		try {
165
			isFreeTextEnabled();
166
		} catch (OperationNotSupportedException e) {
167
			logger.warn("AbstractGroupedContainer.initTextViewer(): EditorAnnotations not supported :" + e.getMessage());
168
		}
169
	}
170

    
171
	
172
	
173
	/**
174
	 * <p>createListener</p>
175
	 */
176
	protected void createListener(){
177
		nameCompositeModifyListener = new ModifyListener(){
178
			
179
			public void modifyText(ModifyEvent e) {
180
				// mark the composite dirty
181
				setDirty(true);
182
				// parse the text
183
				String text = nameViewer.getText();
184
				
185
				NonViralName name = parseHandler.parse(text);
186
				getTaxonBase().setName(name);
187
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
188
				
189
				// show errors resulting from parsing
190
				try {
191
					calculateAnnotations();
192
				} catch (OperationNotSupportedException e1) {
193
					logger.warn("AbstractGroupedContainer.createListener(): EditorAnnotations not supported :" + e1.getMessage() );
194
				}
195
				// store the position of the cursor				
196
				storeCursor();
197
				// notify selection listener
198
				setDelayedSelection();
199
			}
200
		};
201
		nameCompositeFocusListener = new FocusAdapter(){
202

    
203
			/* (non-Javadoc)
204
			 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
205
			 */
206
			@Override
207
			public void focusLost(FocusEvent e) {
208
				super.focusLost(e);
209

    
210
				persistName();
211
			}		
212
		};
213
		
214
		addListener();
215
	}
216
	
217
	private void addListener(){
218
		getNameViewer().addModifyListener(nameCompositeModifyListener);
219
		getNameViewer().addFocusListener(nameCompositeFocusListener);
220
	}
221
	
222
	private void removeListener(){
223
		getNameViewer().removeModifyListener(nameCompositeModifyListener);
224
		getNameViewer().removeFocusListener(nameCompositeFocusListener);
225
	}
226
		
227
	/**
228
	 * Initialize the composite specific code
229
	 */
230
	protected abstract void initializeComposite();
231
	
232
	/**
233
	 * <p>getEmptyTextPrompt</p>
234
	 *
235
	 * @return a {@link java.lang.String} object.
236
	 */
237
	protected String getEmptyTextPrompt() {
238
		return EMPTY_NAME_PROMPT;
239
	}
240
	
241
	/**
242
	 * 
243
	 */
244
	private void showNameRelations() {
245
		TaxonNameBase<?, ?> name = getName();
246
		if (name == null) {
247
			return;
248
		}
249
		
250
		ILabelImageStrategy strategy = LabelImageProvider.getLabelStrategy(name);
251
		LabelImageProvider labelProvider = new LabelImageProvider(strategy);
252
		
253
		Set<NameRelationship> nameRelations = name.getNameRelations();
254
		if (nameRelations.size() == 0) {
255
			return;
256
		}
257
//		for (NameRelationship nameRelation : nameRelations) {
258
//			String typeLabel = null;
259
//			TaxonNameBase<?, ?> relatedName = null; 
260
//
261
//			if (name.equals(nameRelation.getFromName())) {
262
//				typeLabel = labelProvider.getNameRelationTypeLabel( 
263
//								nameRelation.getType());
264
//				relatedName = nameRelation.getToName();
265
//			} else {
266
//				typeLabel = labelProvider.getNameRelationTypeInverseLabel( 
267
//								nameRelation.getType());
268
//				relatedName = nameRelation.getFromName();
269
//			}
270
//			
271
//			setNonEditableInfo(typeLabel + " " + NameHelper.getDisplayName(relatedName));
272
//		}
273
	}
274
		
275
	/**
276
	 * <p>initTextViewer</p>
277
	 */
278
	protected void initTextViewer() {
279
		
280
//		showNameRelations();
281
		
282
		updateIndent();
283
		
284
		updateIcon();
285
		
286
		String text = NameHelper.getDisplayNameWithRef(getData());
287
		
288
		if (text.length() == 0) {
289
			initEmptyText();
290
		} else {
291
			getNameViewer().setText(text);
292
			placeCursor();
293
		}
294
		try {
295
			calculateAnnotations();
296
		} catch (OperationNotSupportedException e) {
297
			logger.warn("AbstractGroupedContainer.initTextViewer(): EditorAnnotations not supported :" + e.getMessage() );
298
		}
299
	}
300
	
301
	/**
302
	 * <p>calculateErrors</p>
303
	 * @throws OperationNotSupportedException 
304
	 */
305
	synchronized protected void calculateAnnotations() throws OperationNotSupportedException {
306
		getNameViewer().clearAnnotations();
307
		showAnnotations();
308
	}
309

    
310
	/**
311
	 * 
312
	 */
313
	public void showAnnotations() throws OperationNotSupportedException {
314
		
315
		if(getName().hasProblem()){
316
			showParsingProblems();
317
		}
318
		
319
		if(!isNameParsable()){
320
			getNameViewer().addAnnotation(
321
					new EditorAnnotationFacade(EditorAnnotationType.WARNING, 0, "This name may only be edited in the details view."));
322
		}
323
		
324
		if(isNameUsedMultipleTimes()){
325
			getNameViewer().addAnnotation(new EditorAnnotationFacade(EditorAnnotationType.WARNING, 0, "This taxons name is used multiple times."));
326
		}
327
		
328
		
329
	}
330

    
331
	/**
332
	 * @throws OperationNotSupportedException 
333
	 * 
334
	 */
335
	private void showParsingProblems() throws OperationNotSupportedException {
336
		String text = getNameViewer().getText();
337
		
338
		List<ParserProblem> parsingProblems = getName().getParsingProblems();
339
		
340
		for (ParserProblem problem : parsingProblems) {
341
			getNameViewer().addAnnotation(new EditorAnnotationFacade(problem), getParsingProblemPosition());
342
		}	
343
	}
344
	
345
	private IPosition getParsingProblemPosition() throws OperationNotSupportedException{
346
		String text = getNameViewer().getText();
347
		
348
		if (getName().hasProblem() && text.length() > 0) {
349
			int start = getName().getProblemStarts();
350
			int length = getName().getProblemEnds() - start;
351
			
352
			if (start == -1 || getName().getProblemEnds() == -1) {
353
				return null;
354
			}
355
			
356
			// Don't let squigglies try to draw beyond the end of the text
357
			if (text.length() < start + length) {
358
				length = text.length() - start;
359
			}
360
			
361
			return new PositionFacade(start, length);
362
		}
363
		return null;
364
	}
365

    
366
	/**
367
	 * <p>handleSplitText</p>
368
	 *
369
	 * @param text a {@link java.lang.String} object.
370
	 */
371
	public void handleSplitText(String text) {
372
		// Create a synonym in a new homotypic group using text as name
373
		TaxonNameBase synonymName = ParseHandler.parseReferencedName(text, null);
374
		
375
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
376
				("New Heterotypic Synonym", getEditor().getUndoContext(), getEditor().getTaxon(), synonymName, getEditor()));
377
	}
378

    
379
	/**
380
	 * Refreshes the display with latest data from the model.
381
	 *
382
	 * Note: Will not parse the text and not calculate errors!
383
	 */
384
	public void refresh() {
385
//		showNameRelations();
386
		
387
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
388

    
389
/* RAP SS-Exclude, obviously not needed		
390
//		if(getNameViewer().getTextWidget() == null){
391
//			// we might get here via dnd. Look slike it can be ignored
392
//			return;
393
//		}
394
*/
395
		
396
		if (text.length() == 0) {
397
			initEmptyText();
398
		} else if(! getNameViewer().getText().equals(text)) {
399
			removeListener();
400
			getNameViewer().setText(text);
401
			addListener();
402
		}
403
		
404
		updateNonEditableInfo();
405
		
406
		updateIcon();
407
//		placeCursor();
408
		updateIndent();
409
		
410
		try {
411
			isFreeTextEnabled();
412
		} catch (OperationNotSupportedException e) {
413
			logger.warn("AbstractGroupedContainer.initTextViewer(): EditorAnnotations not supported :" + e.getMessage() );
414
		}
415
	}
416

    
417
	/**
418
	 * 
419
	 */
420
	protected abstract void updateIcon();
421

    
422
	
423
	protected abstract void updateIndent();
424

    
425

    
426
	/**
427
	 * <p>updateNonEditableInfo</p>
428
	 */
429
	protected abstract void updateNonEditableInfo();
430
	
431
	/**
432
	 * @throws OperationNotSupportedException 
433
	 * 
434
	 */
435
	private void isFreeTextEnabled() throws OperationNotSupportedException {
436
		// Decide whether editing of freetext is allowed or not.
437
		if (freetextEditingAllowed()) {
438
			
439
			// set editable
440
			getNameViewer().setEditable(true);
441
			
442
			// restore foreground font color again
443
			getNameViewer().setForeground(control.getForeground());
444
		} else {
445
			// set non-editable
446
			getNameViewer().setEditable(false);
447
			
448
			// grey out text as a non-editable indicator
449
			getNameViewer().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
450
		}
451
	}
452
	
453

    
454
	/**
455
	 * Checks whether the freetext should be editable based on specific empty fields.
456
	 * @return
457
	 */
458
	private boolean freetextEditingAllowed() {
459
		NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
460
		boolean enableFreetext = true;
461

    
462
		enableFreetext |= isNameUsedMultipleTimes();
463
		enableFreetext &= isNameParsable();
464
		
465
		return enableFreetext;
466
	}
467
	
468
	private boolean isNameUsedMultipleTimes(){
469
		if(getName().getTaxonBases().size() > 1){
470
			return true;
471
		}
472
		return false;
473
	}
474
	
475
	private boolean isNameParsable(){
476
		TaxonNameBase name = getName();
477
		
478
		boolean isParsable = true; 
479
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
480
		
481
		if(name instanceof NonViralName){
482
			NonViralName nonViralName = (NonViralName) name;
483
			isParsable &= ! nonViralName.isProtectedAuthorshipCache();
484
			isParsable &= ! nonViralName.isProtectedNameCache();
485
		}
486
		
487
		return isParsable;
488
	}
489

    
490
	/**
491
	 * Parse the text and calculate errors
492
	 * @throws OperationNotSupportedException 
493
	 */
494
	public void parseAndCalculateAnnotations() throws OperationNotSupportedException{
495
		removeListener();
496
		String unparsedNameString = getNameViewer().getText();
497
		parseHandler.parse(unparsedNameString);
498
		addListener();
499
		calculateAnnotations();
500
	}
501

    
502
	/**
503
	 * <p>getTaxonBase</p>
504
	 *
505
	 * @return the taxonBase
506
	 */
507
	public T getTaxonBase() {
508
		return getData();
509
	}
510
	
511
	/**
512
	 * <p>getName</p>
513
	 *
514
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
515
	 */
516
	public TaxonNameBase getName(){
517
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
518
	}
519

    
520
	/**
521
	 * <p>persistName</p>
522
	 */
523
	public void persistName(){
524
		if(isDirty()){
525
			try {
526
				getNameViewer().setEnabled(false);
527
			} catch (OperationNotSupportedException e) {
528
				logger.warn("AbstractGroupedContainer.persistName(): " + e.getMessage() );
529
			}
530
			final String unparsedNameString = getNameViewer().getText();
531
//			Job job = new Job("Persisting Name"){
532
//
533
//				@Override
534
//				protected IStatus run(IProgressMonitor monitor) {
535
//					
536
					final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
537
//					
538
//					Display.getDefault().asyncExec(new Runnable(){
539
//						public void run() {
540
							getTaxonBase().setName(name);
541
							getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
542
							setDirty(false);
543
							try {
544
								getNameViewer().setEnabled(true);
545
							} catch (OperationNotSupportedException e) {
546
								logger.warn("AbstractGroupedContainer.persistName(): " + e.getMessage() );
547
							}							
548
//						};
549
//					});
550
//					
551
//					
552
//					return Status.OK_STATUS;
553
//				}
554
//				
555
//			};
556
//			
557
//			job.setPriority(Job.DECORATE);
558
//			job.schedule();
559
		}
560
	}
561
	
562
	/**
563
	 * <p>Getter for the field <code>group</code>.</p>
564
	 *
565
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
566
	 */
567
	public AbstractGroup getGroup(){
568
		if(group == null){
569
			throw new IllegalStateException("Group shall not be null.");
570
		}
571
		return group;
572
	}
573
	
574
	/**
575
	 * <p>remove</p>
576
	 */
577
	public void remove(){
578
		getGroup().remove(this);
579
	}
580
		
581
	/**
582
	 * <p>createControl</p>
583
	 */
584
	protected void createControl() {
585
		control = getEditor().getToolkit().createComposite(getGroup().getControl());
586
		
587
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
588
		TableWrapLayout layout = new TableWrapLayout();
589
		layout.leftMargin = 0;
590
		layout.rightMargin = 0;
591
		layout.topMargin = 5;
592
		layout.bottomMargin = 5;
593
		
594
		layout.verticalSpacing = 0;
595
		layout.horizontalSpacing = 0;
596
		
597
		control.setLayout(layout);
598
		
599
	}
600
	
601
	/**
602
	 * @return
603
	 */
604
	protected TaxonNameEditor getEditor() {
605
		return getGroup().getEditor();
606
	}
607

    
608
	/**
609
	 * <p>Getter for the field <code>control</code>.</p>
610
	 *
611
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
612
	 */
613
	public Composite getControl(){
614
		return control;
615
	}
616
	
617
	/**
618
	 * <p>createLineWrapSupport</p>
619
	 * @throws OperationNotSupportedException 
620
	 */
621
	protected void createLineWrapSupport() throws OperationNotSupportedException {
622
		new LineWrapSupportFacade(getNameViewer(), getEditor().getManagedForm());
623
	}
624
	
625
	
626
	/**
627
	 * <p>createTextViewer</p>
628
	 * @throws OperationNotSupportedException 
629
	 */
630
	protected void createTextViewer() throws OperationNotSupportedException {
631
		nameViewer = new NameViewerFacade(control);
632
		
633
		focusListener = new FocusAdapter() {
634
			@Override
635
			public void focusGained(FocusEvent e) {
636
				for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
637
					container.colorSelected(NOT_SELECTED);
638
				}
639
				getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
640
				placeCursor();
641
				colorSelected(SELECTED_FOCUS);
642
			}
643
		};
644
		nameViewer.addFocusListener(focusListener);
645
		
646
//		
647
		MouseAdapter mouseListener = new MouseAdapter() {
648
			@Override
649
			public void mouseDown(MouseEvent e) {
650
				storeCursor();
651
			}
652
		};
653
		control.addMouseListener(mouseListener);
654
		nameViewer.addMouseListener(mouseListener);
655
	}
656
	
657
	/**
658
	 * <p>setIcon</p>
659
	 *
660
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
661
	 */
662
	public void setIcon(Image icon) {
663
		getNameViewer().setIcon(icon);
664
	}
665
	
666
	/**
667
	 * <p>setIndent</p>
668
	 *
669
	 * @param indent a int.
670
	 */
671
	public void setIndent(int indent) {
672
		if (control.getLayout() instanceof TableWrapLayout) {
673
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
674
			layout.leftMargin = indent;
675
			layout.rightMargin = ACCEPTED_INDENT;
676
			control.setLayout(layout);
677
			control.layout();
678
		} else {
679
			new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
680
		}
681
	}	
682
	
683
	/**
684
	 * <p>setSelected</p>
685
	 */
686
	public void setSelected() {
687
		getNameViewer().setFocus();
688
	}
689
	
690
	/**
691
	 * <p>isSelected</p>
692
	 *
693
	 * @return a boolean.
694
	 */
695
	public boolean isSelected(){
696
		return getEditor().getSelectedContainer() == this;
697
	}
698
	
699
	/**
700
	 * <p>colorSelected</p>
701
	 *
702
	 * @param mode a int.
703
	 */
704
	public void colorSelected(int mode){
705
		if(!control.isDisposed()){
706
			String colorString = null;
707
			
708
			switch(mode){
709
			case SELECTED_FOCUS:
710
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
711
				break;
712
			case SELECTED_NO_FOCUS:
713
				colorString = Resources.COLOR_CONTROL_SELECTED;
714
				break;
715
			default:
716
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
717
			}
718
			
719
			backgroundColor = EditorUtil.getColor(colorString);
720
			
721
			setBackground(backgroundColor);
722
		}
723
	}
724

    
725
	
726
	/**
727
	 * <p>setDelayedSelection</p>
728
	 */
729
	protected void setDelayedSelection(){
730
		//TODO this might be done better
731
		// this is the quickest solution i could come up with and it improves performance
732
		// please reimplement if you know better.
733
		selection = this;
734
		
735
		// start timer
736
		Display display = DisplayProxy.getDefault();
737
		Runnable runnable = new Runnable() {
738
			
739
			public void run() {
740
				getEditor().getManagedForm().setInput(selection);
741
			}
742
		};
743
		display.timerExec(1000, runnable);
744
		
745
	}
746
	
747
	/**
748
	 * <p>setBackground</p>
749
	 *
750
	 * @param color a {@link org.eclipse.swt.graphics.Color} object.
751
	 */
752
	public void setBackground(Color color) {
753
		control.setBackground(color);
754
		
755
		for(Control child : control.getChildren()){
756
			child.setBackground(color);
757
		}
758
		
759
		getNameViewer().setBackground(color);
760
	}
761

    
762
	/* (non-Javadoc)
763
	 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
764
	 */
765
	/**
766
	 * <p>setFont</p>
767
	 *
768
	 * @param font a {@link org.eclipse.swt.graphics.Font} object.
769
	 */
770
	public void setFont(Font font) {
771
		getNameViewer().setFont(font);
772
	}
773
	
774
	/**
775
	 * <p>Getter for the field <code>nameViewer</code>.</p>
776
	 *
777
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
778
	 */
779
	public NameViewerFacade getNameViewer() {
780
		if (nameViewer == null){
781
			throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
782
		}
783
		return nameViewer;
784
	}
785
	
786
	/**
787
	 * If <code>textViewer</code> has already been set, it will show a
788
	 * <code>prompt</code> along the lines of "Click here to start entering data"
789
	 * when empty.
790
	 *
791
	 * @param prompt a {@link java.lang.String} object.
792
	 */
793
	public void createEmptyViewerPrompt(final String prompt) {
794
		
795
		Assert.isNotNull(getNameViewer());
796
		 
797
		setFocusListener(new FocusListener() {
798

    
799
			
800
			public void focusGained(FocusEvent e) {
801
				try {
802
					if (getNameViewer().getDocumentAsString().equals(prompt)) {
803
						getNameViewer().setFont(getViewerFont());
804
						getNameViewer().setDocumentAsString("");
805
					}
806
				} catch (OperationNotSupportedException e1) {
807
				}
808
			}
809

    
810
			
811
			public void focusLost(FocusEvent e) {
812
				try {
813
					if (getNameViewer().getDocumentLength() == 0) {
814
						initEmptyText();
815
					}
816
				} catch (OperationNotSupportedException e1) {
817
				}
818
			}
819
			
820
		});
821
		getNameViewer().addFocusListener(getFocusListener());
822
		
823
		try {
824
			if (getNameViewer().getDocumentLength() == 0) {
825
				getNameViewer().setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
826
				getNameViewer().setDocumentAsString(prompt);
827
			}
828
		} catch (OperationNotSupportedException e1) {
829
		}
830
	}
831
	
832
	/**
833
	 * <p>getViewerFont</p>
834
	 *
835
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
836
	 */
837
	abstract protected Font getViewerFont();
838

    
839
	/**
840
	 * <p>initEmptyText</p>
841
	 */
842
	protected void initEmptyText() {
843
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
844
		getNameViewer().setFont(defaultFont);
845
		
846
		getNameViewer().setDocumentAsString(getEmptyTextPrompt());
847
		placeCursor();
848
	}
849

    
850
	/**
851
	 * <p>Setter for the field <code>focusListener</code>.</p>
852
	 *
853
	 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
854
	 */
855
	protected void setFocusListener(FocusListener focusListener) {
856
		this.focusListener = focusListener;
857
	}
858

    
859
	private FocusListener getFocusListener() {
860
		return focusListener;
861
	}
862

    
863
	/**
864
	 * <p>setDirty</p>
865
	 *
866
	 * @param isDirty a boolean.
867
	 */
868
	public void setDirty(boolean isDirty) {
869
		if(isDirty){
870
			getEditor().getManagedForm().dirtyStateChanged();
871
		}
872
		this.isDirty = isDirty;
873
	}
874
	
875
	/**
876
	 * <p>isDirty</p>
877
	 *
878
	 * @return a boolean.
879
	 */
880
	public boolean isDirty(){
881
		return isDirty;
882
	}
883
	
884
	/**
885
	 * <p>setMenu</p>
886
	 *
887
	 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
888
	 */
889
	public void setMenu (Menu menu) {
890
		control.setMenu(menu);
891
		
892
		getNameViewer().setMenu(menu);
893
	}
894
	
895
	private Control[] draggableControls;
896
	
897
	/**
898
	 * <p>setDraggableControl</p>
899
	 *
900
	 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
901
	 */
902
	protected void setDraggableControl(Control[] controls) {
903
		draggableControls = controls;
904
	}
905
	
906
	/**
907
	 * <p>setIsDraggable</p>
908
	 *
909
	 * @param draggable a boolean.
910
	 */
911
	public void setIsDraggable(boolean draggable) {
912

    
913
		if (draggable) {
914

    
915
			if (draggableControls == null) {
916
				throw new NullPointerException(
917
						"Draggable controls must be set to add draggability");
918
			}
919
			
920
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };			
921
			int operations = DND.DROP_MOVE;
922

    
923
			for(Control draggableControl : draggableControls){
924
				DragSource dragSource = new DragSource(draggableControl, operations);
925
				dragSource.setTransfer(types);
926
				
927
				dragSource.addDragListener(new NameEditorDragListener(this));
928
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
929
			}
930
		} 
931
	}
932

    
933
	private String nonEditableText;
934

    
935
	ControlListener nonEditableResizeListener = new ControlAdapter() {
936
		
937
		int width = 0;
938
		
939
		@Override
940
		public void controlResized(ControlEvent e) {
941
			if (nonEditableInfoLabel.getBounds().width == width) {
942
				return;
943
			}
944
			width = nonEditableInfoLabel.getBounds().width;
945
			if (nonEditableInfoLabel.getBounds().width > 0) {
946
				nonEditableInfoLabel.setText(
947
						Dialog.shortenText(nonEditableText.toUpperCase(), 
948
						nonEditableInfoLabel));
949
			}
950
		}
951
	};
952

    
953
	private String nonEditableHoverText;
954

    
955
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
956

    
957
	private T data;
958
			
959
	/**
960
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
961
	 * input field. For instance, NameComposites display things like name relations,
962
	 * sec. references, etc. here.
963
	 *
964
	 * @param info the text to display in the label
965
	 * @param append whether the string should be appended to text that is already shown in the label
966
	 * @throws OperationNotSupportedException 
967
	 */
968
	public void setNonEditableInfo(String info, boolean append) throws OperationNotSupportedException {
969
		// TODO non editable info should only be drawn once, when everything else is drawn
970
		info = info.toUpperCase();
971
		
972
		if(append){
973
			nonEditableText += ", " + info;
974
			nonEditableHoverText += "\n" + info;
975
		}else{
976
			nonEditableText = info;
977
			nonEditableHoverText = info;
978
		}
979
		
980
		if (nonEditableInfoLabel == null) {
981
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
982
			TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
983
			// Set indent to viewer ruler's width 
984
			if (getNameViewer().getVerticalRulerControl() != null) {
985
				// TODO right justify
986
				layoutData.indent = INameViewer.RULER_WIDTH;
987
			}
988
			nonEditableInfoLabel.setLayoutData(layoutData);
989
			
990

    
991
			
992
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
993
				@Override
994
				public String getLabelText() {
995
					return nonEditableText.toUpperCase();
996
				}
997
			}; 
998
			nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
999
			
1000
			nonEditableInfoHover = new ToolTipFacade(nonEditableInfoLabel);
1001
			nonEditableInfoHover.setRespectDisplayBounds(true);
1002
			
1003
		} 
1004
		nonEditableInfoHover.setText(nonEditableHoverText);
1005
		nonEditableInfoLabel.setText(nonEditableText);
1006
		
1007
		calculateAnnotations();
1008
	}
1009
	
1010
	/**
1011
	 * <p>Getter for the field <code>data</code>.</p>
1012
	 *
1013
	 * @return a T object.
1014
	 */
1015
	public T getData(){
1016
		return data;
1017
	}
1018
	
1019
	/**
1020
	 * <p>Setter for the field <code>data</code>.</p>
1021
	 *
1022
	 * @param data a T object.
1023
	 */
1024
	public void setData(T data){
1025
		this.data =  (T) HibernateProxyHelper.deproxy(data);
1026
	}
1027

    
1028
	/**
1029
	 * If the user hitting carriage return should cause something to happen -
1030
	 * i.e. the creation of a new composite - call this method and override
1031
	 * the method handleSplitText().
1032
	 */
1033
	protected void createLineBreakListener() throws OperationNotSupportedException {
1034
		lineBreakListener = LineBreakListenerFacade.getInstance(this);
1035
		getNameViewer().addVerifyListener(lineBreakListener);
1036
	}
1037
		
1038
	abstract class LabelEllipsisListener extends ControlAdapter {
1039
		
1040
		private final Label label;
1041
		int width = 0;
1042

    
1043
		LabelEllipsisListener(Label label) {
1044
			this.label = label;
1045
		}
1046
		
1047
		abstract public String getLabelText();
1048
		
1049
		@Override
1050
		public void controlResized(ControlEvent e) {
1051
			if (label.getBounds().width == width) {
1052
				return;
1053
			}
1054
			width = label.getBounds().width;
1055
			if (label.getBounds().width > 0) {
1056
				label.setText(TextHelper.shortenText(getLabelText(), label));
1057
			}
1058
		}
1059
	}
1060

    
1061
	/**
1062
	 * <p>storeCursor</p>
1063
	 */
1064
	public void storeCursor() {
1065
		try {
1066
			this.cursorPosition = getNameViewer().getCursorPosition();
1067
		} catch (OperationNotSupportedException e) {
1068
			this.cursorPosition = 0;
1069
		}
1070
	}
1071
	
1072
	/**
1073
	 * Puts the cursor to the position it was last seen on or to the end of line
1074
	 * if no former position is known.
1075
	 */
1076
	public void placeCursor(){
1077
		if(cursorPosition == 0){
1078
			getNameViewer().setCursorToEOL();
1079
		}else{
1080
			getNameViewer().setCursorPosition(cursorPosition);
1081
		}
1082
	}
1083

    
1084

    
1085

    
1086
	/**
1087
	 * <p>Setter for the field <code>group</code>.</p>
1088
	 *
1089
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
1090
	 */
1091
	public void setGroup(AbstractGroup group) {
1092
		this.group = group;
1093
	}
1094

    
1095

    
1096

    
1097
	/**
1098
	 * <p>restoreColor</p>
1099
	 */
1100
	public void restoreColor() {
1101
		setBackground(backgroundColor);
1102
	}
1103
	
1104

    
1105

    
1106
	/* (non-Javadoc)
1107
	 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
1108
	 */
1109
	@Override
1110
	public void initialize(IManagedForm form) {
1111
		// TODO Auto-generated method stub
1112
		
1113
	}
1114

    
1115

    
1116
	/* (non-Javadoc)
1117
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1118
	 */
1119
	@Override
1120
	public void dispose() {
1121
		if(getControl() != null){
1122
			setMenu(null);
1123
			getControl().dispose();
1124
		}
1125
	}
1126

    
1127

    
1128
	/* (non-Javadoc)
1129
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1130
	 */
1131
	@Override
1132
	public void commit(boolean onSave) {
1133
		if(isDirty()){
1134
			persistName();
1135
		}
1136
	}
1137

    
1138

    
1139
	/* (non-Javadoc)
1140
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1141
	 */
1142
	@Override
1143
	public boolean setFormInput(Object input) {
1144
		// TODO Auto-generated method stub
1145
		return false;
1146
	}
1147

    
1148

    
1149
	/* (non-Javadoc)
1150
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1151
	 */
1152
	@Override
1153
	public void setFocus() {
1154
		getNameViewer().setFocus();
1155
	}
1156

    
1157

    
1158
	/* (non-Javadoc)
1159
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1160
	 */
1161
	@Override
1162
	public boolean isStale() {
1163
		// TODO Auto-generated method stub
1164
		return false;
1165
	}
1166
} 
(2-2/13)